JPA:锁定依赖对象(eclipselink)

时间:2011-06-30 15:47:56

标签: java jpa transactions eclipselink

我在JPA中有一个简单的一对多关系。为了便于讨论,假设我的实体是人和电话,其中一个人可以拥有许多电话(号码)。

这两个对象都可以独立更新,并且我具有高并发性。我需要在两个对象上实现悲观锁定(这是我的假设)。

所以,在我的代码中,我做了类似的事情,

            em.getTransaction().begin();
            Person p = em.find(Person.class, id, LockModeType.PESSIMISTIC_WRITE);
            ...

和电话一样,

            em.getTransaction().begin();
            Phone ph = em.find(Phone.class, id, LockModeType.PESSIMISTIC_WRITE);
            ...

请注意,在Person事务中,可能会更新从属Phone对象,反之亦然。用例是lastModified标志。更新电话会更新拥有人员中的lastModified时间戳,相反,更新人员中的lastModified时间戳会更新电话中的lastModified标志(这是通过实体监听器完成的)。我意识到用例有点人为,但试着忽略它。

上面的代码导致死锁。

INFO: [EL Warning]: 2011-06-30 08:41:52.65--ServerSession(122902)--Exception [EclipseLink-4002] (Eclipse Persistence Services - 2.2.0.v20110202-r8913): org.eclipse.persistence.exceptions.DatabaseException
Internal Exception: java.sql.SQLTransactionRollbackException: A lock could not be obtained within the time requested
Error Code: 30000

此时我的假设是Person事务为Person A抓取一个锁,然后试图获取Phone 1的锁定。同时,Phone事务获取Phone 1的锁定,击败Person事务,然后试图抓住Person A.死锁的锁。

这只是一个猜测,它基于对事务如何工作的天真理解,并且基本上不了解对象锁定如何在JPA中工作。我会认为对象及其依赖是原子地锁定的......这就是为什么需要这样做才能发挥作用。

有什么想法吗?

我想要解决的一个想法是忘记在同一个事务中更新依赖对象。相反,在第一次完成更新依赖对象后打开一个新事务。

1 个答案:

答案 0 :(得分:2)

对你来说,同时你似乎很奇怪

  • 需要悲观锁(这似乎意味着你真的不希望两个交易同时更新人/电话)
  • 准备通过在两个单独的交易中执行单个操作来牺牲数据一致性

无论如何,如果你真的需要使用悲观锁,请确保始终以相同的顺序请求锁(人然后是电话,或者电话然后是人,但不是两者)。这可以防止你遇到的那种僵局。