首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 程式設計入門

Hibernate 入門核心教程:從持久化原理到實戰 CRUD 與關聯映射

     編輯:Java 程式設計入門

為什麼需要 Hibernate?

**對象持久化**三大方案對比:

方案優點缺點
序列化簡單、跨網絡傳輸全量讀寫、無法檢索、結構變更不兼容
JDBC 手寫 SQL完全控制、性能極致樣板代碼量大、對象關係映射手工維護、移植性差
EJB Entity Bean標準化、容器管理事務/安全重量級、性能差、配置複雜、依賴應用服務器
Hibernate輕量級、全自動 ORM、HQL 面向對象查詢、緩存機制、移植性強、Spring 無縫整合極複雜 SQL 需原生、學習曲線中等

核心概念速記

  • ORM:Object-Relational Mapping,對象屬性 ↔ 數據庫欄位、類 ↔ 表、繼承/關聯/集合映射策略
  • SessionFactory:重量級、線程安全、整個應用單例、緩存配置、建立昂貴
  • Session:輕量級、**非線程安全**、請求級/事務級、一級緩存、CRUD 入口
  • 三種對象狀態:
    • Transient (瞬時):new 出來,無 ID,無關聯 Session
    • Persistent (持久):有 ID,關聯 Session,變更自動同步 DB (脏檢查)
    • Detached (游離):有 ID,Session 關閉/清除/evict,可重新 attach

第一個實體類與映射文件

實體類 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
            
        
        
        
        
    

數據庫準備 (Oracle 示例)


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 核心配置 hibernate.cfg.xml





    
        org.hibernate.dialect.OracleDialect
        oracle.jdbc.OracleDriver
        jdbc:oracle:thin:@localhost:1521:ORCL
        scott
        tiger
        true
        true
        update
        
    

工具類:單例 SessionFactory


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(); }
}

DAO 模式封裝 CRUD


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適用場景
HQLs.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 CriteriaBuildercb.createQuery(Fruit.class).where(cb.gt(root.get("price"), 10))標準 JPA、類型安全、動態查詢首選
Native SQLs.createNativeQuery("SELECT * FROM T_FRUIT WHERE PRICE > ?", Fruit.class).setParameter(1, 10).list()複雜 SQL、存儲過程、特定數據庫函數

緩存與性能優化要點

  1. 一級緩存:Session 級,不可禁用,同一 Session 內 `get/load` 同 ID 只查一次 DB
  2. 二級緩存:SessionFactory 級,需配置 Ehcache/Infinispan/Redis,適用於**讀多寫少、非關鍵數據** (字典表、配置表)
  3. 查詢緩存:配合二級緩存,緩存 HQL 結果集
  4. 批量操作:`hibernate.jdbc.batch_size` (如 20),`session.flush()`/`clear()` 定期清理內存
  5. 延遲加載:關聯默認 `lazy="true"`,注意 `LazyInitializationException` (Open Session In View 模式或 `fetch="join"`)

常見坑與最佳實踐

  • ❌ **Session 當字段成員變量** → 線程不安全、內存洩漏 → ✅ 請求級 ThreadLocal 或 Spring 管理
  • ❌ **忘記關閉 Session** → 連接洩漏 → ✅ `try-finally` 或 Spring `@Transactional`
  • ❌ **大量數據用 `list()` 一次性加載** → OOM → ✅ `ScrollableResults` / 分頁 / 流式處理
  • ❌ **`load()` 不存在拋異常 vs `get()` 返回 null** → 明確業務語義選擇
  • ❌ **`saveOrUpdate()` 濫用** → 游離對象無 ID 變 save、有 ID 變 update,可能誤插入 → ✅ 明確業務流程用 `save`/`update`/`merge`
  • ✅ **ID 生成策略選擇**:`native` (跨庫)、`identity` (MySQL/SQL Server)、`sequence` (Oracle/PostgreSQL)、`hilo` (高併發預分配)、`uuid` (分布式)

總結學習路徑

  1. 掌握 **核心 API** (Configuration, SessionFactory, Session, Transaction, Query)
  2. 熟練 **映射文件/注解** (id, property, 關聯, 繼承, 組件, 集合)
  3. 理解 **三態轉換** 與 **脏檢查機制**
  4. 實戰 **HQL/JPQL/Criteria** 查詢與**分頁優化**
  5. 配置 **二級緩存** 與 **批量處理**
  6. 整合 **Spring** (`LocalSessionFactoryBean`, `HibernateTransactionManager`, `@Transactional`)
  7. 進階:**攔截器/事件監聽器、多租戶、Envers 審計、Spatial GIS**

官方文檔:Hibernate 5.6 User Guide | 經典書籍:《Java Persistence with Hibernate》

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved