环境:
我们有一个部署在JBoss 4.2.3.GA服务器上的应用程序,它使用的是Hibernate 3.4和JTA 1.0。
有一个导入器可以创建或更新某些实体,然后导入一些数据。由于多种原因,大部分导入都是在新事务中完成的,并且在每个事务中,外部事务中创建/更新的实体可能会再次更新。
调用序列看起来像这个伪代码:
服务1:
//container managed transaction T1 is started here
import() {
A a = ... ;//new or read from database
if( isNew( a ) ) {
create(a);
} else {
update(a);
}
Service1 s = ...; //injected or looked up
for( D d : someDataList ) {
//nested transaction T2 is started due to this call, T1 should be suspended
s1.importData(d);
//nested transaction T2 should have been committed here
}
服务2:
@TransactionAttribute(REQUIRES_NEW)
importData(D d) {
A a = ...; //get the corresponding A instance and update as needed
update(a);
//other stuff such as importing d
}
问题:
现在的问题是,我们最终遇到了竞争条件,有几个交易试图锁定相同的表格,但到目前为止我们既没有能够重现问题,也没有确定真正的原因。
我们有一些假设:
由于在T1期间某些实体被更新,因此转换会获得一些数据库锁。然后T1被暂停,因为T2被启动,这反过来试图获取相同的数据库锁,因此被阻止。 T2最终超时,然后T1可以正常完成并释放锁。
可能的解决方案?:
到目前为止,似乎只有一种可能的解决方案:将T1中的所有更新包装到另一个事务T1 *中(可能完全跳过T1)并使T1 *和T2顺序运行。
这是一个理智的解决方案,只要商业案例允许(我不确定,因为我自己没有实施该商业案例)?
也可能有其他解决方案,如果有,请提供一些提示。但是,我怀疑是因为看起来T1在T2试图获取它之前必须释放锁定,因此基本上使T1和T2顺序运行。
问题
从上面可以看出以下问题:
感谢阅读所有这些:)
更新1:
由于我不是代码的作者,我也必须深入研究它。到目前为止,没有任何关于Hibernate中显式锁定的提示,因此AFAIK Hibernate在写入数据库时只使用数据库锁。当数据库连接打开时。
我们正在使用自动刷新,因此T1在某些情况下T2尝试相同之前可能会打开连接,但T1无法提交并关闭连接,因为它已暂停,直到T2提交为止。因此,在T2刷新之前,由于T1的刷新而导致数据库似乎获取的锁定无法释放。
使用手动刷新不是一种解决方案,因为如果T2在T1之前提交但是对实体的更改是相反的,那么我们就会丢失更新。我知道这是设计中的一个缺陷,我们需要解决这个问题,但我也想确认我们的假设是正确的,以便提供合理的解决方案:)
答案 0 :(得分:1)
我会尝试提供一些线索,
你说该表已被锁定。我没有看到你在做什么。默认情况下,Hibernate没有获取任何锁。您需要明确告诉它,see。即使在这里,你也不是在桌子上获得锁定而是在单排上。只有在使用悲观锁定并且两个事务都需要锁定在同一行时,您的假设才能成立。
如果您没有使用悲观锁定,那么当T1尝试提交时,您应该有乐观锁定异常。因此,您的方案可能导致同一个对象未被两个事务更新。
如果没有调试,并且知道“创建”和“更新”中发生了什么,很难回答您的问题。