Hibernate--悲观锁和乐观锁

当多个事务同时访问数据库中的相同数据时,如果没有采取必要的隔离措施,将会导致各种并发问题,这时可以采取悲观锁乐观锁对其控制。


悲观锁:
悲观锁是在每次操作数据时,都悲观地认为会有其他事务也会来操作同一数据。因此,在整个数据处理过程中,将数据处于锁定状态。悲观锁由数据库来实现,在锁定的时间其他事务不能对数据进行存取,这样很有可能造成长时间等待。在Hibernate中,用户可以显式地设定要锁定的表或字段及锁的模式。Hibernate锁模式有如下几种:

  1. LockMode.NONE:如果缓存中存在对象,直接返回该对象的引用,否则通过select语句到数据库中加载该对象,这是锁模式的默认值。
  2. LockMode.READ:不管缓存中是否存在对象,总是通过select语句到数据库中加载该对象,如果映射文件中设置了版本元素,就执行版本检查,比较缓存中的对象是否与数据库中的对象版本一致。
  3. LockMode.UPGRADE:不管缓存中是否存在对象,总是通过select语句到数据库中加载该对象,如果映射文件中设置了版本元素,就执行版本检查,比较缓存中的对象是否与数据库中的对象版本一致,如果数据库系统支持悲观锁(如Oracle/MySql),就执行select…for update语句,如果不支持(如Sybase),就执行普通select语句。
  4. LockMode.UPGRADE_NOWAIT:与LockMode.UPGRADE具有同样功能,此外对于Oracle等支持update nowait的数据库,执行select…for update nowait语句,nowait表明如果执行该select语句的事务不能立即获得悲观锁,那么不会等待其他事务释放锁,而是立刻抛出异常。
  5. LockMode.WRITE:保存对象时会自动使用这种锁定模式,仅供hibernate内部使用,应用程序中不应该使用它。
  6. LockMode.FORCE:强制更新数据库中对象的版本属性,从而表明当前事务已经更新了这个对象。

设置锁模式的方法:

  • 调用Session.load()时指定锁定模式
  • 调用Session.lock()
  • 调用Query.setLockMode()

乐观锁:
相对于悲观锁,乐观锁认为多个事务同时操作同一数据的情况很少发生,因此乐观锁不做数据库层次上锁定,而是基于数据版本标识实现应用程序级别上的锁定机制,既能保证多个事务的并发操作,又能有效防止第二类丢失更新的发生。
数据版本标识,就是通过为数据表增加一个“version”字段实现。读取数据时,将版本号一同读出,之后更新此数据时,将此版本号加1。在提交数据时,将现有版本号与数据库对应记录的版本号进行对比,如果提交数据的版本号大于数据表中的版本号,则允许更新数据,否则禁止更新数据。
在hibernate中,为乐观锁提供了两种基本版本控制的实现,分别是基于version的实现和基于timestamp的实现。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Hibernate可以使用悲观锁乐观锁来控制多个线程同时访问同一条记录时的并发性问题。 实现悲观锁的方法是,在Hibernate的查询语句中使用“for update”语句,例如: ``` Session session = sessionFactory.getCurrentSession(); Transaction tx = session.beginTransaction(); try { Item item = (Item) session.get(Item.class, itemId, LockMode.UPGRADE); item.setPrice(newPrice); session.update(item); tx.commit(); } catch (Exception e) { tx.rollback(); throw e; } ``` 在这个例子中,我们使用了LockMode.UPGRADE参数来获取悲观锁,这会在数据库中将该行记录锁定,直到事务提交或回滚为止。 要实现乐观锁,可以使用Hibernate的@Version注解来定义一个版本号属性,例如: ``` @Entity public class Item { @Id private Long id; private String name; private double price; @Version private int version; // getters and setters } ``` 在使用乐观锁的代码中,我们首先获取实体对象,修改实体对象的属性值,然后执行更新操作,例如: ``` Session session = sessionFactory.getCurrentSession(); Transaction tx = session.beginTransaction(); try { Item item = (Item) session.get(Item.class, itemId); item.setPrice(newPrice); session.update(item); tx.commit(); } catch (StaleObjectStateException e) { tx.rollback(); throw new OptimisticLockException("The item has been updated by another transaction", e); } catch (Exception e) { tx.rollback(); throw e; } ``` 在这个例子中,如果在我们修改实体对象的属性值后,有另一个事务已经修改了该实体对象,那么我们就会捕获到StaleObjectStateException异常,这时我们就可以回滚事务并抛出一个OptimisticLockException异常,提示用户该实体对象已经被其他事务修改过了。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值