我正在使用 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?
- 获取您想要更新到列表的实体列表
- 根据需要更新
- 调用
saveAll
PS:当您的列表大小很大时,请注意此解决方案的内存使用情况。
为什么 findById
和findOneByX
表现不同?
根据M. Deinum的建议, hibernate will auto flush你的改变
prior to executing a JPQL/HQL query that overlaps with the queued entity actions
因为 findById
和findOneByX
会执行查询,它们之间有什么不同?
首先,刷新的原因是确保 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;
}
假设我们有
@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/