如何更新 Spring Data JPA @Modifying @Query 查询中的 JPA/Hibernate @Version 字段?

Posted

技术标签:

【中文标题】如何更新 Spring Data JPA @Modifying @Query 查询中的 JPA/Hibernate @Version 字段?【英文标题】:How to update the JPA/Hibernate @Version field in a Spring Data JPA @Modifying @Query query? 【发布时间】:2017-10-07 23:37:32 【问题描述】:

为了测试@Version 注释,我使用以下设置来管理持久性:

Spring Boot Starter 1.5.3.RELEASE 休眠 5.2.10.Final Spring Data Envers 1.1.3.RELEASE -> Spring Data JPA 1.11.3.RELEASE

测试的数据库:

H2 PostgreSQL MariaDB 甲骨文

实体正在使用@Version 注释字段,该字段将是incremented on update。一个示例实体如下所示:

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Table;
import javax.persistence.Version;

@Entity
@Table(name = "\"users\"")
public class User 

    @Id
    private Long id;

    private boolean active;

    @Version
    @Column(nullable = false)
    private long version = 0L;

    // getters/setters (not shown)


除了 CRUD 之外,基于 Spring Data JPA 的 UserRepository 还提供自定义修改查询

import org.springframework.data.jpa.repository.Modifying;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.CrudRepository;

public interface UserRepository extends CrudRepository<User, Long> 

    @Modifying
    @Query("UPDATE User u SET u.active = false WHERE u.id = ?1")
    void deactivate(Long id);

    @Modifying
    @Query("UPDATE User u SET u.active = false WHERE u.id IN :ids")
    void deactivateAll(@Param("ids") Long... ids);

    @Modifying
    @Query("UPDATE User u SET u.active = false WHERE u.id IN :ids")
    void deactivateAll(@Param("ids") Iterable<Long> ids);


由于 JPQL/HQL 查询的版本字段不会自动增加,这必须通过相应地调整自定义查询手动完成

尝试使用 Hibernate 特定(不符合 JPA)VERSIONED 关键字(仅限 HQL)来扩展查询时

@Modifying
@Query("UPDATE VERSIONED User u SET u.active = false WHERE u.id = ?1")
void deactivate(Long id);

生成的 SQL 无效独立于测试的数据库,导致错误看起来像这样(架构名称是 TEST):

未找到表“USER0_”; SQL 语句:更新 "TEST"."users" 集 user0_."version"=user0_."version"+1, "active"=0 where ("id") IN (从“HT_users”中选择“id”)[42102-194]

那么,如何在 Spring Data JPA @Modifying @Query 查询中更新 JPA/Hibernate @Version 字段?

【问题讨论】:

【参考方案1】:

回答了类似的问题here - 尝试在 UPDATE 关键字之后添加 VERSIONED 关键字:

@Modifying
@Query("UPDATE VERSIONED User...)

【讨论】:

【参考方案2】:

您可以添加 u.version=u.version+1 查询。更新后的@Querys 如下所示:

import org.springframework.data.jpa.repository.Modifying;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.CrudRepository;

public interface UserRepository extends CrudRepository<User, Long> 

    @Modifying
    @Query("UPDATE User u SET u.version=u.version+1, u.active = false WHERE u.id = ?1")
    void deactivate(Long id);

    @Modifying
    @Query("UPDATE User u SET u.version=u.version+1, u.active = false WHERE u.id IN :ids")
    void deactivateAll(@Param("ids") Long... ids);

    @Modifying
    @Query("UPDATE User u SET u.version=u.version+1, u.active = false WHERE u.id IN :ids")
    void deactivateAll(@Param("ids") Iterable<Long> ids);


使用这个可能会有一些注意事项。显然不会失败 OptimisticLockException 就像 EntityManager 发布的更新一样 (as described here)。

【讨论】:

尝试使用@Lock(LockModeType.OPTIMISTIC_FORCE_INCREMENT)而不是手动增加版本 @JaySmith 非常感谢您的提示,但在我看来,Lock 注释不能与我的示例存储库中的修改查询结合使用。至少我得到一个非常不言自明的异常:“java.lang.IllegalStateException:在非 SELECT 查询上设置锁定模式的非法尝试”只是按原样添加时。文档和 API 文档在这里对我没有帮助,或者我遗漏了一些东西。我还需要考虑其他任何事情吗? 我用versioned关键字创建了hql查询,它工作正常。你得到错误Table "USER0_" not found`。它与versioned 关键字有关吗?在hibernate docs 中,据说不能显式修改version @JaySmith 您是否对 HQL 使用了查询注释? JPA 设置怎么样,例如全局引用的标识符。我将更新我的问题并添加一些我缺少的设置。你是对的,休眠文档说禁止手动更改版本。我将再次查看 Lock 注释,也许我会在那里找到提示如何正确更新版本。实际上这就是我问这个问题的原因,因为到目前为止这对我来说并不明显。再次感谢您的支持! 我没有使用spring data jpa。我用session.createQuery 方法创建了hql 查询。

以上是关于如何更新 Spring Data JPA @Modifying @Query 查询中的 JPA/Hibernate @Version 字段?的主要内容,如果未能解决你的问题,请参考以下文章

如何更新 Spring Data JPA @Modifying @Query 查询中的 JPA/Hibernate @Version 字段?

如何将自定义sql附加到Spring Data JPA插入/更新

即使 JPA 实体不脏,我是不是可以强制 spring-data 更新可审计字段?

使用 spring data jpa 更新单个字段

Spring Data JPA Update @Query 没有更新?

Spring Data JPA 更新行而不获取行 ById