JPA Pessimistic Lock尝试永远不会超时

时间:2013-10-09 07:47:52

标签: hibernate postgresql jpa pessimistic-locking

我正在尝试在JPA中使用悲观锁定,而在Hibernate 3中使用Postgres数据库。我无法让锁定超时 - 它似乎永远都会挂起。

以下是一个例子:

EntityManagerFactory factory; 

// (initialise the factory )

EntityManager em1 = factory.createEntityManager();
EntityManager em2 = factory.createEntityManager();

// em1 gets a lock

EntityTransaction transaction1 = em1.getTransaction();
transaction1.begin();
MyObject object1 = em1.find( MyObject.class, 1, LockModeType.PESSIMISTIC_READ );

// em2 tries for a lock

Map<String,Object> timeoutProperties = new HashMap<String,Object>();
timeoutProperties.put("javax.persistence.lock.timeout", 5000);

EntityTransaction transaction2 = em2.getTransaction();
transaction2.begin();
MyObject object2 = em2.find( MyObject.class, 1, LockModeType.PESSIMISTIC_READ, timeoutProperties );

// After five seconds I expect em2 to bail out, but it never does.

transaction1.rollback();
transaction2.rollback();

据我了解,em2应该尝试最多五秒钟(5000毫秒)来获取锁定然后应该抛出异常。而是代码陷入僵局。

如果我在两个不同的线程中运行它,那么我看到thread2(em1)释放它后,thread2(带有em2)就会获得锁定。所以锁定正在发生,只是永远不会超时。

我使用PESSIMISTIC_WRITE和任何超时值(2ms,0ms'NO WAIT')等获得相同的效果。

我正在使用Hibernate 3.6.10 Final(最新的Hibernate 3版本)和Postgres jdbc驱动程序9.2-1003.jdbc4(最新的驱动程序)。我正在与Postgres 8.4数据库竞争。

我发现的所有文档都表明这应该有效。有什么想法吗?

感谢, 阿拉斯泰尔

3 个答案:

答案 0 :(得分:8)

Postgres SELECT for update语法仅提供如果无法立即获取锁定则不等待的选项。见postgres docs。

  

防止操作等待其他事务   提交,使用NOWAIT选项。随着NOWAIT,该声明报道了一个   错误,而不是等待,如果无法锁定选定的行   立即。请注意,NOWAIT仅适用于行级锁定 -   所需的ROW SHARE表级锁仍然是普通的   方式(见第13章)。您可以先使用NOWAIT选项使用LOCK,   如果您需要获取表级锁定而无需等待。

使用postgres时,我发现超时的任何超过0的值都会导致hibernate发出SELECT FOR UPDATE但是当超时为0时会发出SELECT FOR UPDATE NO WAIT

答案 1 :(得分:0)

将它放在persistence.xml中:

<property name="javax.persistence.lock.timeout" value="0"/>

或者在调用第一个锁之前设置属性。

答案 2 :(得分:0)

当像下面这样提供时,

javax.persistence.lock.timeout也不对我有用

@QueryHints({@QueryHint(name = "javax.persistence.lock.timeout",value = "15000")})

但是后来我尝试了其他可行的方法。现在,我使用实体管理器配置hbernate,而不是使用@Repository和CrudRepository。与锁定和设置锁定超时一起使用了createQuery。并且此配置按预期工作。我有两个事务在并行运行,并试图锁定数据库中完全相同的行。第一个事务能够获取WRITE锁定,并在释放锁定之前将其保持约10秒钟。同时,第二个事务尝试获取同一行上的锁,但是由于javax.persistence.lock.timeout设置为15秒,因此它等待释放锁,然后获取自己的锁。因此,使流程序列化。

@Component
public class Repository {

    @PersistenceContext
    private EntityManager em;

    public Optional<Cache> getById(int id){
        List<Cache> list = em.createQuery("select c from Cache c where c.id = ?1")
                            .setParameter(1, id)
                            .setHint("javax.persistence.lock.timeout", 15000)
                            .setLockMode(LockModeType.PESSIMISTIC_WRITE)
                            .getResultList();


        return Optional.ofNullable(list.get(0));
    }

    public void save(Cache cache) {
        cache = em.find(Cache.class, cache.getId());
        em.merge(cache);
    }
}