我在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中工作。我会认为对象及其依赖是原子地锁定的......这就是为什么需要这样做才能发挥作用。
有什么想法吗?
我想要解决的一个想法是忘记在同一个事务中更新依赖对象。相反,在第一次完成更新依赖对象后打开一个新事务。
答案 0 :(得分:2)
对你来说,同时你似乎很奇怪
无论如何,如果你真的需要使用悲观锁,请确保始终以相同的顺序请求锁(人然后是电话,或者电话然后是人,但不是两者)。这可以防止你遇到的那种僵局。