Varlık Sınıfı "A" var. A Sınıfının aynı "A" türünden çocukları olabilir. Ayrıca "A", eğer çocuksa ebeveyni tutmalıdır.
Mümkün mü? Öyleyse Entity sınıfındaki ilişkileri nasıl eşleştirmeliyim? ["A" bir kimlik sütununa sahiptir.]
Yanıtlar:
Evet, bu mümkün. Bu, standart çift yönlü @ManyToOne
/ @OneToMany
ilişkinin özel bir durumudur . Özeldir çünkü ilişkinin her iki ucundaki varlık aynıdır. Genel durum, JPA 2.0 spesifikasyonu Bölüm 2.10.2'de detaylandırılmıştır .
İşte çalışılmış bir örnek. İlk olarak, varlık sınıfı 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...
}
İşte main()
bu tür üç varlığı sürdüren kaba bir yöntem:
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();
}
Bu durumda, işlem tamamlamadan önce üç varlık eşgörünümünün tümü devam ettirilmelidir. Ebeveyn-çocuk ilişkileri grafiğindeki varlıklardan birini sürdürmezsem, bir istisna atılır commit()
. Eclipselink'te bu, RollbackException
tutarsızlığın detaylandırılmasıdır.
Bu davranış, 's ve ek açıklamalarındaki cascade
özellik aracılığıyla yapılandırılabilir . Örneğin , bu ek açıklamaların ikisini de ayarlarsam , varlıklardan birini güvenli bir şekilde sürdürür ve diğerlerini göz ardı edebilirim. İşlemimde ısrar ettiğimi söyle . JPA uygulama traversler 'ın onunla işaretlendiği için özellik . JPA uygulaması bulur ve orada. Açıkça talep etmemiş olsam da, her iki çocuğu da benim adıma sürdürüyor.A
@OneToMany
@ManyToOne
cascade=CascadeType.ALL
parent
parent
children
CascadeType.ALL
son
daughter
Bir not daha. Çift yönlü bir ilişkinin her iki tarafını da güncellemek her zaman programcının sorumluluğundadır. Diğer bir deyişle, ne zaman bir ebeveyne çocuk eklesem, çocuğun ebeveyn mülkünü buna göre güncellemeliyim. Çift yönlü bir ilişkinin yalnızca bir tarafının güncellenmesi, JPA altında bir hatadır. Daima ilişkinin her iki tarafını da güncelleyin. Bu, JPA 2.0 spesifikasyonunun 42. sayfasında açık bir şekilde yazılmıştır:
Çalışma zamanı ilişkilerinin tutarlılığını sürdürme sorumluluğunu üstlenen uygulamanın bu olduğunu unutmayın - örneğin, uygulama çalışma zamanında ilişkiyi güncellediğinde çift yönlü bir ilişkinin "bir" ve "çok" taraflarının birbiriyle tutarlı olmasını sağlamak için .
Benim için işin püf noktası çoka çok ilişkisini kullanmaktı. A varlığınızın alt bölümlere sahip olabilen bir bölüm olduğunu varsayalım. Sonra (alakasız ayrıntıları atlayarak):
@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;
}
...
}
Hiyerarşik yapı etrafında bazı kapsamlı iş mantığım olduğundan ve JPA (ilişkisel modele dayalı) onu desteklemek için çok zayıf olduğundan, arayüz IHierarchyElement
ve varlık dinleyicisini tanıttım 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
bir ilişki olduğu varsayılarak taşınabilir değildir . JPA 2.0 spesifikasyonu, Varlık Dinleyicileri ve Geri Çağırma Yöntemlerinin 93. Sayfası: "Genel olarak, taşınabilir bir uygulamanın yaşam döngüsü yöntemi EntityManager veya Sorgu işlemlerini çağırmamalı, diğer varlık örneklerine erişmemeli veya ilişkileri değiştirmemelidir". Sağ? İzin verirsem haberim olsun.