java - Spring Boot JPA/JDBC 批处理 findById 有效,但 findOneByX 不起作用

标签 java spring spring-boot jdbc spring-jdbc

我正在使用 Spring Boot JPA,我通过确保以下几行位于我的 application.properties 中来启用批处理:

spring.jpa.properties.hibernate.jdbc.batch_size=1000
spring.jpa.properties.hibernate.order_inserts=true
spring.jpa.properties.hibernate.order_updates=true

我现在有一个循环,我在一个实体上执行 findById,然后像这样保存该实体:

var entity = dao.findById(id)
// Do some stuff
dao.save(entity) //This line is not really required but I am being explicit here

将以上内容放入循环中,我看到保存(更新)语句已批量发送到数据库。 我的问题是,如果我执行 findOneByX,其中 X 是实体的属性,则批处理不起作用(批量大小为 1),请求一次发送一个,即:

var entity = dao.findOneByX(x)
// Do some stuff
dao.save(entity)

为什么会发生这种情况?当我们仅findById时,JPA/JDBC是否仅配备批处理?

最佳答案

解决方案

引用How to implement batch update using Spring Data Jpa?

  1. 获取您想要更新到列表的实体列表
  2. 根据需要更新
  3. 调用 saveAll

PS:当您的列表大小很大时,请注意此解决方案的内存使用情况。


为什么 findByIdfindOneByX表现不同?

根据M. Deinum的建议, hibernate will auto flush你的改变

prior to executing a JPQL/HQL query that overlaps with the queued entity actions

因为 findByIdfindOneByX会执行查询,它们之间有什么不同?

首先,刷新的原因是确保 session 和数据库处于相同的状态,因此您可以从 session 缓存(如果可用)和数据库获得一致的结果。

调用findById时,hibernate将尝试从 session 缓存中获取它,如果实体不可用,则从数据库中获取它。而对于findOneByX ,我们总是需要从数据库中获取它,因为 X 不可能缓存实体。

那么我们可以考虑下面的例子:

@Entity
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
public class Student {
    @Id
    private Long id;
    private String name;
    private int age;
}

假设我们有

<表类=“s-表”> <标题> id 名称 年龄 <正文> 1 艾米 10
@Transactional
public void findByIdAndUpdate() {
    dao.save(new Student(2L, "Dennis", 14));
    // no need to flush as we can get from session
    for (int i = 0; i < 100; i++) {
        Student dennis = dao.findById(2L).orElseThrow();
        dennis.setAge(i);
        dao.save(dennis);
    }
}

将会导致

412041 nanoseconds spent executing 2 JDBC batches;

1 表示插入 1 1 表示更新。

  • Hibernate:我确信如果记录不在 session 中,可以从 session (无需刷新)或数据库中获取结果,所以让我们跳过刷新,因为它很慢!
@Transactional
public void findOneByNameAndUpdate() {
    Student amy = dao.findOneByName("Amy");
    // this affect later query
    amy.setName("Tammy");
    dao.save(amy);
    for (int i = 0; i < 100; i++) {
        // do you expect getting result here?
        Student tammy = dao.findOneByName("Tammy");
        // Hibernate not smart enough to notice this will not affect later result.
        tammy.setAge(i);
        dao.save(tammy);
    }
}

将会导致

13964088 nanoseconds spent executing 101 JDBC batches;

1 表示首次更新,100 表示循环更新。

  • Hibernate:嗯,我不确定存储的更新是否会影响结果,最好刷新更新,否则我会被开发人员指责。

关于java - Spring Boot JPA/JDBC 批处理 findById 有效,但 findOneByX 不起作用,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/72313315/

相关文章:

spring - 如何在 Spring Boot 中添加 thymeleaf 方言?

java - Elastic Search for Spring Boot 2.1.3的最新稳定版本

java - 在 Linux 中通过脚本启动 Java 程序

java - 插入长文本时 Hibernate 不会保留

java - Spring MVC : CharacterEncodingFilter; why only set response encoding by force?

java - SpringBootServletInitializer 和 @ConfigurationProperties 不适用于 .war 部署上的根属性

java - Spring 启动 : Handle Custom Exceptions using ControllerAdvice for REST

javascript - jQuery改变Div的颜色

java - 在哪里可以找到 String::lines 实现?

java - 将指针位置传递给 Java 中的对象