本文系統整理 Hibernate 核心開發技能,涵蓋 SessionFactory 單例工具類、基礎增刪改查、三大物件狀態轉換、一級緩存機制、HQL/QBC/原生 SQL 多維查詢技術,附完整可執行測試代碼。
public class H3Util {
private static final SessionFactory sessionFactory = buildSessionFactory();
private static SessionFactory buildSessionFactory() {
try {
// 載入 classpath:hibernate.cfg.xml
return new Configuration().configure().buildSessionFactory();
} catch (Throwable ex) {
throw new ExceptionInInitializerError(ex);
}
}
public static SessionFactory getSessionFactory() {
return sessionFactory;
}
}
設計要點:SessionFactory 線程安全、重量級,全應用僅需一個實例;Session 輕量、非線程安全,請求級別開啟關閉。
public class TestHibernate {
// 新增
@Test public void add() {
Session session = H3Util.getSessionFactory().openSession();
Transaction tx = session.beginTransaction();
for (int i = 0; i < 20; i++) {
session.save(new User(null, "test"+i, "123456", new Date(), 1, 1000f));
}
tx.commit(); session.close();
}
// 查詢單筆 (get: 立即載入 / load: 延遲載入)
@Test public void find() {
Session session = H3Util.getSessionFactory().openSession();
User user = (User) session.get(User.class, 1L); // 立即發 SQL
// User user = (User) session.load(User.class, 1L); // 不發 SQL,用到屬性時才發
session.close();
}
// 刪除
@Test public void del() {
Session session = H3Util.getSessionFactory().openSession();
Transaction tx = session.beginTransaction();
User user = (User) session.get(User.class, 2L);
session.delete(user);
tx.commit(); session.close();
}
// 修改 (持久態物件修改屬性 -> 提交事務自動同步)
@Test public void update() {
Session session = H3Util.getSessionFactory().openSession();
Transaction tx = session.beginTransaction();
User user = (User) session.get(User.class, 1L);
user.setPassword("111111"); // 無需呼叫 update()
tx.commit(); session.close();
}
// HQL 列表查詢
@Test public void findList() {
Session session = H3Util.getSessionFactory().openSession();
List<User> list = session.createQuery("from User").list();
session.close();
}
}
| 狀態 | 特徵 | 典型操作 |
|---|---|---|
| 瞬時態 (Transient) | new 出來、無 OID、不受 Session 管理 | session.save() → 持久態 |
| 持久態 (Persistent) | 有 OID、受 Session 管理、變更自動同步 DB | session.delete() → 遊離態 / session.evict() → 遊離態 |
| 遊離態 (Detached) | 有 OID、曾受管理、現不受 Session 管理 | session.update()/merge() → 持久態 / session.save() 拋異常 (OID 衝突) |
@Test public void statusDemo() {
Session session = sf.openSession(); Transaction tx = session.beginTransaction();
// TO: 瞬時態
User user = new User(null, "nnn", "123", new Date(), 0, 999f);
session.save(user); // TO → PO (賦予 OID)
tx.commit(); // 同步到 DB
// session.close(); // 若關閉 Session → DO (遊離態)
// PO: 持久態 (修改屬性自動髒檢查)
user.setUsername("wangwu"); // 無需 update()
// DO: 遊離態 (session.close() 後)
// session.update(user); // 重新關聯 → PO
// session.delete(user); // PO → TO (刪除後變瞬時態)
tx.commit(); session.close();
}
Session 內,生命週期與 Session 綁定。session.get()、session.load()、session.byId() 依 ID 查詢;HQL/QBC/SQL 查詢不走一級緩存 (每次發 SQL)。@Test public void testL1Cache() {
Session session = sf.openSession();
// 1. get 兩次同 ID → 只發 1 條 SQL
User u1 = (User) session.get(User.class, 1L);
User u2 = (User) session.get(User.class, 1L); // 命中緩存
assertSame(u1, u2); // 同一物件引用
// 2. HQL 查詢不走緩存
Query q1 = session.createQuery("from User where id=1");
Query q2 = session.createQuery("from User where id=1"); // 發 2 條 SQL
// 3. 修改持久態物件,再次 get 看到最新值 (無 SQL)
u1.setName("admin");
User u3 = (User) session.get(User.class, 1L);
assertEquals("admin", u3.getName());
session.close();
}
// 分頁
query.setFirstResult((pageNo-1)*pageSize).setMaxResults(pageSize);
// 命名/位置參數 (推薦命名參數)
query.setString("username", "%test%"); // like
query.setParameter("salary", 10000f); // 類型不確定時
// 投影查詢 (選特定欄位)
List<Object[]> rows = session.createQuery("select u.id, u.name from User u").list();
List<User> users = session.createQuery("select new User(id, name) from User where id=:id").setLong("id", 1).list();
// 聚合函數
Long count = (Long) session.createQuery("select count(*) from User").uniqueResult();
Double avg = (Double) session.createQuery("select avg(salary) from User").uniqueResult();
// 分組
List<Object[]> groups = session.createQuery("select sex, count(*) from User group by sex").list();
// 排序
List<User> sorted = session.createQuery("from User order by salary desc, id asc").list();
// 命名查詢 (映射文件中定義)
Query nq = session.getNamedQuery("findOrder");
Criteria c = session.createCriteria(User.class);
// 條件
c.add(Restrictions.eq("username", "李四"));
c.add(Restrictions.like("username", "%test%"));
c.add(Restrictions.between("salary", 3000, 8000));
c.add(Restrictions.isNull("birthday"));
// 投影 (選欄位)
ProjectionList pl = Projections.projectionList()
.add(Projections.property("username"))
.add(Projections.property("sex"));
c.setProjection(pl);
// 分組
c.setProjection(Projections.projectionList()
.add(Projections.groupProperty("sex"))
.add(Projections.rowCount()));
// 分頁 & 排序
c.setFirstResult(0).setMaxResults(10)
.addOrder(Order.asc("salary")).addOrder(Order.desc("id"));
// 1. 純標量查詢 (回傳 Object[])
SQLQuery sql = session.createSQLQuery("select * from t_user");
List<Object[]> rows = sql.list();
// 2. 自動映射實體 (addEntity)
sql.addEntity(User.class);
List<User> users = sql.list();
// 3. 別名映射 (多表連接時)
sql.addEntity("u", User.class); // select u.* from t_user u
get/load 同 ID 無額外 SQL。fetch="join" 或 HQL left join fetch。