点评实习三面的时候被问到这个问题,现在整理一下,自己也加深印象。
数据库方面
1.悲观锁
概念:悲观锁在操作数据库过程中,认为其他外部操作会改变改变数据,因此将数据锁定,直到其操作完成。
做法:使用数据库提供的锁机制。
实现:使用“select * from B where id = 100 for update”,这条语句锁定了B表中id=100的记录,在该事务提交之前,外界是无法修改或读取该数据的。
举例:mysql innodb
set autocommit = 0;//设置事务不是自动提交
begin;或 start transanction;//开始事务
select * from B where id = 100 for update;
update B set status =1 where id = 100;
commit;//提交事务
注意点:比如上面这个事务,当有第二个事务select * from B where id = 100 for update时候,会等待第一个事务完成;
但是,select 语句不会受影响,可以读数据。
缺点:加锁的机制会导致效率低,影响其他用户访问。
2.乐观锁
概念:一般认为数据不会发生冲突,只有在提交的时候才检查数据是否有冲突,如果有冲突,返回错误信息,用户自己选择如何处理
做法:数据版本记录机制;给表增加一个字段,版本号或者时间戳。
版本号:读取数据时候,将版本号读出,数据每更新一次,version号+1,当提交更新的时候,检查版本号是否一致;一致的话,就进行更新;不一致的话,拒绝更新,用户重新操作;
时间戳:读取数据的时候,将时间戳读出,数据每次更新,将当前时间存入,当提交更新的时候,检查时间戳是否与上一次读取的相同,一致则更新,不一致,拒绝更新,用户重新操作;
Hibernate中乐观锁 悲观锁
1. 悲观锁
String hqlStr ="from TUser as user where user.name=Max";
Query query = session.createQuery(hqlStr);
query.setLockMode("user",LockMode.UPGRADE); //加锁
List userList = query.list();//执行查询,获取数据
生成的sql语句:
select tuser0_.id as id, tuser0_.name as name, tuser0_.group_id as group_id, tuser0_.user_type as user_type, tuser0_.sex as sex from t_user tuser0_ where (tuser0_.name='Erica' ) for update
可以看到也是用select ... for update实现的
2.Hibernate的加锁机制
LockMode.NONE:无锁
LockMode.WRITE:在写操作自动获取写锁(update insert)
LockMode.READ:在读取记录时候自动获取
上述三种在Hibernate内部使用,比如为了保证update过程中对象不会被外界修改,在save方法中自动为目标对象加WRITE锁。
LockMode.upgrade:利用sql for update实现
LockMode. UPGRADE_NOWAIT :Oracle的特定实现,利用Oracle的for update nowait子句实现加锁。
3.乐观锁
实现一、 配置optimistic-lock属性:
<hibernate-mapping>
<class name="org.hibernate.sample.TUser" table="t_user" dynamic-update="true" dynamic-insert="true" optimistic-lock="version">
……
</class>
</hibernate-mapping>
optimistic-lock属性有如下可选取值:
Ø none:无乐观锁
Ø version:通过版本机制实现乐观锁
Ø dirty:通过检查发生变动过的属性实现乐观锁
Ø all:通过检查所有属性实现乐观锁
实现二、添加一个Version属性描述符
<hibernate-mapping>
<class name="org.hibernate.sample.TUser" table="t_user" dynamic-update="true" dynamic-insert="true" optimistic-lock="version">
<id name="id" column="id" type="java.lang.Integer">
<generator class="native"/>
</id>
<version column="version" name="version" type="java.lang.Integer"/>
……
</class>
</hibernate-mapping>