【问题标题】:How to use hibernate @DynamicUpdate with spring data jpa?如何将休眠@DynamicUpdate与spring data jpa一起使用?
【发布时间】:2019-02-09 06:15:13
【问题描述】:

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

我的仓库是;

public interface UserRepository extends JpaRepository<User,Long> {
}

我的服务是;

public User save(User user) {
    return userRepository.save(user);
}

我的实体;

@Entity
@DynamicUpdate(true)
public class User implements Serializable {
    // column definitions, etc.
}

如何只更新User 中的一列?

【问题讨论】:

    标签: java spring hibernate spring-data-jpa


    【解决方案1】:

    您的问题是由于您传递了一个全新的 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);
    }
    

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

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

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

    如果更新的列很多并且变化很大,要么在newUsercurrentUser 之间定义手动映射逻辑,或者使用一些映射工具,例如Orika,我有一个类似的自动化结构,其中数百个实体通过这些映射器进行修补,允许以极其通用的方式处理许多 CRUD 操作。

    【讨论】:

    • 我想使用动态更新来更新用户的个人资料,如果用户有很多字段,那么很难设置所有值。
    • @RaviDeveloper 您不需要设置其他字段,在您执行repository.get() 后,您将拥有来自db 的现有User,只需更新其profile 列并执行@ 987654337@,请注意,所有这些都应该在@Transactional 方法中,就像我的回答一样。使用@DynamicUpdate 它应该可以工作,并且只更新更改的列; profile
    • @RaviDeveloper 但如果这是一个用例,意味着您将始终更新“仅用户的个人资料”,那么最好使用@Query,因为@DynamicUpdate 是相对较慢,但应该还可以
    • @RaviDeveloper 它应该在您的Entity 上进行注释,您在示例中正确使用它,尽管不需要将布尔值传递给它,因为默认情况下它是true。如果它不起作用,请显示您当前的实现正在执行什么样的 SQL,也许还有 hibernate config 等问题
    【解决方案2】:

    首先我想解释一下为什么@DynamicUpdate 不适合你。所以我应该注意@DynamicUpdate 的工作原理:

    @DynamicUpdate 注解用于指定 UPDATE SQL 语句应该在任何时候生成一个实体 被修改。默认情况下,Hibernate 使用缓存的 UPDATE 语句 设置所有表格列。当实体被注释时 @DynamicUpdate 注释,PreparedStatement 将包括 仅值已更改的列。(more details)

    例如,假设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);
    

    第三种解决方案:

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

      @Column(name = "create_date", nullable = false, updatable = false)
        private Instant createDate;
    

    【讨论】:

      猜你喜欢
      • 2021-09-30
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-03-10
      • 1970-01-01
      • 2014-10-21
      • 1970-01-01
      相关资源
      最近更新 更多