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

4

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

  • Spring Boot Starter 1.5.3.RELEASE
  • Hibernate 5.2.10.Final
  • Spring Data Envers 1.1.3.RELEASE -> Spring Data JPA 1.11.3.RELEASE

测试过的数据库:

  • H2
  • PostgreSQL
  • MariaDB
  • Oracle

实体利用@Version注释字段,在更新时将被增加。一个示例实体如下所示:

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)

}

Spring Data JPA基于的UserRepository提供了除CRUD之外的自定义修改查询功能。
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语句:update “TEST”。“users” set user0_。“version”=user0_。“version”+1,“active”=0 where(“id”)IN(select“id”from“HT_users”)[42102-194]
那么,我该如何在Spring Data JPA @Modifying @Query查询中更新JPA / Hibernate @Version字段?
2个回答

2
您可以在查询中添加 u.version=u.version+1。更新后的@Query如下所示:
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);

}

使用此方法可能存在注意事项。例如,这显然不会像EntityManager发出的更新一样失败,因为OptimisticLockException(如此描述)。


尝试使用@Lock(LockModeType.OPTIMISTIC_FORCE_INCREMENT)代替手动增加版本号。 - Jay Smith
@JaySmith非常感谢您的提示,但我觉得Lock注释似乎不能与修改查询(如我的示例存储库中的查询)结合使用。至少当我将其添加为原样时,我会得到一个相当自说明的异常:“java.lang.IllegalStateException:在非SELECT查询上设置锁定模式的非法尝试”。文档和API文档在这里对我没有帮助,或者我漏掉了什么。我还需要考虑其他方面吗? - AntiTiming
我使用 versioned 关键字创建了 HQL 查询,它可以正常工作。但是你会收到错误信息 Table "USER0_" not found。这与 versioned 关键字有关吗?在 Hibernate 文档 中提到,您不能显式地修改 version - Jay Smith
@JaySmith,你在HQL中使用了Query注解吗?JPA设置方面怎么样,例如全局引用标识符。我会更新我的问题并添加一些我缺失的设置。而且你说得对,Hibernate文档说禁止手动更改版本。我会再次查看Lock注解,也许我能找到提示如何正确更新版本。实际上这就是我提出这个问题的原因,因为目前对我来说并不明显。再次感谢你的支持! - AntiTiming
我没有使用Spring Data JPA。我使用session.createQuery方法创建了HQL查询。 - Jay Smith
@JaySmith 好的,谢谢您的更新。我没有时间进一步调查 VERSIONED 关键字。关于锁定注释,我提出了另一个 SO 问题以澄清是否可能,参见此处:https://dev59.com/yKHia4cB1Zd3GeqPWaB0 - AntiTiming

0

如同在这里回答类似问题所述 - 尝试在UPDATE关键字后添加VERSIONED关键字:

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

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接