JTA嵌套事务锁

时间:2013-03-25 16:49:28

标签: hibernate jboss transactions jta

环境:

我们有一个部署在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顺序运行。

问题

从上面可以看出以下问题:

  • 我们的假设是否正确,即T1是否持有T2所需要的锁定以及因为它被暂停而无法释放?
  • 上面描述的解决方案是唯一的方法还是没有手动事务划分的其他方法?

感谢阅读所有这些:)

更新1:

由于我不是代码的作者,我也必须深入研究它。到目前为止,没有任何关于Hibernate中显式锁定的提示,因此AFAIK Hibernate在写入数据库时​​只使用数据库锁。当数据库连接打开时。

我们正在使用自动刷新,因此T1在某些情况下T2尝试相同之前可能会打开连接,但T1无法提交并关闭连接,因为它已暂停,直到T2提交为止。因此,在T2刷新之前,由于T1的刷新而导致数据库似乎获取的锁定无法释放。

使用手动刷新不是一种解决方案,因为如果T2在T1之前提交但是对实体的更改是相反的,那么我们就会丢失更新。我知道这是设计中的一个缺陷,我们需要解决这个问题,但我也想确认我们的假设是正确的,以便提供合理的解决方案:)

1 个答案:

答案 0 :(得分:1)

我会尝试提供一些线索,

你说该表已被锁定。我没有看到你在做什么。默认情况下,Hibernate没有获取任何锁。您需要明确告诉它,see。即使在这里,你也不是在桌子上获得锁定而是在单排上。只有在使用悲观锁定并且两个事务都需要锁定在同一行时,您的假设才能成立。

如果您没有使用悲观锁定,那么当T1尝试提交时,您应该有乐观锁定异常。因此,您的方案可能导致同一个对象未​​被两个事务更新。

如果没有调试,并且知道“创建”和“更新”中发生了什么,很难回答您的问题。