写点什么

一个 Hibernate 的事务问题

用户头像
YoungZY
关注
发布于: 2020 年 09 月 30 日

本文首发于 http://www.YoungZY.com/



工作中遇到一个问题。为了方便说明,做了简化。

业务要求:有两个接口,要么都成功,要么都失败。即任一个接口调用失败,两个接口相关的数据(如果库里有的话)都要删除。

假设:tab_two 中已有一条数据,再调接口时会报主键冲突。

期望的结果:第一次调用时失败,并把tab_two 中已有的一条数据清除。第二次调用成功。

public class SomeInterface {
public void execute() {
try {
if (m1) {
method1();
} else if (m2) {
method2();
}
} catch (Exception e) {
hasException = true;
} finally {
if (hasException) {
clearData();
}
}
}
private void method1() {
// 用Hibernate写表 tab_one
}
private void method2() {
// 用Hibernate写表 tab_two
}
private void clearData() {
// 使用JDBC
// 删除表 tab_one
// 删除表 tab_two
}
}

问题:某一接口失败时,其数据未被清除。

伪码如上。写表Hibernate,删表JDBC。

接口2失败后,tab_one 的数据被清除了,但tab_two的数据还在。

猜测:Hibernate和JDBC有不同的事务处理机制。

以前也有类似的问题:

{
...
saveByHibernate();
// 调存储过程时会报错:用Hibernate存的表没有数据
invokeProduce();
...
}
// 修改后
{
...
saveByHibernate();
// 把Hibernate存的数据再查一下,执行存储过程就没问题了
load();
invokeProduce();
...
}

基于以上经验,修改代码:写表删表都用Hibernate。

经过测试,还是不行,失败接口的数据仍然存在。Debug的过程中发现,确实有delete,但删的是表里已有的数据,新数据(当前调用接口的数据)并未被删除,事务结束时被写入了表中。

想到Hibernate对象的三种状态:瞬时态(Transient)、持久态(Persistent)、脱管/游离态(Detached),于是使用了 evict(obj) 方法,结果还是不行。

public void clearData(Object errorData) {
getSession().evict(errorData);
// 使用Hibernate
// 删除表1对象,删之前先查
obj1 = load();
delete(obj1);
// 删除表2对象
obj2 = load();
delete(obj2);
}

通过日志发现,load之前会有insert,好像有个隐式的flush。

最后决定使用 getSession().clear() 试一下,居然意外地解决了。

没太搞清楚其中的原理,按照下图所示的各种状态之间的流转,evict和clear应该是一样的啊。

虽然不知其所以然,毕竟问题是解决了。





Hibernate版本:4.1.8.Final



阅读原文

发布于: 2020 年 09 月 30 日阅读数: 37
用户头像

YoungZY

关注

http://www.youngzy.com 2018.11.14 加入

还未添加个人简介

评论

发布
暂无评论
一个Hibernate的事务问题