Spring @Transactional 死锁

发布于 2024-12-12 21:49:29 字数 2171 浏览 1 评论 0原文

我有以下设置。

  • Spring 3.0.5
  • Hibernate 3.5.6
  • MySql 5.1

要通过 Hibernate 在数据库中保存记录,我有以下工作流程

  1. send JSON {id:1,name:"test",children:[...] } 到 Spring MVC 应用程序并使用 Jackson 将其转换为对象图(如果它是现有实例,则 JSON 具有数据库集中记录的正确 ID

  2. 通过服务层调用将对象保存在DB中(详细信息如下)

    • 服务层接口SomeObjectService的save函数,上面有@Transactional注解,readOnly=false和Propagation REQUIRED< /p>

    • 该服务层的实现SomeObjectServieImpl调用DAO保存 方法

    • DAO 通过调用 hibernate 的合并来保存新数据,例如 hibernateTempate().merge(someObj)

    • hibernate merge 首先通过 SELECT 从数据库加载对象

    • 我有一个连接到 spring 的 EntityListener (我使用了这种技术 Spring + EntityManagerFactory +Hibernate Listeners + Injection)并监听@PostLoad

    • 侦听器使用 LockingServie 更新 someObject 的一个字段,将其设置为锁定(此实际上应该仅在 someObject 通过 Hibernate HQL、SQL 或 Criteria 调用加载,但也会在合并时调用)

    • LockingServie 有一个函数 lock(someObj,userId),它也用 @Transactional 和 readOnly=false 进行注释,并且必需

    • 更新是通过调用 Query query = sess.createQuery("update someObj setlockedBy=:userId"); 进行的,然后 query.executeUpdate();

    • 合并加载数据后,首先更新someObject并插入相关子对象(<=这里正是死锁发生的点)< /p>

  3. 将 JSON 结果(这也包括新创建的对象 ID)返回给客户端。

对我来说,问题似乎是,首先

  1. 记录被加载到一个事务中
  2. ,然后在另一个(内部)事务中被更改
  3. ,然后应该使用外部事务的数据再次更新,但由于它被锁定而无法更新。

我可以通过 MySQL 看到

SHOW OPEN TABLES 

子表(对象图的一部分)被锁定。

有趣的事实是,死锁不会发生在 someObj 表上,而是发生在代表子项的表上。

我在这里有点迷失了。非常欢迎任何帮助。

顺便说一句,也许可以 隔离级别能让我摆脱这个问题吗?

I have the following setup.

  • Spring 3.0.5
  • Hibernate 3.5.6
  • MySql 5.1

To save a record in the DB via Hibernate I have the following workflow

  1. send JSON {id:1,name:"test",children:[...]} to Spring MVC App and use Jackson to transform it into an object graph (if it is an existing instance the JSON has the proper ID of the record in the DB set

  2. save the object in DB via service layer call (details below)

    • the save function of service layer interface SomeObjectService has the @Transactional annotation on it with readOnly=false and Propagation REQUIRED

    • the implementation of this service layer SomeObjectServieImpl calls the DAO save
      method

    • the DAO saves the new data via a call of hibernate's merge e.g. hibernateTempate().merge(someObj)

    • hibernate merge loads the object first from the DB via SELECT

    • I have a EntityListener who is wired to spring (I used this technique Spring + EntityManagerFactory +Hibernate Listeners + Injection) and listens to @PostLoad

    • The listener uses a LockingServie to updates one field of someObject to set it as locked (this should actually only happen when someObject is loaded via Hibernate HQL,SQL or Criteria calls but gets called also on merge)

    • the LockingServie has a function lock(someObj,userId) which is also annotated with @Transactional with readOnly=false and REQUIRED

    • the update happens via a call of Query query = sess.createQuery("update someObj set lockedBy=:userId"); and then
      query.executeUpdate();

    • after merge has loaded the data it start with updating someObject and inserting relevant children (<= exacely here is the point where the deadlock happens)

  3. return JSON result (this also includes the newly created object ID) back to client.

The problem seems for me that first

  1. the record gets loaded in a transaction
  2. then gets changed in another (inner-)transaction
  3. and then should get updated again with the data of the outer transaction but can't get updated because it is locked.

I can see via MySQL's

SHOW OPEN TABLES 

that a child table (that is part of the object graph) is locked.

Interesting fact is that the deadlock doesn't occur on the someObj table but rather on a table that represents a child.

I am a bit lost here. Any help is more than welcome.

BTW can maybe the isolation level get me out of this problem here?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

2024-12-19 21:49:29

我不知道问题的解决方案,但我不会有事务锁定方法。如果您确实需要手动锁定某些内容,请在另一个事务服务方法中进行锁定。

I don't know the solution to the problem, but I would not have a transactional lock method. If at all you need to lock something manually, make it within another transactional service method.

黯淡〆 2024-12-19 21:49:29

我最终使用了 @Bozho 的 HibernateExtendedJpaDialect

其解释如下>>
Hibernate、Spring、JPS 和 Hibernate隔离 - 不支持自定义隔离

将隔离设置为READ_UNCOMMITED

@Transactional(readOnly = false, propagation = Propagation.REQUIRED, isolation=Isolation.READ_UNCOMMITTED)
public Seizure merge(Seizure seizureObj);

我知道这不是一个很好的解决方案,但至少这解决了我的问题。

如果有人想要详细的描述,请询问...

I ended up using @Bozho's HibernateExtendedJpaDialect

which is explained here >>
Hibernate, spring, JPS & isolation - custom isolation not supported

To set the isolation to READ_UNCOMMITED

@Transactional(readOnly = false, propagation = Propagation.REQUIRED, isolation=Isolation.READ_UNCOMMITTED)
public Seizure merge(Seizure seizureObj);

Not a very nice solution I know but at least this solved my problem.

If somebody wanna have a detailed description please ask...

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文