簡體   English   中英

Hibernate 批量更新開啟但仍然單獨執行每個查詢

[英]Hibernate batch update is turned on but still executes eache query seperatly

我正在嘗試向我的 spring 引導項目添加批量更新。 該批次似乎已激活,但當我查看 hibernate 日志時,仍然有多個查詢。

hibernate.jdbc.batch_size=5

hibernate 統計

295647400 nanoseconds spent acquiring 1 JDBC connections;
0 nanoseconds spent releasing 0 JDBC connections;
3693300 nanoseconds spent preparing 21 JDBC statements;
5752515000 nanoseconds spent executing 20 JDBC statements;
1275544900 nanoseconds spent executing 4 JDBC batches;
0 nanoseconds spent performing 0 L2C puts;
0 nanoseconds spent performing 0 L2C hits;
0 nanoseconds spent performing 0 L2C misses;
1287992700 nanoseconds spent executing 1 flushes (flushing a total of 19 entities and 0 collections);
735000 nanoseconds spent executing 2 partial-flushes (flushing a total of 1 entities and 1 collections)

hibernate 日志

2022-10-09 19:21:16,192 DEBUG org.hibernate.engine.jdbc.spi.SqlStatementLogger: insert into SYSTEM.SNAPSHOT (CREATED_ON, ENTITY_PROPER_NAME, ID) values (?, ?, ?)
2022-10-09 19:21:16,192 DEBUG org.hibernate.engine.jdbc.batch.internal.AbstractBatchImpl: Reusing batch statement
2022-10-09 19:21:16,192 DEBUG org.hibernate.engine.jdbc.spi.SqlStatementLogger: insert into SYSTEM.SNAPSHOT (CREATED_ON, ENTITY_PROPER_NAME, ID) values (?, ?, ?)
2022-10-09 19:21:16,192 DEBUG org.hibernate.engine.jdbc.batch.internal.AbstractBatchImpl: Reusing batch statement
2022-10-09 19:21:16,192 DEBUG org.hibernate.engine.jdbc.spi.SqlStatementLogger: insert into SYSTEM.SNAPSHOT (CREATED_ON, ENTITY_PROPER_NAME, ID) values (?, ?, ?)
2022-10-09 19:21:16,193 DEBUG org.hibernate.engine.jdbc.batch.internal.AbstractBatchImpl: Reusing batch statement
2022-10-09 19:21:16,193 DEBUG org.hibernate.engine.jdbc.spi.SqlStatementLogger: insert into SYSTEM.SNAPSHOT (CREATED_ON, ENTITY_PROPER_NAME, ID) values (?, ?, ?)
2022-10-09 19:21:16,193 DEBUG org.hibernate.engine.jdbc.batch.internal.AbstractBatchImpl: Reusing batch statement
2022-10-09 19:21:16,193 DEBUG org.hibernate.engine.jdbc.spi.SqlStatementLogger: insert into SYSTEM.SNAPSHOT (CREATED_ON, ENTITY_PROPER_NAME, ID) values (?, ?, ?)
2022-10-09 19:21:16,193 DEBUG org.jboss.logging.DelegatingBasicLogger: Executing batch size: 5

我期待 hibernate 為 Oracle 數據庫生成如下語句。

INSERT ALL 
    INTO Snapshot ( created_on, entity_proper_name, id ) VALUES ( ?, ?, ? )
    INTO Snapshot ( created_on, entity_proper_name, id ) VALUES ( ?, ?, ? )
    INTO Snapshot ( created_on, entity_proper_name, id ) VALUES ( ?, ?, ? )
SELECT 1 FROM dual;

當我將執行的語句檢入 Jprofiler 或直接檢入 Oracle 時,它與 hibernate 日志中顯示的相同。 執行計數也與單獨運行插入時應有的相同。

在此處輸入圖像描述

Hibernate 批處理是否適用於 Oracle 數據庫?


代碼片段

Spring Boot v2.7.1 
Spring v5.3.21
Java 17.0.3.1
Oracle Database 12c Enterprise Edition Release 12.1.0.2.0 - 64bit Production

