มีคลาสเอนทิตี "A" คลาส A อาจมีลูกประเภทเดียวกัน "A" นอกจากนี้ "A" ควรถือเป็นผู้ปกครองหากเป็นเด็ก
เป็นไปได้หรือไม่ ถ้าเป็นเช่นนั้นฉันจะแมปความสัมพันธ์ในคลาสเอนทิตีได้อย่างไร ["A" มีคอลัมน์รหัส]
มีคลาสเอนทิตี "A" คลาส A อาจมีลูกประเภทเดียวกัน "A" นอกจากนี้ "A" ควรถือเป็นผู้ปกครองหากเป็นเด็ก
เป็นไปได้หรือไม่ ถ้าเป็นเช่นนั้นฉันจะแมปความสัมพันธ์ในคลาสเอนทิตีได้อย่างไร ["A" มีคอลัมน์รหัส]
คำตอบ:
ใช่เป็นไปได้ นี่เป็นกรณีพิเศษของมาตรฐานสองทิศทาง@ManyToOne
/ @OneToMany
ความสัมพันธ์ เป็นเรื่องพิเศษเนื่องจากเอนทิตีในแต่ละจุดสิ้นสุดของความสัมพันธ์นั้นเหมือนกัน กรณีทั่วไปเป็นรายละเอียดในมาตรา 2.10.2 ของข้อมูลจำเพาะ JPA 2.0
นี่คือตัวอย่างที่ใช้งานได้ อันดับแรกคลาสเอนทิตีA
:
@Entity
public class A implements Serializable {
@Id
@GeneratedValue(strategy=GenerationType.AUTO)
private Long id;
@ManyToOne
private A parent;
@OneToMany(mappedBy="parent")
private Collection<A> children;
// Getters, Setters, serialVersionUID, etc...
}
นี่เป็นmain()
วิธีการคร่าวๆที่ยังคงมีอยู่สามเอนทิตีดังกล่าว:
public static void main(String[] args) {
EntityManager em = ... // from EntityManagerFactory, injection, etc.
em.getTransaction().begin();
A parent = new A();
A son = new A();
A daughter = new A();
son.setParent(parent);
daughter.setParent(parent);
parent.setChildren(Arrays.asList(son, daughter));
em.persist(parent);
em.persist(son);
em.persist(daughter);
em.getTransaction().commit();
}
ในกรณีนี้อินสแตนซ์เอนทิตีทั้งสามต้องคงอยู่ก่อนที่จะคอมมิตธุรกรรม หากฉันไม่สามารถคงอยู่หนึ่งในเอนทิตีในกราฟของความสัมพันธ์แม่ลูกได้ข้อยกเว้นจะเกิดcommit()
ขึ้น ใน Eclipselink นี่คือไฟล์RollbackException
รายละเอียดของความไม่สอดคล้องกัน
ลักษณะการทำงานนี้สามารถกำหนดค่าได้โดยใช้cascade
แอตทริบิวต์A
ของ@OneToMany
และ@ManyToOne
คำอธิบายประกอบ ตัวอย่างเช่นหากฉันตั้งค่าcascade=CascadeType.ALL
คำอธิบายประกอบทั้งสองนั้นฉันสามารถคงอยู่ในเอนทิตีหนึ่งอย่างปลอดภัยและเพิกเฉยต่อรายการอื่น ๆ สมมติว่าฉันยังคงparent
ทำธุรกรรมของฉันอยู่ JPA การดำเนินลัดเลาะparent
's คุณสมบัติเพราะมันจะมีเครื่องหมายchildren
CascadeType.ALL
การใช้งาน JPA พบson
และที่daughter
นั่น จากนั้นเด็กทั้งสองยังคงอยู่ในนามของฉันแม้ว่าฉันจะไม่ได้ร้องขออย่างชัดเจนก็ตาม
อีกหนึ่งบันทึก เป็นความรับผิดชอบของโปรแกรมเมอร์เสมอในการอัปเดตความสัมพันธ์แบบสองทิศทางทั้งสองด้าน กล่าวอีกนัยหนึ่งคือเมื่อใดก็ตามที่ฉันเพิ่มเด็กให้กับผู้ปกครองบางคนฉันต้องอัปเดตคุณสมบัติหลักของเด็กตามนั้น การอัปเดตความสัมพันธ์แบบสองทิศทางเพียงด้านเดียวเป็นข้อผิดพลาดภายใต้ JPA อัปเดตความสัมพันธ์ทั้งสองฝ่ายเสมอ สิ่งนี้เขียนอย่างไม่น่าสงสัยในหน้า 42 ของข้อมูลจำเพาะ JPA 2.0:
โปรดทราบว่าเป็นแอปพลิเคชันที่รับผิดชอบในการรักษาความสอดคล้องกันของความสัมพันธ์รันไทม์ตัวอย่างเช่นเพื่อให้แน่ใจว่าด้าน "หนึ่ง" และด้าน "หลาย" ของความสัมพันธ์แบบสองทิศทางสอดคล้องกันเมื่อแอปพลิเคชันอัปเดตความสัมพันธ์ขณะรันไทม์ .
สำหรับฉันเคล็ดลับคือการใช้ความสัมพันธ์แบบกลุ่มต่อกลุ่ม สมมติว่าเอนทิตี A ของคุณเป็นแผนกที่สามารถมีหน่วยงานย่อยได้ จากนั้น (ข้ามรายละเอียดที่ไม่เกี่ยวข้อง):
@Entity
@Table(name = "DIVISION")
@EntityListeners( { HierarchyListener.class })
public class Division implements IHierarchyElement {
private Long id;
@Id
@Column(name = "DIV_ID")
public Long getId() {
return id;
}
...
private Division parent;
private List<Division> subDivisions = new ArrayList<Division>();
...
@ManyToOne
@JoinColumn(name = "DIV_PARENT_ID")
public Division getParent() {
return parent;
}
@ManyToMany
@JoinTable(name = "DIVISION", joinColumns = { @JoinColumn(name = "DIV_PARENT_ID") }, inverseJoinColumns = { @JoinColumn(name = "DIV_ID") })
public List<Division> getSubDivisions() {
return subDivisions;
}
...
}
เนื่องจากฉันมีตรรกะทางธุรกิจที่ครอบคลุมเกี่ยวกับโครงสร้างลำดับชั้นและ JPA (ตามโมเดลเชิงสัมพันธ์) อ่อนแอมากที่จะสนับสนุนฉันจึงแนะนำอินเทอร์เฟซIHierarchyElement
และเอนทิตีฟังHierarchyListener
:
public interface IHierarchyElement {
public String getNodeId();
public IHierarchyElement getParent();
public Short getLevel();
public void setLevel(Short level);
public IHierarchyElement getTop();
public void setTop(IHierarchyElement top);
public String getTreePath();
public void setTreePath(String theTreePath);
}
public class HierarchyListener {
@PrePersist
@PreUpdate
public void setHierarchyAttributes(IHierarchyElement entity) {
final IHierarchyElement parent = entity.getParent();
// set level
if (parent == null) {
entity.setLevel((short) 0);
} else {
if (parent.getLevel() == null) {
throw new PersistenceException("Parent entity must have level defined");
}
if (parent.getLevel() == Short.MAX_VALUE) {
throw new PersistenceException("Maximum number of hierarchy levels reached - please restrict use of parent/level relationship for "
+ entity.getClass());
}
entity.setLevel(Short.valueOf((short) (parent.getLevel().intValue() + 1)));
}
// set top
if (parent == null) {
entity.setTop(entity);
} else {
if (parent.getTop() == null) {
throw new PersistenceException("Parent entity must have top defined");
}
entity.setTop(parent.getTop());
}
// set tree path
try {
if (parent != null) {
String parentTreePath = StringUtils.isNotBlank(parent.getTreePath()) ? parent.getTreePath() : "";
entity.setTreePath(parentTreePath + parent.getNodeId() + ".");
} else {
entity.setTreePath(null);
}
} catch (UnsupportedOperationException uoe) {
LOGGER.warn(uoe);
}
}
}
Top
เป็นความสัมพันธ์ หน้า 93 ของข้อมูลจำเพาะ JPA 2.0, Entity Listeners และ Callback Methods: "โดยทั่วไปแล้วเมธอด Lifecycle ของแอปพลิเคชันแบบพกพาไม่ควรเรียกใช้ EntityManager หรือการดำเนินการ Query เข้าถึงอินสแตนซ์ของเอนทิตีอื่นหรือแก้ไขความสัมพันธ์" ขวา? แจ้งให้เราทราบหากฉันไม่อยู่