Hibernate中的事务与并发

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/u011301372/article/details/82931710

事务的相关概念

什么是事务

  • 事务是逻辑上的一组操作,组成事务的各个执行单元,操作要么全都成功,要么全部失败
  • 转账的例子:小王给小李转钱,扣钱,加钱,两个操作组成了一个事务

事务的特性

  • 原子性------事务的不可分割
  • 一致性----事务执行的前后数据的完整性保持一致
  • 隔离性-----一个事务执行的过程中,不应该受到其他的事务的干扰
  • 持久性----事务一旦提交,数据就永久保持到数据库中

如果不考虑隔离性:引发一些读的问题

  • 脏读----一个事务读到了另一个事务未提交的数据
  • 不可重复读-----一个事务读到了另一个事务已经提交的update数据,导致多次查询结果不一致。
  • 虚读----一个事务读到了另一个事务已经提交的inset数据,导致多级查询机构不一致。

通过设置数据库的隔离级别来解决上述读的问题

  • 未提交读:以上的读的问题都有可能发生
  • 已提交读:避免脏读,但是不可重复读,虚读都有可能发生
  • 可重复读:避免脏读,不可重复读,但是虚读是有可能发生
  • 串行化:以上读的情况都可以避免

如果想在Hibernate的框架中来设置隔离级别,需要在Hibernate.cfg.xml的配置文件中通过标签来配置

  • 通过:hibernate.connection,isolation = 4 来配置
  • 取值:
    • 1——Read uncommitted isolation
    • 2——Read commited isolation
    • 4——Repeatable read isolation
    • 8——Serializable isolation
<!--设置数据库的隔离级别,就使用默认值就OK-->
<property name="hibernate.connection.isolation">4</property>

丢失更新的问题

  • 如果不考虑隔离性,也会产生写入数据的问题,这一类的问题叫丢失更新的问题
  • 例如:两个事务同时对某一条记录做修改,就会引发丢失更新的问题
    • A 事务和B事务同时获取到一条数据,同时再做修改
    • 如果A事务修改完成后,提交了事务
    • B事务修改完成后,不管是提交还是回滚,如果不做处理,都会对数据产生影响

两种解决方案

悲观锁

  • 采用的是数据库提供的一种锁机制,如果采用做了这种机制,在SQL语句的后面添加 for update 子句
  • 只有当A事务提交后,锁释放了,其他事务才能操作该条记录

乐观锁

  • 采用版本号的机制来解决的,会给表结构添加一个字段version = 0 ,默认值是)
    • 当A事务在操作完该条记录,提交事务时,会先检查版本号,如果发生版本号的值相同时,才可以提交事务。同时会更新版本号version = 1
    • 当B事务操作完该条记录时,提交事务时,会先检查版本号,如果发现版本不同时,程序会出抛出异常。

在这里插入图片描述

 //分别交替的逐行调试run1(),run2()方法,开启两个事务,查看数据库中的更新信息
 @Test
    public void run1(){
        Session session = HibernateUtils.getsSession();
        Transaction tr = session.beginTransaction();
        //获取持久态的对象
         User user = session.get(User.class,1);
         //重新设置新的名称
        user.setName("新");

        tr.commit();
        session.close();
    }
    @Test
    public void run2(){
        Session session = HibernateUtils.getsSession();
        Transaction tr = session.beginTransaction();
        //获取持久态的对象
        User user = session.get(User.class,1);
        //重新设置新的名称
        user.setAge(67);

        tr.commit();
        session.close();
    } 

使用Hibernate框架解决丢失更新的问题

悲观锁

  • 使用session.get(Customer.class 1.LockMode.UPDATE);方法

乐观锁

  • 在对应的JavaBean中添加一个属性,名称可以是任意的。例如:private Integer version;提供get 和set 方法
  • 在映射的配置文件中,提供标签即可。
//在JavaBean中添加属性
private Integer version;
//get 和set方法

