前言
最近一个项目中使用了Hibernate的乐观锁,不巧的是出现了乐观锁最容易报的错:org.hibernate.StaleObjectStateException: Row was updated or deleted by another transaction
下面将通过一个模拟的实例重现问题,并做相应的分析。
乐观锁的作用
乐观锁的主要作用是为了解决事务并发带来的问题,相对于悲观锁而言,乐观锁机制采取了更加宽松的加锁机制;
悲观锁大多数情况下依靠数据库的锁机制实现,以保证操作最大程度的独占性,但随之而来的就是数据库性能的大量开销,特别是对长事务而言;
乐观锁机制在一定程度上解决了这个问题;乐观锁,大多是基于数据版本(Version)记录机制实现;
乐观锁原理:读取出数据时,将此版本号一同读出,之后更新时,对此版本号加一;
此时,将提交数据的版本数据与数据库表对应记录的当前版本信息进行比对,如果提交的数据版本号大于数据库表当前版本号,则予以更新,否则认为是过期数据;
当出现version过期时,会抛出如下错误:
public StaleObjectStateException(String persistentClass, Serializable identifier) {
super("Row was updated or deleted by another transaction (or unsaved-value mapping was incorrect)");
this.entityName = persistentClass;
this.identifier = identifier;
}
本地重现
1.MYSQL建表SQL如下
CREATE TABLE `test_bean` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`version` int(11) NOT NULL,
`status` int(11) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8;
INSERT INTO `test_bean` VALUES ('1', '1', '1');
其中的version字段,就是用来做乐观锁处理的
2.实体TestBean以及TestBean.hbm.xml
public class TestBean {
private long id;
private int version;
private int status;
}
TestBean.hbm.xml如下:
<hibernate-mapping package="zh.maven.hibernate_version">
<class name="TestBean" table="test_bean" optimistic-lock="version">
<id name="id" column="id">
<generator class="native" />
</id>
<version name="version" column="version" type="integer"></version>
<property name="status" />
</class>
</hibernate-mapping>
3.主要代码
其中包括FirstService调用SecondService实现查询和更新,SecondService调用SecondDao实现查询和更新,代码如下:
public class FirstService {
private SecondService secondService;
public void modifyProcess() {
TestBean testBean = secondService.get(1L);
printLog(testBean);
testBean.setStatus(4);
secondService.update(testBean);
printLog(testBean);
sendNotify(testBean);
}
private void sendNotify(TestBean testBean) {
testBean.setStatus(20);
secondService.update(testBean);
printLog(testBean);
}
......
}
public class SecondService {
private SecondDao secondDao;
public TestBean get(long id) {
return s