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

Hibernate 延遲載入完整配置指南:一對一、一對多、多對一與自關聯

     編輯:Java 程式設計入門

核心原則

延遲載入 (Lazy Loading) 透過代理物件延遲關聯資料查詢,直到真正存取時才發送 SQL,避免不必要的資料庫往返與記憶體佔用。配置關鍵在 lazy 屬性與特定關聯類型的額外設定。

實體關係圖

範例領域:Team (班級) 1:N Student (學生) 1:1 Certificate (身份證)

ER圖

映射檔配置詳解

1. Team.hbm.xml (一端)


<hibernate-mapping>
    <class name="edu.dgut.ke.model.Team" table="TEAM" lazy="true">
        <id name="id" type="string">
            <column name="ID" length="32"/>
            <generator class="uuid.hex"/>
        </id>
        <property name="teamname" type="string">
            <column name="TEAMNAME" length="32" not-null="true" unique="true"/>
        </property>
        <!-- 一對多:Set 集合加 lazy="true" -->
        <set name="students" inverse="true" cascade="all" lazy="true">
            <key><column name="TEAMID" length="32" not-null="true"/></key>
            <one-to-many class="edu.dgut.ke.model.Student"/>
        </set>
    </class>
</hibernate-mapping>

2. Certificate.hbm.xml (一對一關聯擁有方)


<hibernate-mapping>
    <class name="edu.dgut.ke.model.Certificate" table="CERTIFICATE" lazy="true">
        <id name="id" type="string">
            <column name="ID" length="32"/>
            <generator class="uuid"/>
        </id>
        <property name="describe" type="string">
            <column name="DESCRIBE" length="50" not-null="true"/>
        </property>
        <!-- 一對一:class 標籤 lazy + one-to-one constrained="true" -->
        <one-to-one name="student"
            class="edu.dgut.ke.model.Student"
            constrained="true" />
    </class>
</hibernate-mapping>

3. Student.hbm.xml (多端與一對一被動端)


<hibernate-mapping>
    <class name="edu.dgut.ke.model.Student" table="STUDENT" lazy="true">
        <id name="id" type="string">
            <column name="ID" length="32"/>
            <generator class="uuid.hex"/>
        </id>
        <!-- 多對一:預設即延遲,無需額外配置 -->
        <many-to-one name="certificate"
            class="edu.dgut.ke.model.Certificate"
            unique="true" column="cardId" cascade="all"/>
        <many-to-one name="team"
            class="edu.dgut.ke.model.Team"
            column="TEAMID" not-null="true"/>
        <property name="studentname" type="string">
            <column name="STUDENTNAME" length="16" not-null="true" unique="true"/>
        </property>
    </class>
</hibernate-mapping>

關鍵配置差異總結表

關聯類型配置位置關鍵屬性備註
多對一 (many-to-one)class 標籤lazy="true" (預設)預設即代理,可設 lazy="false" 關閉
一對多 (set/list/bag)集合標籤lazy="true"預設 true,代理集合
多對多集合標籤lazy="true"同一對多
一對一 (one-to-one)class + one-to-oneclass lazy="true" + constrained="true"雙重配置缺一不可,否則退化為預設抓取 (join)

constrained="true" 深度解析

  • 告訴 Hibernate:該一對一關聯的外鍵同時是主鍵 (共享主鍵策略)
  • 唯有此設定,Hibernate 才能確信「對方一定存在」,從而安全建立代理
  • 若無此設定,Hibernate 必須立即執行 JOIN 查詢確認存在性,失去延遲優勢

常見陷阱與解決

1. LazyInitializationException:no session


// 錯誤:Session 關閉後存取延遲屬性
Session s = factory.openSession();
Team t = s.get(Team.class, id);
s.close();
Set stus = t.getStudents(); // 拋出異常

解決方案:

  • Open Session in View:Web 環境用 Filter/Interceptor 延長 Session 至視圖渲染結束
  • FETCH JOIN:HQL from Team t left join fetch t.students where t.id=:id
  • DTO 投影:查詢時直接選取所需欄位 select new dto.TeamDTO(t.id, t.name, s.id, s.name) ...
  • 初始化工具:Hibernate.initialize(t.getStudents()) 在 Session 開啟時預載

2. N+1 查詢問題

遍歷集合觸發 N 次額外查詢:


List teams = session.createQuery("from Team").list();
for (Team t : teams) {
    System.out.println(t.getStudents().size()); // 每次觸發 SELECT * FROM STUDENT WHERE TEAMID=?
}

解決:批次抓取 @BatchSize(size=10) 或 <set batch-size="10">,或改用 FETCH JOIN。

註解配置對照 (JPA/Hibernate 5+)


@Entity @Table(name="TEAM")
@Proxy(lazy = true) // 類別級代理
public class Team {
    @Id @GeneratedValue(generator = "uuid") @GenericGenerator(name="uuid", strategy="uuid2")
    @Column(name="ID", length=32) String id;

    @OneToMany(mappedBy = "team", cascade = CascadeType.ALL, fetch = FetchType.LAZY)
    @BatchSize(size = 20) // 批次抓取優化
    private Set students = new HashSet<>();
}

@Entity @Table(name="CERTIFICATE")
public class Certificate {
    @OneToOne(mappedBy = "certificate", fetch = FetchType.LAZY, optional = false)
    @MapsId // 共享主鍵 = constrained="true"
    private Student student;
}

效能調優建議

  1. 預設全域開啟延遲 (hibernate.default_batch_fetch_size)
  2. 根據業務場景選擇 FETCH JOIN / EntityGraph / DTO 精確載入
  3. 避免在循環中觸發延遲載入,使用批次抓取或預載
  4. 監控 SQL 數量,單請求控制在 20 條以內
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved