關聯映射是 Hibernate 核心能力。本文通過 多對一(多員工共用一地址)與 一對多(一員工多證書)兩大經典場景,完整演示實體設計、XML 映射、表結構、CRUD 測試代碼。
public class Employee {
private Integer id;
private String firstName, lastName;
private Integer salary;
private Address address; // 多對一關聯
// Getter/Setter
}
public class Address {
private Integer id;
private String street, city, state, zipcode;
// Getter/Setter
}
CREATE TABLE EMPLOYEE (
id INT PRIMARY KEY AUTO_INCREMENT,
first_name VARCHAR(20), last_name VARCHAR(20), salary INT,
address_id INT NOT NULL,
FOREIGN KEY (address_id) REFERENCES ADDRESS(id)
);
CREATE TABLE ADDRESS (
id INT PRIMARY KEY AUTO_INCREMENT,
street_name VARCHAR(40), city_name VARCHAR(40),
state_name VARCHAR(40), zipcode VARCHAR(10)
);
<class name="Employee" table="EMPLOYEE">
<id name="id" column="id"><generator class="native"/></id>
<property name="firstName" column="first_name"/>
<property name="lastName" column="last_name"/>
<property name="salary" column="salary"/>
<many-to-one name="address" column="address_id" class="Address" not-null="true"/>
</class>
<class name="Address" table="ADDRESS">
<id name="id" column="id"><generator class="native"/></id>
<property name="street" column="street_name"/>
<property name="city" column="city_name"/>
<property name="state" column="state_name"/>
<property name="zipcode" column="zipcode"/>
</class>
關鍵:<many-to-one> 定義關聯端,column 指定外鍵列,class 指定關聯實體,not-null="true" 保證外鍵非空。
// 保存地址(先保存被引用端)
Address addr = new Address("Kondapur", "Hyderabad", "AP", "532");
session.save(addr);
// 保存員工(關聯地址)
Employee e1 = new Employee("Manoj", "Kumar", 4000, addr);
Employee e2 = new Employee("Dilip", "Kumar", 3000, addr);
session.save(e1); session.save(e2);
// 查詢驗證
List<Employee> list = session.createQuery("from Employee").list();
for (Employee e : list) {
System.out.println(e.getFirstName() + " " + e.getAddress().getCity());
}
// 輸出:Manoj Hyderabad / Dilip Hyderabad
public class Employee {
private Integer id;
private String firstName, lastName;
private Integer salary;
private Set<Certificate> certificates = new HashSet<>(); // 一對多
// Getter/Setter
}
public class Certificate {
private Integer id;
private String name;
// Getter/Setter
}
CREATE TABLE CERTIFICATE (
id INT PRIMARY KEY AUTO_INCREMENT,
certificate_name VARCHAR(30),
employee_id INT,
FOREIGN KEY (employee_id) REFERENCES EMPLOYEE(id)
);
<class name="Employee" table="EMPLOYEE">
<id name="id" column="id"><generator class="native"/></id>
<set name="certificates" cascade="all">
<key column="employee_id"/>
<one-to-many class="Certificate"/>
</set>
<property name="firstName" column="first_name"/>
<property name="lastName" column="last_name"/>
<property name="salary" column="salary"/>
</class>
<class name="Certificate" table="CERTIFICATE">
<id name="id" column="id"><generator class="native"/></id>
<property name="name" column="certificate_name"/>
</class>
關鍵:<set> 保證唯一性,<key> 定義外鍵,<one-to-many> 聲明集合元素類型,cascade="all" 級聯保存/刪除。
// 保存員工及證書
Employee emp = new Employee("Manoj", "Kumar", 4000);
Set<Certificate> certs = new HashSet<>();
certs.add(new Certificate("MCA"));
certs.add(new Certificate("MBA"));
certs.add(new Certificate("PMP"));
emp.setCertificates(certs);
session.save(emp); // 級聯保存證書
// 查詢並遍歷
List<Employee> list = session.createQuery("from Employee").list();
for (Employee e : list) {
for (Certificate c : e.getCertificates()) {
System.out.println(c.getName());
}
}
// 輸出:MBA, PMP, MCA(Set 無序)
| 維度 | 多對一 | 一對多 |
|---|---|---|
| 關係擁有方 | 多方 | 一放(通過集合) |
| 映射元素 | <many-to-one> | <set>/<list>/<bag> + <one-to-many> |
| 外鍵位置 | 多方表 | 多方表 |
| 級聯保存 | 需先保存一放 | 一放保存自動級聯多方 |
| 集合類型 | 單值引用 | Set/List/Bag/Map |
@ManyToOne / <many-to-one> 反向引用,便於從多方導航。cascade 謹慎選擇:all 含刪除,刪除員工會連帶刪除證書;若證書獨立生命週期用 save-update。fetch="join" / HQL join fetch。@OneToMany/@ManyToOne),XML 僅用於歷史遷移。