【问题标题】:How does Hibernate control the transaction with multiple session.update()s if you don't commit the transaction explicitly?如果您不明确提交事务,Hibernate 如何使用多个 session.update() 控制事务?
【发布时间】:2020-10-07 15:40:18
【问题描述】:

我继承了一个使用 Spring MVC 和 Hibernate 存储到 DB2 数据库中的系统。最近它开始出现看起来像 DB2 报告的死锁问题,所以我一直在调试它。我不是 Spring 或 Hibernate 专家,但我两者都知道。

代码结构如下:

controller -> service -> one or more DAO classes that performs the updates

在出现问题的情况下,控制器调用一个带有@Transactional@Override 注释的服务方法。该服务转到使用@Repository 注释的DAO 类,并调用使用@Transactional@Override 注释的多个方法。这就是代码的结构。

一个控制器调用一个服务方法。服务方法调用多个 DAO 方法。大多数 DAO 方法都标记为@Transactional@Override。一些在@Transactional 中有 REQUIRES_NEW。同样在每个 DAO 方法中,SessionSessionFactory.getCurrentSession() 获取。

第一个问题:使用这种方法,事务从哪里开始,最终提交在哪里?和回滚?这些都没有明确编码。

当其中一个方法更新记录,然后后续方法删除同一记录时,就会出现死锁。如果您想知道为什么有人想要这样做,更新会添加想要删除记录中信息的人的用户 ID,以便可以通过存储过程将其保存到另一个表中。然后第二个方法调用删除记录。

最初的编码员尝试将 REQUIRES_NEW 添加到 @Transactional,认为新事务将确保更新发生。但死锁仍然不定期发生。

我尝试将更新更改为原生 SQL 调用,但 Hibernate 不允许我启动另一个事务,因为一个事务已经打开。

这里有人有什么指导吗?

【问题讨论】:

  • 编辑了我原来的帖子来回答你的问题。
  • 是的,我确实发现 Hibernate 会忽略我假设的更新,因为它看到记录将被删除,但我不希望这样,因为在更新记录后类似于存储过程(在本例中是 DB2 的时态表系统)将记录复制到历史表中。最终发生的是 Hibernate 尝试首先更新一条记录(这导致 DB2 将该记录复制到另一个表),然后删除该记录。因此,正在对同一记录尝试三个数据库操作,我敢打赌这会导致死锁。
  • 死锁是两个事务互相等待放弃锁。

标签: java spring hibernate db2 database-deadlocks


【解决方案1】:

当一个方法(调用链中哪个类或方法本身首先用@Transactional注释)从外部调用(它必须从类外部调用,因为注释“创建”了一个代理)时,事务开始。链中具有默认传播级别的任何其他带注释的类/方法都将加入该事务。当调用事务标记为REQUIRES_NEW 的方法时,父事务将被挂起(如果存在)并开始新事务。请注意,如果您从同一类中的其他方法调用带注释的方法,则它不会通过代理,因此与您删除注释相同。提交发生在启动事务的方法返回之后。和回滚一样,是在抛出异常之后。死锁可能来自不同的地方,即使你有代码也很难找出来。例如,它可以来自更新和存储过程尝试锁定某些行和发生循环等待等...

我有一个天真的形象,背景发生了什么。 有两个类 ui 和 service。该服务被注释为@Transactional。 Spring 创建代理,所有来自外部的调用都经过它。在调用真正的方法之前,它会启动事务,之后它会提交。

【讨论】:

  • 非常感谢您解释它是如何工作的。现在我至少知道交易边界了。我在想我的死锁问题来自于存储过程试图同时访问相同的表记录作为尝试删除它的代码。
猜你喜欢
  • 2018-01-26
  • 1970-01-01
  • 1970-01-01
  • 2017-10-19
  • 2023-03-28
  • 1970-01-01
  • 2015-10-02
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多