應用.yml

spring:
  datasource:
    url: jdbc:oracle:thin:@localhost:1521/db
    username: user
    password: password
    driver-class-name: oracle.jdbc.OracleDriver
  jpa:
    database-platform: org.hibernate.dialect.Oracle12cDialect
    hibernate:
      naming:
        physical-strategy: org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl
      ddl-auto: update
    show-sql: true
    properties:
      hibernate:
        dialect: org.hibernate.dialect.Oracle12cDialect
        format_sql: false
        jdbc:
          fetch_size: 100
          batch_size: 5
        order_updates: true
        order_inserts: true
        batch_versioned_data: true
        generate_statistics: true

快照實體

@Entity
@Table(name = "SNAPSHOT", schema = "SYSTEM", catalog = "")
public class Snapshot {
    @GeneratedValue(strategy = GenerationType.SEQUENCE)
    @Id
    @Column(name = "ID")
    private long id;
    @Basic
    @Column(name = "CREATED_ON")
    private String createdOn;
    ...
}

快照服務

@Transactional
    public void execute() {
    ...
    for (int i = 0; i < snapshots.size(); i++) {  
        snapshots.get(i).setFieldValue(fieldValue);
        snapshots.get(i).setCreatedOn(createdOn);
    }

    snapshotRepository.saveAll(snapshots);
       ...
}

pom.xml

<dependency>
    <groupId>com.oracle.database.jdbc</groupId>
    <artifactId>ojdbc11</artifactId>
    <version>21.7.0.0</version>
</dependency>

我不希望 hibernate 使用特定於供應商的特殊多插入語法。 它而是使用所謂的(有時)數組操作,它發送帶有值(行)數組的參數化查詢,oracle jdbc 驅動程序 確實支持 這是一篇隨機的 oracle 文章提到了這些東西,但是所有主要的數據庫都有這種可能性,這里有一些使用它的示例代碼(沒有休眠)jdbc

這也適用於您所有的日志。 Jdbc 表示它運行了 4 個批次,用於 20 個插入,每批次有 5 行。 任何加速都來自減少的網絡往返次數,以及數據庫中每次批量更新僅執行一次查找或解析操作。 Oracle 的 sql 引擎仍然每行執行 1 次插入,並具有所有相關的盛況和環境,如約束檢查和索引維護。 我知道解決這個問題的唯一方法是具有直接路徑模式的 oracle 加載程序。

但是要驗證 oracle 實際上使用了批處理語句是一個問題,因為它具有數據傳輸功能。 sql 引擎將再次運行單獨的語句。

對於相同的數據,速度至少會顯着提高 50%,盡管這可能取決於插入的數據類型。 與非批處理操作相比, 這個評估說的是 500%,但這並不是一個很好的證明。 您可以在插入時為定義的行數引發數據庫錯誤, 然后檢查引發的異常(應該是一些BatchUpdateException )和處理的實際行數。 這可能足以證明批處理發生在客戶端。 jdbc 代碼中的斷點也可以,我猜OraclePreparedStatement.sendBatch()是一個候選者。 也許您可以調試 oracle 可執行文件。 您必須使用不使用瘦客戶端的連接,就像您所做的那樣( jdbc:oracle:thin... ),但是 OCI 客戶端(oracle 調用接口), 您正在搜索的調用OCIBindArrayOfStruct . 對於數據庫服務器端,老實說我不知道。 也許可以嗅出 IP 流量以確定每 n 行的請求數。

就我而言,我將相同數據的加速作為充分的證據,即發生了批處理。 即使沒有,我所需要的只是加速。

我相信這是因為 Hibernate 提供的分配大小

在您的實體 class 中,您可以嘗試更改 allocationSize 嗎?

@Id
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "SNAPSHOT_SEQ")
@SequenceGenerator(name = "SNAPSHOT_SEQ", sequenceName = "SNAPSHOT_SEQ", allocationSize = 150)
@Column(name = "ID", nullable = false)
private Long id;

暫無
暫無

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

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