出于并发目的,我需要在从AVAILABLE池中进行选择时将数据库列的状态更新为USED。
我正在考虑尝试@Modifying
和@Query
(根据where子句更新状态的查询)
一切都很好,但这是一个更新查询,所以它不会返回更新的数据。
因此,在spring数据中是否有可能更新并返回一行,以便首先读取该行的人可以专门使用它。
我的更新查询类似于UPDATE MyObject o SET o.state = 'USED' WHERE o.id = (select min(id) from MyObject a where a.state='AVAILABLE')
,因此基本上会将最低可用ID标记为已使用。有一个锁定选项,但这些需要特殊处理,如果另一个线程发生异常,请再试一次,这在我的场景中未获批准
答案 0 :(得分:1)
您需要显式声明一个事务,以避免其他事务能够读取所涉及的值,直到它提交为止。允许它的level with best performance是 READ_COMMITED ,它不允许来自其他事务的脏读(适合您的情况)。所以代码看起来像这样:
<强>回购:强>
@Repository
public interface MyObjectRepository extends JpaRepository<MyObject, Long> {
@Modifying
@Query("UPDATE MyObject o SET o.state = 'USED' WHERE o.id = :id")
void lockObject(@Param("id") long id);
@Query("select min(id) from MyObject a where a.state='AVAILABLE'")
Integer minId();
}
<强>服务强>
@Transactional(isolation=Isolation.READ_COMMITTED)
public MyObject findFirstAvailable(){
Integer minId;
if ((minId = repo.minId()) != null){
repo.lockObject(minId);
return repo.findOne(minId);
}
return null;
}
答案 1 :(得分:1)
我建议使用多个事务以及乐观锁定。
确保您的实体具有以@Version
注释的属性。
在第一个事务中加载实体,将其标记为USED
,关闭事务。
这将刷新并提交更改,并确保在此期间没有其他人触摸实体。
在第二笔交易中,您无法对实体执行任何操作。
对于这些小额交易,我发现将它们移至单独的方法很笨拙,因此我可以使用@Transactional
。因此,我改用TransactionTemplate
。