我正在使用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
中的一列?
答案 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不足的情况,但是如果您有用例仅更新目标列集,那是最佳选择,效率最高。
如果更新的列很多并且可以有很大的不同,要么在newUser
到currentUser
之间定义手动映射逻辑,要么使用诸如Orika
之类的映射工具,我有一个类似的自动结构,其中通过这些映射器修补了数百个实体,从而提供了一种非常通用的方式来处理许多CRUD操作。