如何在Spring Data JPA中使用Hibernate @DynamicUpdate?

Rav*_*per 6 java spring hibernate spring-data-jpa

我正在使用spring data-jpa。我只想更新一栏。

我的资料库是;

public interface UserRepository extends JpaRepository<User,Long> {
}
Run Code Online (Sandbox Code Playgroud)

我的服务是;

public User save(User user) {
    return userRepository.save(user);
}
Run Code Online (Sandbox Code Playgroud)

我的实体;

@Entity
@DynamicUpdate(true)
public class User implements Serializable {
    // column definitions, etc.
}
Run Code Online (Sandbox Code Playgroud)

如何只更新其中的一列User

Spa*_*ara 7

首先,我想解释为什么@DynamicUpdate对您不起作用。所以我应该注意如何@DynamicUpdate工作:

@DynamicUpdate批注用于指定每当修改实体时都应生成UPDATE SQL语句。默认情况下,Hibernate使用缓存的UPDATE语句来设置所有表列。当使用@DynamicUpdate批注对实体进行批注时,PreparedStatement将仅包括其值已更改的列。(更多详细信息

例如,假设User具有一个名为的属性name

因此,如果您是第一次用定义的名称保存用户,那么现在您正在传递,null因为该值@DynamicUpdate会将其假定为更改,并尝试将名称更新为null

第一个解决方案:

作为第一个解决方案,如果您想要@DynamicUpdate工作,则首先应User使用旧值填充所有其他属性,然后将其保存。

优点:生成的SQL查询仅更新您想要的属性。

缺点: Hibernate必须每次都生成相应的SQL字符串,因此Hibernate方面会降低性能。

第二种解决方案:

您可以User使用自定义查询进行更新:

@Modifying
@Query("UPDATE User SET name=(:name) WHERE id=(:id)")
public void updateName(@Param("name")String name, @Param("id")Long id);
Run Code Online (Sandbox Code Playgroud)

第三种解决方案:

作为最后的解决方案,我建议您使用updatable = false。这将在实体插入的第一刻填充属性。

  @Column(name = "create_date", nullable = false, updatable = false)
    private Instant createDate;
Run Code Online (Sandbox Code Playgroud)


bur*_*ete 7

您的问题是由于您传递了一个全新的User实体,因此 Hibernate 无法使用已经从数据库中获取的缓存版本并决定动态更新哪些列。

因此,请尝试执行以下操作以确认@DynamicUpdate;

服役中;

@Transactional
public User save(User newUser) {
    User currentUser = userRepository.get(newUser.getId());
    // handle merging of updated columns onto currentUser manually or via some mapping tool
    currentUser.setName(newUser.getName());
    return userRepository.save(currentUser);
}
Run Code Online (Sandbox Code Playgroud)

有了上面的逻辑,再加上动态更新注释,你应该只能看到 name 列的更新,除非你启用了一些审计,或者使用了@Version'ed 列。

如果更新的列总是相同的,那么最好将updatable = false@Column用于定义中不是更新目标的列,因为使用@DynamicUpdate效率非常低,因为它重新生成每个 sql,从不使用缓存的 sql。但请注意使用此功能,因为您根本无法更新这些列。

我不建议使用,@Query除非您有原生 JPA/Hibernate 不足的情况,但如果您有一个仅更新目标列集的用例,它是最好的选择,也是最有效的。

如果更新的列有很多,可以极大地变化,要么限定之间的手动映射逻辑newUsercurrentUser,或利用一些映射工具,如Orika,我有一个类似的自动化结构,其中数百实体通过这些映射器修补,允许一个非常通用的方法处理许多 CRUD 操作。