我正在使用spring数据库,我遇到了一个问题,我无法找到答案。我的存储库查询是:
@Modifying
@Query("UPDATE User u SET u.firstName = 'blabla' WHERE u.loginName = 'admin'")
public int test();
实体User
有一个javax.persistence.Version
注释字段:
@Version
private Long version;
当我执行查询时,版本字段不会更新,但如果不是查询,我会这样做:
User user = this.userRepository.findUserById(1L);
user.setFirstName("blabla");
this.userRepository.save(user);
版本字段已更新。为什么呢?
答案 0 :(得分:5)
JPA 2.0规范的第4.10节明确指出:
批量更新直接映射到数据库更新操作,绕过乐观锁定检查。如果需要,可移植应用程序必须手动更新版本列的值,和/或手动验证版本列的值。
一般来说,批量更新和删除几乎绕过了持久性提供程序应用的许多功能,您只需保存实体就可以使用这些功能。除了乐观锁定之外,还包括持久性提供程序管理的持久性操作级联。
答案 1 :(得分:0)
这是一个很好的问题,所以我决定将答案变成full-blown article。
要使用开放式锁定,首先需要一个version
列。
现在,如果批量更新语句不增加version
列,则可能发生丢失更新异常:
现在,为了避免丢失更新,您只需要增加version
列即可。
您可以使用任何类型的查询进行操作。
int updateCount = entityManager
.createQuery(
"update Post " +
"set " +
" status = :newStatus," +
" version = version + 1 " +
"where " +
" status = :oldStatus and " +
" lower(title) like :pattern")
.setParameter("oldStatus", PostStatus.PENDING)
.setParameter("newStatus", PostStatus.SPAM)
.setParameter("pattern", "%spam%")
.executeUpdate();
CriteriaBuilder builder = entityManager
.getCriteriaBuilder();
CriteriaUpdate<Post> update = builder
.createCriteriaUpdate(Post.class);
Root<Post> root = update.from(Post.class);
Expression<Boolean> wherePredicate = builder
.and(
builder.equal(
root.get("status"),
PostStatus.PENDING
),
builder.like(
builder.lower(root.get("title")),
"%spam%"
)
);
Path<Short> versionPath = root.get("version");
Expression<Short> incrementVersion = builder
.sum((short) 1, versionPath);
update
.set(root.get("status"), PostStatus.SPAM)
.set(versionPath, incrementVersion)
.where(wherePredicate);
int updateCount = entityManager
.createQuery(update)
.executeUpdate();
UPDATE post
SET
status = 2,
version = version + 1
WHERE
status = 0 AND
lower(title) LIKE '%spam%'
一旦增加version
列,将在并发事务中防止丢失更新,该并发事务加载了由bulk语句更新的实体的先前版本: