Spring Boot JPA CrudRepository 选择性更新 - 只有一些属性

Posted

技术标签:

【中文标题】Spring Boot JPA CrudRepository 选择性更新 - 只有一些属性【英文标题】:Spring Boot JPA CrudRepository selective update - only some properties 【发布时间】:2017-08-04 00:49:44 【问题描述】:

在将 Hibernate 与 Spring Boot JPA 一起使用时,我注意到当尝试在扩展 Spring 的 CrudRepository 接口的存储库工件中实现更新方法时,必须将要更新的整个实体对象传递给这样的更新方法。直觉上这似乎很浪费,我想知道什么是让 Spring Data/JPA 假设只有那些传入的值需要更新而让那些没有传入的值保持不变的干净方法。

这是目前的服务方法,

   public void updateCustomer(Long id, Customer customer) throws Exception 
        customerRepository.save(customer);
    

【问题讨论】:

过早的优化是万恶之源。你为什么认为这是浪费...你将如何确定哪些字段发生了变化(让我猜猜,获取完整的对象,逐个字段比较并构造一个查询)试图弄清楚发生了什么变化而浪费的时间是可能更大,然后更新所有字段,更不用说增加的复杂性。如果您想要选择性更新,hibernate 已经支持它自己确定它(但如上所述选择性地使用)。更不用说您是否有更大的对象图(如地址、帐户等)。 他说得有道理!如果您只想更新一列,那么获取对象很浪费,对吧?不过,据我所知,使用 hibernate 时,您会求助于自定义查询。我最近刚刚看到一篇有趣的文章:blog.jooq.org/2017/03/08/… 尽管你可以反对过早的优化,但这绝对是正确的,最终可以为你节省很多钱来购买更大的数据库服务器。 为什么更新单个列或整个对象(或关系等)需要更大的数据库?您的数据量不会增加,因此不确定更大的数据库与更大的更新语句有何关系。通常,数据库(有时是您的持久性提供程序)足够聪明,可以进行各种优化,但在您搞砸选择性更新的那一刻就停止这样做。 【参考方案1】:

在 JPA 中,您传递实体,框架会计算出您更改了哪些属性。如果您还不知道这一点,我建议您多花一点时间阅读 JPA 的基础知识。从表面上看,JPA 看起来简单易懂,但如果您不了解它的构建原理,您最终将花费大量时间进行调试和谷歌搜索来破译错误消息。

当实体为managed 时,JPA 将在加载对象时获取对象的副本,然后将其与传递给save/persist 的版本进行比较,并确定应更新哪些列。这使得开发人员模型易于使用,您(通常)不必考虑对实体进行的更改。

您可能会认为,正如 Rob 所建议的那样,加载单个实体来更改单个属性是一种浪费,但情况并非总是如此。默认情况下,EclipseLink 和 Hibernate 都使用共享(二级)缓存,所以如果一个实体已经加载,它可能是从内存而不是 DB 加载的,一旦你修改了实体,JPA 会比较内存中的两个对象和生成修改列所需的 SQL。

有时您需要优化 JPA 代码(但前提是您已经测量了哪些操作很慢)。经典场景是批量删除/更新,您不希望 JPA 花费时间阅读/复制/管理您正在删除/更新的实体。在这些情况下,您可以使用删除或更新查询,而不是加载实体并调用 EntityManager.remove() 或修改事务中的实体。但是您应该知道,这会绕过 EntityListeners 和级联指令,因为这些指令仅对托管实体调用。

【讨论】:

以上是关于Spring Boot JPA CrudRepository 选择性更新 - 只有一些属性的主要内容,如果未能解决你的问题,请参考以下文章

Spring Boot(十五):spring boot+jpa+thymeleaf增删改查示例

[Spring Boot] Adding JPA and Spring Data JPA

spring-data-jpa 和 spring-boot-starter-data-jpa 的区别

spring boot jpa

Spring Boot 整合Spring Data JPA

Spring Boot中使用Spring Data JPA示例