什么是悲观锁
悲观锁的使用
select * from users where name = 'Jack' for update
这条SQL语句锁定了users表中所有符合检索条件(name="Jack")的记录。本次事务提交之前(事务提交时会释放事务过程中的锁),外界无法修改这些记录。Hibernate的悲观锁也是基于数据库的锁机制实现的。
String hql = "from Users where name = "Jack";
Query query = session.createQuery(hql);
query.setLockMode("users",LockMode.UPGRADE); //对users表进行加锁
List list = query.list(); //执行查询,获取数据
Hibernate的加锁模式有:
LockMode.NONE :无锁机制。
LockMode.WRITE :Hibernate在 Insert和 Update记录的时候会自动获取。
LockMode.READ :Hibernate在读取记录的时候会自动获取。
以上这三种锁机制一般由 Hibernate内部使用,如Hibernate为了保证 Update过程中对象不会被外界修改,会在 save 方法实现中自动为目标对象加上 WRITE锁。
LockMode.UPGRADE :利用数据库的 for update 子句加锁。
LockMode. UPGRADE_NOWAIT : Oracle的特定实现,利用 Oracle的 for update nowait子句实现加锁。
什么是乐观锁
与悲观锁相反,乐观锁使用完全不同的方式。乐观锁通过Version列保存当前数据版本,如果程序修改了数据,将将版本列加1.反过来,如果版本列有了变化,说明该数据被修改过了。程序保存数据时会检查数据的Version列。如果Version列已经发生了变化,程序会重新读取、修改并保存数据。由于该机制不需要锁定数据行,允许多线程同时访问同一条数据,因此被称为乐观锁。乐观锁的效率要高于悲观锁,因此现代编程更倾向于乐观锁。
乐观锁则认为其他用户企图改变正在更改的对象的概率是很小的,因此乐观锁直到准备提交所作的更改时才将对象锁住,读取以及改变该对象时并不加锁。可见乐观锁加锁的时间要比悲观锁短,乐观锁可以用较大的锁粒度获得较好的并发访问性能。
乐观锁的配置
Hibernate支持乐观锁,保存数据时Hibernate会自动完成检查Version列、修改数据、更新Version列等工作。Hibernate隐藏了所有的Version操作细节,只需要指定实体类的Version列即可。实体类中可用@Version配置版本属性,版本列一般为数字类型属性,代码如下。
@Version
private int version;
XML中使用<version />配置乐观锁,使用name属性版本列,注意,<version />版本列要配置在<id />主键后面、<property />普通属性前面,代码如下。
<version name="version"></version>
XML配置版本属性要比@配置灵活,版本属性即可以为int、long等数据类型,也可以为Timestamp时间戳等类型,配置时用type配置类型,代码如下。
<version type="timestamp" column="version"></version>
或者直接用<timestamp />配置日期版本,与上面的配置是等价的,代码如下。
<timestamp column="version" />