我正在通过Hibernate 3针对Postgres数据库在JPA中使用悲观锁定。我无法超时锁定-它似乎永远挂着。
这里是一个例子:
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毫秒)来获取锁,然后应该引发异常。而是代码陷入僵局。
如果我在两个不同的线程中运行它,那么我会看到线程2(带有em2)在线程1(em1)释放它后立即获得了锁。因此锁定正在发生,只是永不超时。
我使用PESSIMISTIC_WRITE并具有任何超时值(2ms,0ms'NO WAIT')等都会得到相同的效果。
我正在使用Hibernate 3.6.10 Final(最新的Hibernate 3版本)和Postgres jdbc驱动程序9.2-1003.jdbc4(最新驱动程序)。我正在针对Postgres 8.4数据库运行。
我发现的所有文档都建议这样做。有什么想法吗?
谢谢,Alastair
用于更新语法的Postgres SELECT仅提供在无法立即获得锁定的情况下不等待的选项。请参阅postgres文档。
为了防止操作等待其他交易提交,请使用NOWAIT选项。使用NOWAIT,该语句报告一个如果无法锁定选定的行,则返回错误,而不是等待立即。请注意,NOWAIT仅适用于行级锁-所需的ROW SHARE表级锁仍然是普通的方式(请参阅第13章)。您可以先将LOCK与NOWAIT选项一起使用,如果您需要不等待就获取表级锁。
[当使用postgres时,我已经观察到超时超过0的任何值都会导致休眠发出SELECT FOR UPDATE
,但是当超时为0时它将发出SELECT FOR UPDATE NO WAIT
将其放入您的persistence.xml:
<property name="javax.persistence.lock.timeout" value="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);
}
}