为什么hibernate在执行saveAndFlush之前再次加载实体?

时间:2016-09-14 17:37:39

标签: java spring hibernate jpa spring-data

我使用Spring Data with Hibernate作为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在 UPDATE之前执行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]

有人可以解释第二次选择的情况吗?我无法看到它。

2 个答案:

答案 0 :(得分:1)

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

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

答案 1 :(得分:0)

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