【问题标题】:Why is hibernate loading an entity again before doing saveAndFlush?为什么在执行 saveAndFlush 之前休眠再次加载实体?
【发布时间】:2017-01-22 14:51:25
【问题描述】:

我正在使用带有 Hibernate 的 Spring Data 作为 JPA 实现。

在某些时候,我装载了一辆车,操纵它并保存它。使用 Spring JPA-Repository 的代码如下所示:

@Entity @DynamicUpdate @DynamicInsert
public class Car{ 
    @PostLoad
    void postLoad(){ log.debug("post load"); }

    @PrePersist @PreUpdate
    void preSave(){ log.debug("prePersist/preUpdate"); }

    /*..*/
}

@Repository
public interface CarRepository extends JpaRepository<Car, Integer>{}

@Controller
public CarController{
    public void businessLogic(){
        Car car = carRepo.findOne(1);  // SELECT ...
        log.debug("Car loaded");
        car.setColor("red");
        // ...
        carRepo.saveAndFlush(car);     // UPDATE car SET ... <-- !!!
    }
}

这在所有自动化测试和 99% 的生产环境中都能正常工作。带有事务日志和 SQL 的日志大部分时间是这样的:

SQL: select ... from Car ...
Car: post load
Car loaded
Getting transaction for [org.springframework.data.jpa.repository.support.SimpleJpaRepository.saveAndFlush]
Car: prePersist/preUpdate
SQL: update Car set ...
Completing transaction for [org.springframework.data.jpa.repository.support.SimpleJpaRepository.saveAndFlush]

只有少数情况下,hibernate 在更新之前执行 SELECT。

SQL: select ... from Car ...
Car: post load
Car loaded
Getting transaction for [org.springframework.data.jpa.repository.support.SimpleJpaRepository.saveAndFlush]
SQL: select ... from Car ...
Car: post load
Car: prePersist/preUpdate 
SQL: update Car set ...
Completing transaction for [org.springframework.data.jpa.repository.support.SimpleJpaRepository.saveAndFlush]

有人可以解释在什么情况下进行第二次选择吗?我看不到。

【问题讨论】:

  • 不知道你的整个设置,但如果 Car 有“子对象”,那么 Hibernate 可能会选择检查这些子对象是否已经在数据库中,因此它不必保存它们也是(因为他们已经在那里)。
  • 代码应该在服务中的其他东西,这将是事务性的,而不是在你的控制器中。现在您有 2 个(隐式)事务,而一切都应该在一个事务中。接下来你应该(需要)做一个saveAndFlush,只有一个save就足够了。

标签: java spring hibernate jpa spring-data


【解决方案1】:

那是 Hibernate 做脏检查。它会重新加载实体以将其与您保存的任何更改进行比较。

有几种方法可以减少其对性能的影响,例如使用版本控制:Java - JPA - @Version annotation 或修改字节码以提高脏检查的效率。

【讨论】:

  • 这也是我所期望的。但大多数时候它不会从数据库重新加载。当 Hibernate 没有足够的信息来执行这个请求时,是否有可能以某种方式识别这些情况??
  • 你的意思是编程吗?我想该机制记录在某处(如果没有其他地方,则在源代码中),但我不会开始围绕它编写自己的功能。
  • 链接失效
  • @sloth 谢谢,已修复。
【解决方案2】:

每次在同一个事务中执行一些保存/删除操作后,当您有一个 SELECT 语句时,Hibernate 都会进行脏检查。进行脏检查以清除 1 级/2 级缓存。粗略地说,Hibernate 通过 HashMap 查找,其中包含缓存表的名称,并与您的请求中使用的表进行比较。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-08-09
    • 2015-11-24
    • 2013-03-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多