// 在User.hbm.xml
<!--乐观锁-->
<class ,,,,,>
<!--name的值为JavaBean中的属性名称-->
	<version name="version" />
</class>

//重新调试以上run1(),run2()方法

绑定本地的Session

在JavaWeb的事务处理中,需要在业务层使用Connection来开启事务

  • 一种是通过参数的方式传递下去
  • 另一种是吧Connection绑定到ThreadLocal对象中

在Hibernate框架中,使用session对象开启事务,所以需要来传递session对象,框架提供了ThreadLocal的方式

  • 需要在Hibernate.cfg.xml的配置文件中提供配置
    • <property name = "hibernate.current_session_context_class">thread</property>
  • 重新HibernateUtils的工具类,使用SessionFactory的getCurrentSession()方法,获取当前的Session对象,并且该Session对象不用手动关闭,线程结束了,会自动关闭。
  • public static Session getCurrentSession(){ return factory.getCurrentSession(); }
  • 注意:想使用getCurrentSession()方法,必须要先配置才可以使用。
<!--web.xml-->
 <servlet>
        <servlet-name>SaveServlet</servlet-name>
        <servlet-class>cm.zst.servlet.SaveServlet</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>SaveServlet</servlet-name>
        <url-pattern>/SaveServlet</url-pattern>
    </servlet-mapping>
public class SaveServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        User u1 = new User();
        u1.setName("测试1");

        User u2 = new User();
        u2.setName("测试2");


        new UserService().save3(u1,u2);
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        doGet(req, resp);
    }
}
public class UserService {
    public void save3(User u1,User u2){
        UserDao  dao = new UserDao();
        dao.save1(u1);
      //  int a = 10/0;//在service层,打开此行,重新访问SaveServlet,会抛出异常,查看数据库信息,只更新一条数据
      //在dao层开启的事务,这两个操作不在同一个事务中,正常情况下应该在service层开启事务
        dao.save2(u2);
        //提交事务
        //出现问题:回滚事务
    }
}
public class UserDao {
    public void save1(User u1){
        Session session = HibernateUtils.getsSession();
        Transaction tr = session.beginTransaction();

        session.save(u1);
        tr.commit();
        session.close();

    }
    public void save2(User u2){
        Session session = HibernateUtils.getsSession();
        Transaction tr = session.beginTransaction();

        session.save(u2);
        tr.commit();
        session.close();
    }
}

打开服务器,访问SaveServlet,查看数据库信息,如下

+----+-------+------+---------+
| id | name  | age  | version |
+----+-------+------+---------+
|  1 | 旧    |   67 |       1 |
|  2 | 小王  |   23 |       0 |
|  3 | 测试1 | NULL |       0 |
|  4 | 测试2 | NULL |       0 |
+----+-------+------+---------+ 

解决方法

<!--在hibernate.cfg.xml中配置-->
<!--开启绑定本地的session-->
        <property name = "hibernate.current_session_context_class">thread</property>


//修改HibernateUtils工具类中的方法
public static Session getCurrentSession(){
	return FACTORY.getCurrentSession();

}

//业务层改造完成
public class UserService {
    public void save3(User u1,User u2){
        UserDao  dao = new UserDao();
        
        //获取session
        Session session = HibernateUtils.getCurrentSession();
        //开启事务
        Transaction tr = session.beginTransaction();
       try{
           dao.save1(u1);
           //  int a = 10/0;
           dao.save2(u2);
           //提交事务
           tr.commit();
       }catch (Exception e){
           e.printStackTrace();
           //出现问题:回滚事务
           tr.rollback();
       }fianlly{

			//自己释放资源,现在,session不用关闭,线程结束后,自动关闭。
	   }

    }
}

//dao层
public class UserDao {
    public void save1(User u1){
        Session session = HibernateUtils.getCurrentSession();
        session.save(u1);
        
    }
    public void save2(User u2){
        Session session = HibernateUtils.getCurrentSession();
        session.save(u2);
       
    }
}


猜你喜欢

转载自blog.csdn.net/u011301372/article/details/82931710