在領域模型設計中,雙向關聯極為常見。例如:
Company 持有 Employee 集合(@OneToMany)Employee 持有 Company 參考(@ManyToOne)當使用 JAXB(或其他序列化引擎)序列化 Company 時,引擎會遞歸遍歷物件圖:
Company → 發現 employees 欄位Employee → 發現 company 欄位Company 實例 → 循環引用引擎無法自行判斷何時停止,導致無限遞歸或 StackOverflowError。
@Entity
public class Company {
@Id @Column(name = "id")
private String id;
@OneToMany(mappedBy = "company")
private List<Employee> employees;
// getters/setters
}
@Entity
public class Employee {
@Id @Column(name = "id")
private String id;
@ManyToOne
@JoinColumn(name = "company_id")
private Company company;
// getters/setters
}
JAXB 提供 com.sun.xml.bind.CycleRecoverable 介面,讓開發者在偵測到循環時返回替代物件,打破循環。
import com.sun.xml.bind.CycleRecoverable;
import javax.xml.bind.annotation.*;
import java.util.List;
@Entity
@XmlRootElement
public class Company implements CycleRecoverable {
@Id @Column(name = "id")
private String id;
@OneToMany(mappedBy = "company")
@XmlElementWrapper(name = "employees")
@XmlElement(name = "employee")
private List<Employee> employees;
// 關鍵:循環偵測回調
@Override
public Object onCycleDetected(Context context) {
Company stub = new Company();
stub.setId(this.id); // 僅保留識別資訊
return stub;
}
// getters/setters
}
@Entity
@XmlRootElement
public class Employee implements CycleRecoverable {
@Id @Column(name = "id")
private String id;
@ManyToOne
@JoinColumn(name = "company_id")
@XmlTransient // 或在循環處理中返回 stub
private Company company;
@Override
public Object onCycleDetected(Context context) {
Employee stub = new Employee();
stub.setId(this.id);
return stub;
}
// getters/setters
}
Company,記錄其身份標識。Employee,再遍歷回 Company。onCycleDetected(Context)。
<company>
<id>C001</id>
<employees>
<employee>
<id>E001</id>
<company> <!-- 這裡會是精簡 stub -->
<id>C001</id>
</company>
</employee>
</employees>
</company>
CycleRecoverable,因為運行時難以預測哪些關聯會形成循環。id),避免攜帶大量資料或再次觸發循環。@XmlTransient:直接忽略該欄位(簡單但資料遺失)。@XmlID / @XmlIDREF:用 ID 參考替代物件嵌套(標準 JAXB 方式,需配合 XmlAdapter)。@JsonManagedReference / @JsonBackReference 或 @JsonIdentityInfo 處理循環。onCycleDetected 會建立新物件,大量循環時可能增加 GC 壓力;可考慮快取 Stub 實例。循環引用是雙向關聯模型的必然產物。JAXB 的 CycleRecoverable 機制提供了標準化、可控的解決路徑:在循環發生時刻攔截,輸出精簡替代物件,保證序列化順利完成且不遺失關鍵識別資訊。掌握此技巧,能讓你在複雜領域模型中游刃有餘地處理 XML/JSON 序列化需求。