| 註解 | 屬性 | 說明 |
|---|---|---|
@ManyToMany | targetEntity | 被關聯端實體類 |
| cascade | PERSIST, MERGE 等,對應 XML cascade | |
| fetch | LAZY(預設)或 EAGER | |
| mappedBy | 被關聯端屬性名(非擁有端必填) | |
@JoinTable | name | 關聯表名 |
| joinColumns | 擁有端外鍵欄位 | |
| inverseJoinColumns | 被關聯端外鍵欄位 | |
@Cache | usage | 緩存策略(READ_WRITE, NONSTRICT_READ_WRITE) |
@Entity
@Table(name = "THEATER")
@Cache(usage = CacheConcurrencyStrategy.READ_WRITE)
public class Theater implements Serializable {
@ManyToMany(
targetEntity = Audience.class,
cascade = {CascadeType.PERSIST, CascadeType.MERGE},
fetch = FetchType.LAZY
)
@JoinTable(
name = "THEATER_AUDIENCE",
joinColumns = @JoinColumn(name = "THEATER_ID"),
inverseJoinColumns = @JoinColumn(name = "AUDIENCE_ID")
)
@Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)
private List<Audience> audiences = new ArrayList<Audience>();
// getter/setter 略
}
@Entity
@Table(name = "AUDIENCE")
@Cache(usage = CacheConcurrencyStrategy.READ_WRITE)
public class Audience implements Serializable {
@ManyToMany(
cascade = {CascadeType.PERSIST, CascadeType.MERGE},
mappedBy = "audiences" // 綁定擁有端屬性名
)
private List<Theater> theaters = new ArrayList<Theater>();
// getter/setter 略
}
public void selectWatchers() {
Theater theater = findTheaterById("showMax");
theater.getAudiences().clear(); // 1. 清空舊關聯(關鍵步驟)
List<Audience> list = findAudiencesByMovie("transformers");
for (Audience a : list) {
a.getTheaters().add(theater); // 2. 被關聯端加入
theater.getAudiences().add(a); // 3. 擁有端加入
}
doSaveEntity(theater); // 4. 持久化擁有端
}
add 會導致關聯表殘留舊記錄,拋出唯一約束異常或產生髒數據。mappedBy 端不維護關聯表,save(theater) 即可級聯更新關聯表。NONSTRICT_READ_WRITE,實體緩存用 READ_WRITE。join fetch。Annotations 方式比 XML 更直觀、編譯期檢查更強。掌握「擁有端負責關聯表維護、被關聯端只需 mappedBy、雙向同步必清空再建」三原則,即可輕鬆駕馭多對多關聯。