ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

hibernate(四)之乐观锁与悲观锁

2022-01-02 10:03:34  阅读:197  来源: 互联网

标签:hibernate return String getCurrentSession 乐观 params 悲观 hql public


hibernate四之乐观锁与悲观锁

一、Hibernate锁机制

Hibernate提供了乐观锁和悲观锁机制,主要用于解决事务并发问题。

悲观锁:Hibernate认为任何操作都可能发生并发,因此在第一个线程查询数据时,就把该条记录锁住。此时其他线程对该记录不能做任何操作(即增删改操作都不能)。必须等当前线程事务结束才可以进行操作。

悲观锁的实现原理 Hibernate悲观锁机制实际上是采用数据库的锁机制实现。 数据库中SQL语句最后加for update则把记录直接锁死,其他用户增删改查都不行,只能等待:select * from TRAIN where id=1 for update; Hibernate中load重载方法:

session.load(Train.class,1,LockOptions.UPGRADE);

只能等待当前用户提交或回滚,若等待超时则报异常! 悲观锁的缺点:处理效率很低。

悲观锁使用步骤及测试

CREATE TABLE TRAIN(
ID NUMBER PRIMARY KEY,	T_START VARCHAR2(20),
T_END VARCHAR2(20),T_TICKET NUMBER);

INSERT INTO TRAIN VALUES(1,'beijing','shanghai',100);

创建实体

