**對象持久化**三大方案對比:
| 方案 | 優點 | 缺點 |
|---|---|---|
| 序列化 | 簡單、跨網絡傳輸 | 全量讀寫、無法檢索、結構變更不兼容 |
| JDBC 手寫 SQL | 完全控制、性能極致 | 樣板代碼量大、對象關係映射手工維護、移植性差 |
| EJB Entity Bean | 標準化、容器管理事務/安全 | 重量級、性能差、配置複雜、依賴應用服務器 |
| Hibernate | 輕量級、全自動 ORM、HQL 面向對象查詢、緩存機制、移植性強、Spring 無縫整合 | 極複雜 SQL 需原生、學習曲線中等 |
Fruit.java (POJO + 規約)
package com.example.entity;
public class Fruit {
private Integer fid; // 包裝類型,支持 null 判斷瞬時態
private String name;
private String comments;
private int price;
// 無參構造器 (Hibernate 必須)
public Fruit() {}
// 業務構造器
public Fruit(String name, String comments, int price) {
this.name = name; this.comments = comments; this.price = price;
}
// Getter/Setter (包含 setFid)
public Integer getFid() { return fid; }
public void setFid(Integer fid) { this.fid = fid; }
public String getName() { return name; }
public void setName(String name) { this.name = name; }
public String getComments() { return comments; }
public void setComments(String comments) { this.comments = comments; }
public int getPrice() { return price; }
public void setPrice(int price) { this.price = price; }
}
Fruit.hbm.xml
T_HILO
HILO_ID
CREATE TABLE T_FRUIT (FID NUMBER(10) PRIMARY KEY, NAME VARCHAR2(20) NOT NULL, COMMENTS VARCHAR2(50), PRICE NUMBER(5) NOT NULL);
CREATE TABLE T_HILO (HILO_ID NUMBER(10));
INSERT INTO T_HILO VALUES (1);
hibernate.cfg.xml
org.hibernate.dialect.OracleDialect
oracle.jdbc.OracleDriver
jdbc:oracle:thin:@localhost:1521:ORCL
scott
tiger
true
true
update
package com.example.util;
import org.hibernate.SessionFactory;
import org.hibernate.cfg.Configuration;
public class HibernateUtil {
private static final SessionFactory sessionFactory = buildSessionFactory();
private static SessionFactory buildSessionFactory() {
try { return new Configuration().configure().buildSessionFactory(); }
catch (Throwable ex) { throw new ExceptionInInitializerError(ex); }
}
public static SessionFactory getSessionFactory() { return sessionFactory; }
public static void shutdown() { getSessionFactory().close(); }
}
package com.example.dao;
import com.example.entity.Fruit;
import com.example.util.HibernateUtil;
import org.hibernate.Session;
import org.hibernate.Transaction;
import java.util.List;
public class FruitDao {
public Integer save(Fruit fruit) {
Session s = HibernateUtil.getSessionFactory().openSession();
Transaction tx = null;
try {
tx = s.beginTransaction();
Integer id = (Integer) s.save(fruit);
tx.commit();
return id;
} catch (Exception e) { if (tx != null) tx.rollback(); throw e; }
finally { s.close(); }
}
public Fruit getById(Integer id) {
Session s = HibernateUtil.getSessionFactory().openSession();
try { return s.get(Fruit.class, id); }
finally { s.close(); }
}
public void update(Fruit fruit) {
Session s = HibernateUtil.getSessionFactory().openSession();
Transaction tx = null;
try {
tx = s.beginTransaction();
s.update(fruit); // 或 saveOrUpdate(fruit)
tx.commit();
} catch (Exception e) { if (tx != null) tx.rollback(); throw e; }
finally { s.close(); }
}
public void delete(Fruit fruit) {
Session s = HibernateUtil.getSessionFactory().openSession();
Transaction tx = null;
try {
tx = s.beginTransaction();
s.delete(fruit);
tx.commit();
} catch (Exception e) { if (tx != null) tx.rollback(); throw e; }
finally { s.close(); }
}
public List findAll() {
Session s = HibernateUtil.getSessionFactory().openSession();
try { return s.createQuery("from Fruit", Fruit.class).list(); }
finally { s.close(); }
}
}
@Test
public void stateTransitionDemo() {
Session s = HibernateUtil.getSessionFactory().openSession();
Transaction tx = s.beginTransaction();
// 1. Transient → Persistent
Fruit f = new Fruit("芒果", "甜", 20);
s.save(f); // f 變 Persistent,fid 生成
System.out.println("ID: " + f.getFid());
// 2. Persistent 狀態下修改 → 自動脏檢查 → 提交時 UPDATE
f.setPrice(22); // 無需調用 update()
// tx.commit() 時自動發送 UPDATE SQL
// 3. Persistent → Detached (close/evict/clear)
tx.commit(); s.close(); // f 變 Detached
// 4. Detached → Persistent (reattach)
Session s2 = HibernateUtil.getSessionFactory().openSession();
Transaction tx2 = s2.beginTransaction();
Fruit f2 = s2.get(Fruit.class, f.getFid()); // 加載新 Persistent 實例
// 或 s2.update(f); // 直接重新關聯 f (需確保 DB 記錄存在)
// 或 s2.saveOrUpdate(f); // 智能判斷:fid=null→save,否則→update
tx2.commit(); s2.close();
}
關鍵:`inverse="true"` 由關係維護端 (Fruit) 控制外鍵;`cascade` 定義級聯操作。
| 方式 | API | 適用場景 |
|---|---|---|
| HQL | s.createQuery("from Fruit where price > ?", Fruit.class).setParameter(0, 10).list() | 面向對象、支持多態、可攜帶參數、編譯期檢查 |
| Criteria (舊) | s.createCriteria(Fruit.class).add(Restrictions.gt("price", 10)).list() | 動態組裝條件、類型安全、已废弃 (Hibernate 5.2+) |
| JPA CriteriaBuilder | cb.createQuery(Fruit.class).where(cb.gt(root.get("price"), 10)) | 標準 JPA、類型安全、動態查詢首選 |
| Native SQL | s.createNativeQuery("SELECT * FROM T_FRUIT WHERE PRICE > ?", Fruit.class).setParameter(1, 10).list() | 複雜 SQL、存儲過程、特定數據庫函數 |
官方文檔:Hibernate 5.6 User Guide | 經典書籍:《Java Persistence with Hibernate》