許多開發者質疑 Java 與 ORM 框架(如 Hibernate)是否適合大規模批量資料操作。確實,若不當使用,Hibernate 的 Session 級快取(一級快取)會導致記憶體暴增,甚至拋出 OutOfMemoryError。但只要掌握正確技巧,完全能消除這類效能顧慮。本文以 Hibernate 為例,深入解析批量插入、更新、刪除的核心策略與程式碼實作。
flush() 與 clear()預設情況下,Hibernate 會將所有持久化物件保留在 Session 快取中,直到事務提交才同步至資料庫。若一次插入 10 萬筆資料,記憶體將無法負荷。解決之道有二:
hibernate.jdbc.batch_size=20(或 50、100,視資料列大小調整)。session.flush() 同步 SQL,隨即呼叫 session.clear() 清空快取,釋放記憶體。Session session = sessionFactory.openSession();
Transaction tx = session.beginTransaction();
for (int i = 0; i < 100000; i++) {
Customer customer = new Customer(/* 參數 */);
session.save(customer);
if (i % 20 == 0) { // 每 20 筆 flush & clear 一次
session.flush();
session.clear();
}
}
tx.commit();
session.close();
此模式確保記憶體佔用穩定,且利用 JDBC 批次降低資料庫往返次數,大幅提升吞吐量。
ScrollableResults 串流處理對於海量資料的更新或刪除,不可一次載入所有實體至記憶體。Hibernate 2.1.6+ 提供 scroll() 方法,以資料庫游標方式逐筆讀取、處理,配合 flush()/clear() 即可實現常數記憶體佔用。
Session session = sessionFactory.openSession();
Transaction tx = session.beginTransaction();
ScrollableResults customers = session.getNamedQuery("GetCustomers")
.scroll(ScrollMode.FORWARD_ONLY);
int count = 0;
while (customers.next()) {
Customer customer = (Customer) customers.get(0);
customer.updateStuff(...); // 業務邏輯更新
if (++count % 20 == 0) {
session.flush();
session.clear();
}
}
tx.commit();
session.close();
注意:若實體啟用二級快取,批量操作期間應暫時停用(<class-cache usage="read-only"/> 或程式碼控制),否則快取同步同樣會造成記憶體壓力。
Hibernate 批量處理的核心原則是:「小批次提交、及時清理快取、利用資料庫游標避免全量載入」。掌握這三點,ORM 完全能勝任企業級大批量資料作業,無需退回到裸 JDBC。