@Entity
public class Train {
    @GeneratedValue
    @Id
    private int id;
    private String name;
    private int ticket;

创建两个线程

public class ThreadClient extends Thread {//继承Thread
    public void run(){//模拟购票操作
        Session session=HibernateUtil.getSession();
        Transaction tx=session.beginTransaction();
        //Train train=(Train)session.load(Train.class, 1);//不加锁时,查询出火车信息
        Train train=(Train)session.load(Train.class,1,LockMode.UPGRADE);//设置悲观锁
        if(train.getTicket()>=1){//判断剩余票数>=购买票数
            try {//满足购票条件,进行购票操作
                Thread.sleep(2000);//模拟用户操作
            } catch (InterruptedException e) { 	e.printStackTrace(); 	}
			
            int ticket=train.getTicket()-1;//将票数更新
            train.setTicket(ticket);		
            System.out.println("购票成功!");
        }else {  System.out.println("票数不足,购买失败!");	}
        tx.commit();//持久对象,提交就自动同步,相当于执行了update		 					session.close();										            }
}

测试类

public static void main(String[] args) {
	ThreadClient c1=new ThreadClient();		c1.start();
	ThreadClient c2=new ThreadClient();		c2.start();
 /** 若ThreadClient类为无锁查询,则显示2个购买成功,但数据库却减少1张票。若ThreadClient类加了悲观锁,则显示2个购买成功,数据库减少2张票。**/}

乐观锁:认为发生并发几率非常小。相同的记录不同的用户都可以查询访问,当多个人都要修改该记录时,只有第一个提交的用户成功,其他的会抛出异常,提示失败!

乐观锁的实现原理

乐观锁机制是借助于一个“版本”字段实现,当第一个更新用户提交成功后,Hibernate会自动将该“版本”字段值+1,当其他用户提交,如果版本字段小于数据库中的值,则会抛出异常,提示失败。如果不使用框架技术,那么我们需要手工做对比,使用Hibernate框架后,Hibernate可以帮助我们做version对比的操作。

乐观锁使用步骤及测试 step1:向TRAIN表中添加一个VERSION版本字段

@Entity
public class Train {
    @GeneratedValue
    @Id
    private int id;
    private String name;
    private int ticket;
    @Version
    private int version;

将悲观锁中的ThreadClient类,改为不加锁的load方法

Train train=(Train)session.load(Train.class, 1);

执行结果:先提交的事务执行成功,后提交的事务执行失败,报错信息为:

StaleObjectStateException: Row was updated or deleted by another transaction

二、ssh整合

依赖

<!--spring -->
<dependency>
  <groupId>org.springframework</groupId>
  <artifactId>spring-context</artifactId>
  <version>4.3.14.RELEASE</version>
</dependency>
<dependency>
  <groupId>org.aspectj</groupId>
  <artifactId>aspectjweaver</artifactId>
  <version>1.8.13</version>
</dependency>
<dependency>
  <groupId>org.springframework</groupId>
  <artifactId>spring-jdbc</artifactId>
  <version>4.3.14.RELEASE</version>
</dependency>

<!--spring mvc-->
<dependency>
  <groupId>org.springframework</groupId>
  <artifactId>spring-webmvc</artifactId>
  <version>4.3.14.RELEASE</version>
</dependency>
<dependency>
  <groupId>com.fasterxml.jackson.core</groupId>
  <artifactId>jackson-core</artifactId>
  <version>2.8.10</version>
</dependency>
<dependency>
  <groupId>com.fasterxml.jackson.core</groupId>
  <artifactId>jackson-databind</artifactId>
  <version>2.8.10</version>
</dependency>


<!--hibernate-->
<dependency>
  <groupId>org.hibernate</groupId>
  <artifactId>hibernate-core</artifactId>
  <version>4.2.0.Final</version>
</dependency>
<dependency>
  <groupId>org.springframework</groupId>
  <artifactId>spring-orm</artifactId>
  <version>4.3.14.RELEASE</version>
</dependency>
<dependency>
  <groupId>mysql</groupId>
  <artifactId>mysql-connector-java</artifactId>
  <version>5.1.44</version>
</dependency>

db.properties

jdbc.driverClassName=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://127.0.0.1:3306/sssp
jdbc.username=root
jdbc.password=root

web.xml

<context-param>
	<param-name>contextConfigLocation</param-name>
	<param-value>classpath:applicationContext.xml</param-value>
</context-param>
<listener>
	<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>

<servlet>
	<servlet-name>springDispatcherServlet</servlet-name>
	<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
	<init-param>
		<param-name>contextConfigLocation</param-name>
		<param-value>classpath:applicationMvc.xml</param-value>
	</init-param>
	<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
	<servlet-name>springDispatcherServlet</servlet-name>
	<url-pattern>/</url-pattern>
</servlet-mapping>

<filter>
	<filter-name>CharacterEncodingFilter</filter-name>
	<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
	<init-param>
		<param-name>encoding</param-name>
		<param-value>UTF-8</param-value>
	</init-param>
</filter>
<filter-mapping>
	<filter-name>CharacterEncodingFilter</filter-name>
	<url-pattern>/*</url-pattern>
</filter-mapping>

spring

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xmlns:tx="http://www.springframework.org/schema/tx"
       xsi:schemaLocation="
           http://www.springframework.org/schema/beans
           http://www.springframework.org/schema/beans/spring-beans-4.2.xsd
           http://www.springframework.org/schema/context
           http://www.springframework.org/schema/context/spring-context-4.2.xsd
           http://www.springframework.org/schema/aop
           http://www.springframework.org/schema/aop/spring-aop-4.2.xsd
           http://www.springframework.org/schema/tx
           http://www.springframework.org/schema/tx/spring-tx-4.2.xsd">
    <!-- 配置自动扫描的包 -->
    <context:component-scan base-package="com.hw.ssh">
        <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
        <context:exclude-filter type="annotation"
                                expression="org.springframework.web.bind.annotation.ControllerAdvice"/>
    </context:component-scan>

    <!-- 配置数据源 -->
    <context:property-placeholder location="classpath:db.properties"/>

    <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
        <property name="username" value="${jdbc.username}"></property>
        <property name="password" value="${jdbc.password}"></property>
        <property name="driverClassName" value="${jdbc.driverClassName}"></property>
        <property name="url" value="${jdbc.url}"></property>
    </bean>

    <bean id="sessionFactory" class="org.springframework.orm.hibernate4.LocalSessionFactoryBean">
        <!-- 注入数据源 -->
        <property name="dataSource" ref="dataSource"/>
        <!-- 设置Spring取那个包中查找相应的实体类 -->
        <property name="packagesToScan">
            <value>com.**.entity</value>
        </property>
        <property name="hibernateProperties">
            <props>
                <prop key="hibernate.dialect">org.hibernate.dialect.MySQL5Dialect</prop>
                <prop key="hibernate.show_sql">true</prop>
                <prop key="hibernate.hbm2ddl.auto">update</prop>
                <prop key="hibernate.current_session_context_class">org.springframework.orm.hibernate4.SpringSessionContext</prop>
            </props>
        </property>
    </bean>

    <!-- 配置事务管理器 -->
    <bean name="transactionManager" class="org.springframework.orm.hibernate4.HibernateTransactionManager">
        <property name="sessionFactory" ref="sessionFactory"></property>
    </bean>

  <tx:annotation-driven transaction-manager="transactionManager" proxy-target-class="true"/>
</beans>

springmvc

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:mvc="http://www.springframework.org/schema/mvc"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
           http://www.springframework.org/schema/beans/spring-beans-4.2.xsd
           http://www.springframework.org/schema/context
           http://www.springframework.org/schema/context/spring-context-4.2.xsd
           http://www.springframework.org/schema/mvc
           http://www.springframework.org/schema/mvc/spring-mvc-4.2.xsd">


    <mvc:annotation-driven/>

    <!-- 静态资源 -->
    <mvc:default-servlet-handler/>

    <!-- 组件扫描 -->
    <context:component-scan base-package="com.hw.ssh.controller">
        <context:include-filter type="annotation"
                                expression="org.springframework.stereotype.Controller"/>
        <context:include-filter type="annotation"
                                expression="org.springframework.web.bind.annotation.ControllerAdvice"/>
    </context:component-scan>

    <!-- 视图解析 -->
    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"
          id="internalResourceViewResolver">
        <property name="prefix" value="/WEB-INF/jsp/"/>
        <property name="suffix" value=".jsp"/>
    </bean>

</beans>

service

@Service
@Transactional(readOnly = true)//没有报Could not obtain transaction-synchronized Session for current thread
public class UserService {
    @Autowired
    private BaseDao<User> userDao;

    public List<User> list() {
        return userDao.find( "from User" );
    }

    @Transactional
    public void add(User user) {
        User user1 = userDao.get( "from User where id =1" );
        user1.setName( user.getName() );
        // userDao.update( user1 );不需要显示调用,@Transactional会持久化查出的对象
    }
}

通用dao

import java.io.Serializable;
import java.lang.reflect.ParameterizedType;
import java.util.List;
import java.util.Map;
import org.springframework.beans.factory.annotation.Autowired;
import org.hibernate.Query;
import org.hibernate.Session;
import org.hibernate.SessionFactory;


public class BaseDao<T> {

    @Autowired
    private SessionFactory sessionFactory;

    private Class<T> clz;

    public Class<T> getClz() { 
        if (clz == null) { // 获取泛型的Class对象 
        clz = ((Class<T>) (((ParameterizedType) (this.getClass().getGenericSuperclass())) .getActualTypeArguments()[0])); 
        } 
        return clz; 
    }

    protected Session getCurrentSession() {
        return this.sessionFactory.getCurrentSession();
    }

    public Serializable save(T o) {
        return this.getCurrentSession().save(o);
    }

    public T get(Serializable id) {
        return (T) this.getCurrentSession().get(clz, id);
    }

    public T get(String hql) {
        Query q = this.getCurrentSession().createQuery(hql);
        List<T> l = q.list();
        if (l != null && l.size() > 0) {
            return l.get(0);
        }
        return null;
    }

    public T get(String hql, Map<String, Object> params) {
        Query q = this.getCurrentSession().createQuery(hql);
        if (params != null && !params.isEmpty()) {
            for (String key : params.keySet()) {
                q.setParameter(key, params.get(key));
            }
        }
        List<T> l = q.list();
        if (l != null && l.size() > 0) {
            return l.get(0);
        }
        return null;
    }

    public void delete(T o) {
        this.getCurrentSession().delete(o);
    }

    public void update(T o) {
        this.getCurrentSession().update(o);
    }

    public void saveOrUpdate(T o) {
        this.getCurrentSession().saveOrUpdate(o);
    }

    public List<T> find(String hql) {
        Query q = this.getCurrentSession().createQuery(hql);
        return q.list();
    }

    public List<T> find(String hql, Map<String, Object> params) {
        Query q = this.getCurrentSession().createQuery(hql);
        if (params != null && !params.isEmpty()) {
            for (String key : params.keySet()) {
                q.setParameter(key, params.get(key));
            }
        }
        return q.list();
    }

    public List<T> find(String hql, Map<String, Object> params, int page, int rows) {
        Query q = this.getCurrentSession().createQuery(hql);
        if (params != null && !params.isEmpty()) {
            for (String key : params.keySet()) {
                q.setParameter(key, params.get(key));
            }
        }
        return q.setFirstResult((page - 1) * rows).setMaxResults(rows).list();
    }

    public List<T> find(String hql, int page, int rows) {
        Query q = this.getCurrentSession().createQuery(hql);
        return q.setFirstResult((page - 1) * rows).setMaxResults(rows).list();
    }

    public Long count(String hql) {
        Query q = this.getCurrentSession().createQuery(hql);
        return (Long) q.uniqueResult();
    }

    public Long count(String hql, Map<String, Object> params) {
        Query q = this.getCurrentSession().createQuery(hql);
        if (params != null && !params.isEmpty()) {
            for (String key : params.keySet()) {
                q.setParameter(key, params.get(key));
            }
        }
        return (Long) q.uniqueResult();
    }

    public int executeHql(String hql) {
        Query q = this.getCurrentSession().createQuery(hql);
        return q.executeUpdate();
    }

    public int executeHql(String hql, Map<String, Object> params) {
        Query q = this.getCurrentSession().createQuery(hql);
        if (params != null && !params.isEmpty()) {
            for (String key : params.keySet()) {
                q.setParameter(key, params.get(key));
            }
        }
        return q.executeUpdate();
    }

}

参数map

Map<String, Object> map=new HashMap<>();
map.put("name", username);
User find = get("from User where name=:name",map);

当然也可以使用继承HibernateDaoSupport 就能获取HibernateDaoSupport里面的方法完成增删改查

@Service
public class ClassService extends HibernateDaoSupport{
	
    @Resource
    public void setSuperSessionFactory(SessionFactory sessionFactory) {
        this.setSessionFactory(sessionFactory);
    }	
	
    public void add(Classroom classroom) {		
        getHibernateTemplate().save(classroom);		
    }

}

sessionFactory.getCurrentSession();

  1. openSession 从字面上可以看得出来,是打开一个新的session对象,而且每次使用都是打开一个新的session,假如连续使用多次,则获得的session不是同一个对象,并且使用完需要调用close方法关闭session。

  2. getCurrentSession ,从字面上可以看得出来,是获取当前上下文一个session对象,当第一次使用此方法时,会自动产生一个session对象,并且连续使用多次时,得到的session都是同一个对象,这就是与openSession的区别之一,简单而言,getCurrentSession 就是:如果有已经使用的,用旧的,如果没有,建新的。

注意 :在实际开发中,往往使用getCurrentSession多,因为一般是处理同一个事务(即是使用一个数据库的情况),所以在一般情况下比较少使用openSession或者说openSession是比较老旧的一套接口了;

标签:hibernate,return,String,getCurrentSession,乐观,params,悲观,hql,public
来源: https://blog.csdn.net/faramita_of_mine/article/details/122245243

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有