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

时间:2018-09-04 08:54:37

标签: java spring hibernate spring-data-jpa

我正在使用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中的一列?

2 个答案:

答案 0 :(得分:5)

首先,我想解释为什么@DynamicUpdate对您不起作用。因此,我应该注意@DynamicUpdate的工作原理:

  

@DynamicUpdate批注用于指定   每当实体时都应生成UPDATE SQL语句   被修改。默认情况下,Hibernate使用缓存的UPDATE语句,该语句   设置所有表列。当实体带有注释时   @DynamicUpdate批注,PreparedStatement将包括   仅更改其值的列。(more details

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

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

第一个解决方案:

作为第一种解决方案,如果希望null有效,则应先用旧值填充所有其他@DynamicUpdate属性,然后再保存。

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

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

第二个解决方案:

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

User

第三种解决方案:

作为最后的解决方案,我建议您使用@Modifying @Query("UPDATE User SET name=(:name) WHERE id=(:id)") public void updateName(@Param("name")String name, @Param("id")Long id); 。这将在实体插入的第一刻填充属性。

updatable = false

答案 1 :(得分:3)

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

使用上述逻辑以及动态更新注释,除非启用了某些审核或使用@Version ed列,否则您应该能够看到only name列的更新。

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

我不建议使用@Query,除非您遇到本机JPA / Hibernate不足的情况,但是如果您有用例仅更新目标列集,那是最佳选择,效率最高。

如果更新的列很多并且可以有很大的不同,要么在newUsercurrentUser之间定义手动映射逻辑,要么使用诸如Orika之类的映射工具,我有一个类似的自动结构,其中通过这些映射器修补了数百个实体,从而提供了一种非常通用的方式来处理许多CRUD操作。