承接上篇「多對多單向關聯」,本文解析七大關聯映射的最後一種:多對多雙向關聯。重點對比單向與雙向的異同,掌握配置關鍵。
public class Role {
private int id;
private String name;
private Set users; // 雙向:Role 端持有 User 集合
// getter/setter
}
public class User {
private int id;
private String name;
private Set roles; // 雙向:User 端持有 Role 集合
// getter/setter
}
兩端皆持有集合,形成雙向導航:role.getUsers()、user.getRoles()。
中間表 t_user_role 維護關聯:
| 表 | 欄位 |
|---|---|
| t_user | id (PK), name |
| t_role | id (PK), name |
| t_user_role | user_id (FK), role_id (FK), PK(user_id, role_id) |
結論:雙向關聯不改變資料庫結構,僅改變物件導航能力。
<class name="com.zs.hibernate.Role" table="t_role">
<id name="id"><generator class="native"/></id>
<property name="name"/>
<set name="users" table="t_user_role">
<key column="role_id" not-null="true"/>
<many-to-many class="com.zs.hibernate.User" column="user_id"/>
</set>
</class>
<class name="com.zs.hibernate.User" table="t_user">
<id name="id"><generator class="native"/></id>
<property name="name"/>
<set name="roles" table="t_user_role">
<key column="user_id" not-null="true"/>
<many-to-many class="com.zs.hibernate.Role" column="role_id"/>
</set>
</class>
| 維度 | 單向 | 雙向 |
|---|---|---|
| 導航 | User → Role (單向) | User ↔ Role (雙向) |
| Java 類別 | 單邊有 Set | 雙邊皆有 Set |
| 映射配置 | 單邊 <set> + many-to-many | 雙邊對稱配置 |
| 資料庫 | 相同中間表 | 相同中間表 |
| 關係對等性 | A→B ≠ B→A | A↔B 等價 |
user.getRoles().add(role);
role.getUsers().add(user); // 必須雙邊加入,否則記憶體不一致
toString()、JSON 序列化、equals/hashCode 需排除集合或使用 @JsonIgnore、@ToString.Excludecascade="all",刪除一方會誤刪另一方及關聯記錄。改由 Service 層顯式管理關聯。inverse(僅一對多雙向才需)。兩端皆為關聯擁有者,Hibernate 會對中間表執行兩次 INSERT/DELETE,可接受。@Entity @Table(name="t_user")
public class User {
@ManyToMany
@JoinTable(name="t_user_role",
joinColumns=@JoinColumn(name="user_id"),
inverseJoinColumns=@JoinColumn(name="role_id"))
private Set roles = new HashSet<>();
}
@Entity @Table(name="t_role")
public class Role {
@ManyToMany(mappedBy="roles") // mappedBy 指向擁有端
private Set users = new HashSet<>();
}
註解中 僅一端維護關聯 (mappedBy),另一端為被動端,避免重複維護中間表。
七大關聯映射已全部講解完畢:
下一階段將深入:繼承映射、元件映射、查詢語言 (HQL/Criteria)、快取策略、效能調優、鎖機制等進階主題。