【发布时间】:2019-12-31 16:57:17
【问题描述】:
我有一个非常简单的域模型,一个具有版本字段的实体,以便使用 JPA (api v2.2) 提供的乐观锁定功能。我使用的实现是 Hibernate v5.3.10.Final。
@Entity
@Data
public class Activity {
@Id
@SequenceGenerator(name = "gen", sequenceName = "gen_seq", allocationSize = 1)
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "gen")
private Long id;
@Column
private String state;
@Version
private int version;
}
然后对该实体进行简单的操作,例如临时更改其阶段:
@Transactional
public Activity startProgress(Long id) {
var entity = activityRepository.findById(id).orElseThrow(RuntimeException::new);
if (entity.getState() == "this") { // or that, or else, etc
// throw some exceptions in some cases
}
entity.setState("IN_PROGRESS");
return activityRepository.saveAndFlush(entity);
}
我想要实现的结果是有一种方法来更新实体,并且该更新还应该增加版本。如果存在不匹配,我希望会抛出 ObjectOptimisticLockingFailureException 或 OptimisticLockingException。我还想在对象中更新 version 字段的值,因为我将它返回给客户端。
我尝试过的几个选项:
- 只需调用
save- 版本字段被更新,但没有返回新值并且客户端获取旧值,这使得下一个请求遇到锁定异常 - 调用
saveAndFlush- 在这种情况下,我在客户端执行了两次更新语句,奇怪的是返回版本X,在数据库中版本是X+1。然后下一个客户端请求再次遇到锁定异常。 - 创建一个
@Modifying查询,自动将其标记为清除(以自动刷新更改)并使用hqlcreate versioned语法。然后我执行了以下查询:update activities set version=version+1, state=? where id=?,但这似乎没有进行乐观锁检查(where version = :version_from_entity)。我也不认为它会引发适当的例外。
所以,最终我想要实现的目标非常简单,我认为我不必自己编写它 - 有一种方法可以更新版本化实体上的一个或多个字段,依赖 JPA用于乐观锁定并获取最新版本,以便客户端可以对实体进行进一步的操作。我阅读了很多类似的问题,但大多数都直接使用实体管理器,这不是我想要的。
【问题讨论】:
-
版本应该会自动更新。您是否使用来自
javax.persistence的正确@Versionannotation? -
是的,
javax.persistence不是来自 spring data commons 的那个。 -
我在本地尝试
saveAndFlush(),但无法重现您提到的问题。它按预期工作,不再发生双重更新......那么在你的情况下,这两个 UPDATE SQL 是什么样的?它们是一样的吗?
标签: hibernate jpa spring-data-jpa spring-data