Hibernate 方便但若直接用於大數據量 (萬筆以上) 批量增刪改查,極易出現 OOM、執行緒卡頓、SQL 暴增等問題。以下是實戰驗證的優化要點。
一級快取會累積所有持久化物件,導致記憶體飆升。批量處理時每累積一定筆數 (如 50~100) 呼叫一次:
session.flush(); session.clear(); // 清空一級快取,釋放記憶體
| 方法 | 特點 | 適用場景 |
|---|---|---|
| list() | 一次性載入所有物件到記憶體 | 小數據集 (<1000) |
| iterate() | 延遲載入,逐筆查詢 (N+1 問題嚴重) | 結果集大但重複率高、或僅需部分資料 |
| scroll() | 游標流式讀取,記憶體佔用極低 | 大數據量唯讀遍歷 (推薦) |
ScrollableResults results = query.scroll(ScrollMode.FORWARD_ONLY);
while (results.next()) {
Entity e = (Entity) results.get(0);
// 處理 e
}
results.close();
fetch="select" 易觸發 N+1 問題;在映射檔或 HQL 中按需指定 fetch="join" 或 left join fetch。lazy="true",勿隨意改為 false。在 hibernate.cfg.xml 或 application.properties 設定:
hibernate.jdbc.batch_size=50 hibernate.jdbc.fetch_size=50 hibernate.order_inserts=true hibernate.order_updates=true hibernate.generate_statistics=false
Ehcache/Redis)。query.setCacheable(true)) 需搭配二級快取,命中率高時極其有效。對於幾十萬筆以上的批次匯入、清洗,Hibernate 並非最佳選擇。建議:
session.doWork(connection -> { ... }) 直接操作 JDBC PreparedStatement.addBatch()。JdbcTemplate.batchUpdate() 處理。LOAD DATA INFILE, COPY)。大批量業務建議 手動控制 Session 與事務,避免單一事務過大導致鎖爭用與回滾成本高:
Session session = sessionFactory.openSession();
Transaction tx = null;
try {
tx = session.beginTransaction();
for (int i = 0; i < total; i++) {
session.save(entity);
if (i % 50 == 0) { session.flush(); session.clear(); }
}
tx.commit();
} catch (Exception e) {
if (tx != null) tx.rollback();
throw e;
} finally {
session.close();
}
hibernate.jdbc.batch_size=50 (或更大)flush() + clear()scroll() 替代 list()show_sql=true 觀察實際 SQL