簡體   English   中英

Spring Boot JPA/JDBC 批處理 findById 有效,但 findOneByX 無效

[英]Spring Boot JPA/JDBC batching findById works but findOneByX not working

我正在使用 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

將上述內容放在一個循環中,我看到 save(update) 語句被批處理到數據庫。 我的問題是,如果我執行 findOneByX ,其中 X 是實體上的屬性,則批處理不起作用(批處理大小為 1),請求一次發送一個,即:

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

為什么會這樣? 當我們僅 findById 時,JPA/JDBC 是否僅配備批處理?

解決方案

請參閱如何使用 Spring Data Jpa 實現批量更新?

  1. 獲取要更新到列表的實體列表
  2. 根據需要更新
  3. 調用saveAll

PS:當您的列表很大時,請注意此解決方案的內存使用情況。


為什么findByIdfindOneByX的行為不同?

正如M. Deinum所建議的, 休眠將自動刷新您的更改

在執行與排隊實體操作重疊的 JPQL/HQL 查詢之前

既然findByIdfindOneByX都會執行查詢,那么它們之間有什么不同呢?

首先,刷新的原因是確保會話和數據庫處於相同狀態,因此您可以從會話緩存(如果可用)和數據庫中獲得一致的結果。

當調用findById時,hibernate 將嘗試從會話緩存中獲取它,如果實體不可用,則從數據庫中獲取它。 而對於findOneByX ,我們總是需要從數據庫中獲取它,因為不可能通過 X 緩存實體。

然后我們可以考慮下面的例子:

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

假設我們有

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 用於更新。

  • Hibernate:如果記錄不在會話中,我確信結果可以從會話(沒有刷新)或數據庫中獲取,所以讓我們跳過刷新,因為它很慢!
@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:嗯,我不確定存儲的更新是否會影響結果,最好刷新更新,否則我會被開發人員指責。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM