如何更新 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
查询。更新后的@Query
s 如下所示:
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 更新可审计字段?