Hibernate事务隔离级别, 悲欢锁和乐观锁

Hibernate事务隔离级别:
  如果没有锁定且多个用户同时访问一个数据库,当他们的事务同时使用相同的数据时可能会发生问题.由于并发操作带来的数据不一致包括:

  1. 丢失数据(lost update)
  2. 修改,读”脏”数据(脏读 dirty read)
  3. 虚读(phantom read)
  4. 不可重复读(unrepeated read)
  5. 第二类丢失更新(second lost updates):

    丢失数据(lost update):
      在完全未隔离事务的情况下,两个事务更新同一条数据资源,某一事物异常终止,回滚造成第一个完成的更新也同时丢失.
    脏读(dirty read):
      第二个事务查询到第一个事务还未提交的更新数据,形成脏读
    虚读(phantom read):
       一个事务执行了两次查询,第二次结果集包含第一次中没有或者某些行已经删除,造成两次结果不一致,只是另一个事务在这两次查询中插入或者删除造成的.
    不可重复读(unrepeated read):
      一个事物两次读取同一行数据,结果得到不同状态结果,如中间正好另一个事务更新了该数据,两次结果相异,不可信任.
    第二类丢失更新(second lost updates):
      如果两个事务都读取同一行,然后两个都进行写操作,并提交,第一个事务所做的改变就会丢失.

数据库事务隔离级别

为了解决数据库事务并发运行时的各种问题数据库系统提供四种事务隔离级别:
    1. Serializable 串行化
   2. Repeatable Read 可重复读
    3. Read Committed 可读已提交
   4. Read Uncommitted 可读未提交
这里写图片描述
使用Hibernate设置数据库隔离级别
在Hibernate的配置文件中可以配置数据库事务隔离级别.每一个隔离级别用一个整数表示
  8 - Serializable 串行化
    事务不会发生并发,一个事物执行完成,下一个事物才可执行
  4 - Repeatable Read 可重复读
  当前事务读取数据时,其它事务对同一份数据做的更更新操作提交之后, 不会再当前事务生效
  2 - Read Committed 可读已提交
  当前事务进行读取数据,其它事务对同一数据进行修改,如果不提交,不会在当前事务生效
  1 - Read Uncommitted 可读未提交
  当前事务进行读取数据,其它事务对统一数据进行修改,如果不提交,也会在当前事务生效  

<property name="hibernate.connection.isolation">1</property>

Hibernate的悲欢锁和乐观锁

数据库隔离级别越高并发性能越差.

并发性:
  当前系统进行了序列化后,你读取数据库后,别人查询不了,称为并发性不好

悲欢锁的实现:
  通常依赖于数据库机制,在整修过程中将数据库锁定,其它任何用户都不能读取或修改

悲欢锁的使用场景:
  悲观锁一般适合短事物比较多(如某一个数据取出之后加1,立即释放)
  使用悲观锁,肯定在加载数据时就要锁住,
  加一个更新的悲欢锁,为查询数据用的,在一个事物查询期间,
  其它事务对数据进行非查询操作,当前事务不受影响

Hibernate使用loadMode进行悲观锁加载.

TestIsolation tI = session.get(TestIsolation.class,1, LockMode.UPGRADE);

如果使用悲观锁,那么lazy(懒加载)无效

乐观锁:
乐观锁:
  不是锁,是一种冲突检测机制,乐观锁的并发性较好.因为改的时候,别人可以随便修改乐观锁的实现方式:
  常用的是版本方式
每次更新时,根据主键和现有的版本号查询到当前数据,更新成功之后,该条数据的版本号在原有的基础上加1.
如果在其它数据的并发事务中更新该数据,版本号比现有的版本低,就无法更新.
  在多事务并发执行操作,在结果处通过版本号的比较来控制结果

  private Integer version

Hibernate使用乐观锁时需要在映射文件中配置才可生效,配置乐观锁必须紧跟id之后

 <version name="version" column="ver"></version>
//(1) 读取配置文件
  Configuration config = new Configuration().configure();
  //(2)创建会话工厂对象
 SessionFactory sf = config.buildSessionFactory();
  //(3)加载session对象
 Session session = sf.openSession();
  //(4)开启事务
 Transaction transaction = session.beginTransaction();
  //(5)更新数据
 TestIsolation tI = new TestIsolation();
          tI.setName("碧瑶");`
          tI.setId(4);
/*当要对该实体进行更新操作时,
  会先判断version的值是否比显示的version值大,
  如果大更新,否则不更新,抛出异常*/
 /*由于悲欢锁会影响事务并发效率,所以不推荐使用*/
        tI.setVersion(1);
        session.update(tI);
     String hql = "from TestIsolation";
Query<TestIsolation> query = session.createQuery(hql);
         //执行
  query.setLockOptions(LockOptions.UPGRADE);
 //(6) 提交事务
         transaction.commit()
//设置mysql事务隔离级别
set session transaction isolation level 设置事务隔离级别;
//mysql 8版本查询隔离级别
select @@transaction_isolation; 
//查看是否自动提交
show variables like 'autocommit';
//设置自动提交.  0是关闭  1是开启
set autocommit=0;
//查看数据条数
select count(*) from teacher;

悲观锁和乐观锁的使用场景:

都是解决事务并发问题

悲欢锁:
  数据被外界修改持保守态度,因此,
  在整个数据处理过程中,将数据处于锁定状态.
悲欢锁的实现,往往依靠数据库提供的锁机制,以保证最大程度的独占性.
悲观锁一般适合短事物比较多(如某一个数据取出之后加1,立即释放)
悲欢锁很少使用,因为它大大限制了并发性

乐观锁:
采取更加宽松的加锁机制,适合于长事务,
基于数据版本(Version)记录机制实现.

数据版本:
   数据增加一个版本标识,在基于数据库表的版本解决方案中,一般是通过为数据库增加一个”version”字段来实现.
  
乐观锁的工作原理:
  读取出数据时,将此版本号一同读出,之后更新时,对此版本号加一。此时,将提交数据的版本数据与数据库表对应记录的当前版本信息进行比对,如果提交的数据版本号大于数据库表当前版本号,则予以更新,否则认为是过期数据。

猜你喜欢

转载自blog.csdn.net/zs1342084776/article/details/81139559