通过Spring Data JPA执行批量更新时@Version字段未更新

时间:2014-02-05 11:33:10

标签: jpa version jpa-2.0 spring-data spring-data-jpa

我正在使用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);

版本字段已更新。为什么呢?

2 个答案:

答案 0 :(得分:5)

JPA 2.0规范的第4.10节明确指出:

  

批量更新直接映射到数据库更新操作,绕过乐观锁定检查。如果需要,可移植应用程序必须手动更新版本列的值,和/或手动验证版本列的值。

一般来说,批量更新和删除几乎绕过了持久性提供程序应用的许多功能,您只需保存实体就可以使用这些功能。除了乐观锁定之外,还包括持久性提供程序管理的持久性操作级联。

答案 1 :(得分:0)

  

这是一个很好的问题,所以我决定将答案变成full-blown article

要使用开放式锁定,首先需要一个version列。

现在,如果批量更新语句不增加version列,则可能发生丢失更新异常:

Bulk update lost update

现在,为了避免丢失更新,您只需要增加version列即可。

您可以使用任何类型的查询进行操作。

JPQL

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();

标准API

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();

本地SQL

UPDATE post
SET
  status = 2,
  version = version + 1
WHERE
  status = 0 AND 
  lower(title) LIKE '%spam%'

一旦增加version列,将在并发事务中防止丢失更新,该并发事务加载了由bulk语句更新的实体的先前版本:

Bulk update with optimistic locking