每次數據庫查詢涉及:連線建立、網絡往返、SQL 解析、執行計劃、結果集傳輸、對象映射。Hibernate 一級緩存(Session Cache)在 Session 範圍內緩存已加載的實體對象,同一 Session 內同一主鍵的重複查詢直接返回緩存對象,避免上述全鏈路開銷。
openSession() 建立,close() 銷毀。EntityName + ID,Value = 實體對象引用。session.get()/load()、延遲加載(關聯導航)、session.save()/update()/saveOrUpdate() 後的對象自動納入。Session session = sessionFactory.openSession();
User user1 = (User) session.load(User.class, 1);
User user2 = (User) session.load(User.class, 1);
System.out.println(user1 == user2); // true,同一實例
session.close();
第二次 load() 直接從 Session Map 取對象,未發 SQL。
User user1 = (User) session.load(User.class, 1);
session.evict(user1); // 從緩存移除
User user2 = (User) session.load(User.class, 1); // 重新查庫
System.out.println(user1 == user2); // false,不同實例
User user1 = (User) session.load(User.class, 1);
session.clear(); // 清空所有
User user2 = (User) session.load(User.class, 1);
System.out.println(user1 == user2); // false
boolean inCache = session.contains(user1);
Session s1 = sessionFactory.openSession();
User u1 = (User) s1.load(User.class, 1);
s1.close(); // 緩存銷毀
Session s2 = sessionFactory.openSession();
User u2 = (User) s2.load(User.class, 1);
s2.close();
System.out.println(u1 == u2); // false,不同 Session 無共享
批量插入 10 萬條數據時,所有實體留在 Session 緩存會撐爆堆內存。
Session session = sessionFactory.openSession();
Transaction tx = session.beginTransaction();
for (int i = 0; i < 100000; i++) {
Entity e = new Entity(...);
session.save(e);
if (i % 100 == 0) { // 每 100 條
session.flush(); // 強制執行 INSERT 批次
session.clear(); // 清空緩存,釋放實體引用
}
}
tx.commit();
session.close();
<property name="hibernate.jdbc.batch_size">50</property>
<property name="hibernate.order_inserts">true</property>
<property name="hibernate.order_updates">true</property>
<property name="hibernate.jdbc.batch_versioned_data">true</property>
batch_size:累積多少條發一次批次(MySQL 需連接參數 rewriteBatchedStatements=true)。order_inserts/updates:將相同 SQL 重排相鄰,提高批次命中率。注意:MySQL 官方驅動 5.1.34+ 支援
rewriteBatchedStatements=true將多條INSERT INTO t VALUES (?)重寫為INSERT INTO t VALUES (?),(?)...,性能提升 10 倍以上。Oracle/SQL Server 原生支援標準批次,無需此參數。
Session session = sessionFactory.openSession();
session.setDefaultReadOnly(true); // 實體進緩存時標記唯讀,脏檢查跳過
List users = session.createQuery("from User").list();
session.close();
唯讀模式下實體不納入脏檢查隊列,減少快照佔用與提交時比較開銷。
| 誤區 | 真相 |
|---|---|
| 一級緩存可跨 Session 共享 | 不可,二級緩存(SessionFactory 級)才跨 Session |
| clear() 會觸發數據庫同步 | 不會,僅清理內存 Map;未 flush 的變更會丟失 |
| evict() 後對象變更會自動持久化 | 不會,游離態對象需 update()/merge() 重新關聯 |
| 大批量查詢不分頁直接 list() | 會一次性載入所有對象到緩存 → OOM,必須用 ScrollableResults 或分頁 |
Hibernate 一級緩存是 Session 級別的主鍵實體緩存,自動透明生效,核心價值在於 消除同一事務內的重複查詢、支撐延遲加載、管理實體狀態變更。掌握 flush()/clear() 分批寫入模式、batch_size 批次優化、readOnly 唯讀模式,是處理大數據量、高併發場景的必備技能。記住:緩存是雙刃劍,用不好就是內存洩漏源頭。