2

环境:

我们有一个部署在 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 又尝试获取相同的数据库锁并因此被阻塞。T2 最终超时,然后 T1 可以正常完成并释放锁。

可能的解决方案?:

到目前为止,似乎只有一种可能的解决方案:将 T1 中的所有更新包装到另一个事务 T1* 中(可能完全跳过 T1)并让 T1* 和 T2 顺序运行。

如果业务案例允许这样做(我不确定,因为我自己没有实施该业务案例),这是否是一个理智的解决方案?

可能还有其他解决方案,如果有,请提供一些提示。但是,我对此表示怀疑,因为似乎 T1 必须在 T2 尝试获取锁之前释放锁,因此基本上使 T1 和 T2 顺序运行。

问题

综上所述,出现以下问题:

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

感谢您阅读所有这些:)

更新1:

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

我们正在使用自动刷新,因此在某些情况下,T1 可能会在 T2 尝试相同操作之前打开连接,但 T1 无法提交和关闭连接,因为它在 T2 提交之前被挂起。因此,数据库似乎由于 T1 的刷新而获得的锁在 T2 刷新之前也无法释放。

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

4

1 回答 1

1

我会尝试提供一些线索,

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

如果你没有使用悲观锁,你应该在 T1 尝试提交时遇到乐观锁异常。因此,您的方案可能会导致两个事务都没有更新相同的对象。

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

于 2013-03-25T18:19:58.770 回答