Hibernate学习_021_Hibernate中的事务控制+乐观锁+悲观锁

事务具有ACID(Atomicity+Consistency+Isolation+Durability)特性,在多线程中存在事务并发的情况,如果处理不好的话,会出现以下五中问题:

       1:丢失更新

2:脏读:程序读到了其他程序插入到数据库中但还没有正式提交的数据。

3:不可重复读

4:第二类丢失更新

5:幻像读取:牵涉到插入和删除操作,也是不可重复读取的一个特例。

对于数据库的并发事务管理,可以通过查询java.sql.Connection的API得到数据库的事务隔离机制。基本来看有如下四种机制:

1:read-uncommitted(value=1)机制:可以读到其他并发进程没有提交的数据,很不安全。

2:read-committed(value=2):只可以读到其它进程已经提交的数据。(最佳实践

3:repeatable-read(value=4):可重复读取,其实内部实现机制就是给数据库中的对应数据加了一把锁,这样,当前进程用完此数据之前,其他所有的进程都不可以更新此数据。

4:serializable(value=8):将各个进程序列化,也就是将各个进程先排队,挨个排的执行,这样可以保证事务绝对安全,但是这样效率是最低的。

那么,在Hibernate中如何设置事务隔离级别呢?一般我们可以再hibernate.cfg.xml中设置属性hibernate.connection.isolation来实现。这里一般设置此属性的值为2(read-committed)。但是,这样可能造成不可重复读取。比如一个事务A读取了事务B已经提交的更新数据,但是如果事务B回滚了,这个时候,A第二次读取这个数据,这个时候,前后两次读取结果不一致,造成不可重复读取。面对这个问题,在hibernate中是使用加锁机制来解决这个问题的。加锁又分两种:乐观锁、悲观锁。(程序中一般不考虑幻象读取,因为在一个事务中本身就很少查询两次数据库的,用的少。)

悲观锁:在程序中,悲观的认为本进程把数据读取出来后,一定会有其他进程去修改数据库总已经读取出来的数据。所以就干脆索性给数据库中对应的记录加一把锁,所以,悲观锁的实现依赖于数据库本身的加锁机制。(我们一般的sql:select * ....... for update就是强制数据库给数据记录加锁。)

那么hibernate程序如何实现悲观锁呢?在用get()或者load()方法取对象的时候,可以选择加锁模式:一般而言只可以选择LockMode.UPGRADE这个模式(因为其他加锁模式是Hibernate内部的默认实现,在Hibernate内部使用,不需要用户去理会,比如:LockMode.None、LockMode.Read、LockMode.Write三个)。代码如下:

	@Test
	public void testQuery() {
		Session session = this.sessionFactory.openSession();
		session.beginTransaction();
		Category c = (Category) session.load(Category.class, 1, LockMode.UPGRADE);
		session.getTransaction().commit();
		session.close();
	}
看测试结果,发出的sql语句末尾加了“for update”来强制让数据库给对应的数据记录加锁,如下所示:

    select
        category0_.id as id0_0_,
        category0_.name as name0_0_ 
    from
        Category category0_ 
    where
        category0_.id=? for update
乐观锁:这就是完全依靠程序来加锁, 不依赖于具体数据库,就是 乐观的认为本进程把数据读取出来后, 不一定 会有其他进程去修改数据库中已经读取出来的数据。如果就是修改了也没事,就报一个异常就可以了。乐观锁效率较高,不会对数据库中的数据记录加锁。

乐观锁的实现方式是:对应的类中加入一个“版本号”字段,并在对应的get方法上用@Version标示,这样,数据库会在每次更新此类对应的数据时,会自动将此记录的版本号字段值自动加1,这样,每次当前进程通过对比此字段的值就能够知道是否还有其他进程在自己对数据进行操作的过程中对数据库中的此条数据记录进行了修改。

比如对Category类加入一个乐观锁控制:代码如下:

@Entity
@Cache(usage=CacheConcurrencyStrategy.READ_WRITE)
public class Category {
	private int id;
	private String name;
	private List<Topic> topics;
	private int version;
	@Version
	public int getVersion() {
		return version;
	}
	public void setVersion(int version) {
		this.version = version;
	}

这样,数据库表中就会多一个version字段,用来实现乐观锁机制的数据版本控制。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值