專案採 Spring + iBatis + MySQL,引入批次機制預期大幅提升寫入效能,但 2000 筆資料無論是否啟用 batch 皆耗時 50 秒。同樣程式碼在獨立 iBatis 測試中僅需 1 秒。差異在於:獨立測試直接使用 SqlMapClient,專案透過 Spring 注入 SqlMapClientFactoryBean 取得實例。
批次更新的前提是 JDBC 連線的 autoCommit = false。若為 true,每條 SQL 皆會立即提交,批次機制形同虛設。驗證程式碼:
System.out.println("當前事務狀態: " +
sqlMapClient.getDataSource().getConnection().getAutoCommit());
// 輸出:true
資料源使用 DBCP:
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
<property name="driverClassName" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost/test"/>
<property name="username" value="root"/>
<property name="password" value="root"/>
</bean>
BasicDataSource 的 defaultAutoCommit 預設為 true,導致 Spring 取得的連線皆為自動提交模式。
在資料源 Bean 定義中顯式關閉預設自動提交:
<property name="defaultAutoCommit" value="false"/>
完整配置:
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
<property name="driverClassName" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost/test"/>
<property name="username" value="root"/>
<property name="password" value="root"/>
<property name="defaultAutoCommit" value="false"/> <!-- 關鍵行 -->
</bean>
SqlMapClient 內部預設 autoCommit=false,故獨立使用無此問題。@Transactional 或 TransactionProxyFactoryBean,Spring 會在事務開始時自動設定 autoCommit=false,批次在事務邊界內自然生效。但若批次操作在無事務邊界的方法中執行,則必須靠資料源層面保證。defaultAutoCommit=false,由上層事務管理器(Spring/JTA)統一控制提交/回滾,避免混淆。重啟應用後,再次執行批次寫入,觀察耗時是否降至秒級;或啟用 MySQL 一般查詢日誌,確認批次 SQL 是以 BEGIN; ... COMMIT; 區塊發送。
此案例揭示了框架整合層面的隱性預設值差異,排查效能問題時,務必從資料源、事務管理、連線池配置三個維度交叉驗證。