我有一个名为“Domain”的课程。每个域可以有多个子域(相同类型)。
我需要能够确定子域和根域。子域可以自己拥有子域。这可能会有很多层次。
示例:
Rootdomain
|- Subdomain 1
| |- Subdomain 2
| |
| |- Subdomain 3
|
|- Subdomain 4
| |- Subdomain 5
如何使用Hibernate注释建模这样的Java类?
答案 0 :(得分:17)
建模非常简单:
@Entity
class Domain {
@ManyToOne //add column definitions as needed
private Domain parent; //each Domain with parent==null is a root domain, all others are subdomains
@OneToMany //add column definitions as needed
private List<Domain> subdomains;
}
请注意,parent
是负责数据库条目的属性,即您需要为要存储的关系的子域设置parent
。
查询不是很简单,因为SQL(以及HQL和JPQL)不容易支持树查询。 Hibernate可以通过懒惰地加载下一级别来实现这一点,但是如果你想在一个查询中加载一堆级别,那就是它变得很难。
答案 1 :(得分:10)
如果你想使用Hibernate / JPA延迟初始化(这是正常情况),那么你应该不使用复合模式。
与Composite Pattern相关的Hibernate问题是:在Composite中,你有一个Composite,它有一个引用它的子组件。 但Component只是一个抽象类或接口,因此每个Component都是Leaf或Composite。 如果你现在对复合的cild集使用延迟初始化,但是有些人需要将具体的子项转换为Leaf或Composite,你将得到一个Cast Exception,因为hibernate使用代理来处理无法转换为Leaf或复合物。
复合模式的第二个缺点是,每个类在其整个生命周期中都将是Leaf或Composite。 如果您的结构永远不会改变,这很好。但是如果Leaf必须成为Composite,那么它将无法工作,因为有人想添加一个子节点/叶子。
因此,如果你有一些动态结构,我推荐一个类节点,它在父节点和子节点之间具有双向关系。 如果您经常需要导航到代码中的父项或子项,则该关系应该是双向的。 保持这种关系有点棘手,所以我决定发布一些代码。
@Entity
public class Domain {
@Id
private long id;
/** The parent domain, can be null if this is the root domain. */
@ManyToOne
private Domain parent;
/**
* The children domain of this domain.
*
* This is the inverse side of the parent relation.
*
* <strong>It is the children responsibility to manage there parents children set!</strong>
*/
@NotNull
@OneToMany(mappedBy = "parent")
private Set<Domain> children = new HashSet<Domain>();
/**
* Do not use this Constructor!
* Used only by Hibernate.
*/
Domain() {
}
/**
* Instantiates a new domain.
* The domain will be of the same state like the parent domain.
*
* @param parent the parent domain
* @see Domain#createRoot()
*/
public Domain(final Domain parent) {
if(parent==null) throw new IllegalArgumentException("parent required");
this.parent = parent;
registerInParentsChilds();
}
/** Register this domain in the child list of its parent. */
private void registerInParentsChilds() {
this.parent.children.add(this);
}
/**
* Return the <strong>unmodifiable</strong> children of this domain.
*
* @return the child nodes.
*/
public Set<Domain> getChildren() {
return Collections.unmodifiableSet(this.children);
}
/**
* Move this domain to an new parent domain.
*
* @param newParent the new parent
*/
public void move(final Domain newParent) {
Check.notNullArgument(newParent, "newParent");
if (!isProperMoveTarget(newParent) /* detect circles... */ ) {
throw new IllegalArgumentException("move", "not a proper new parent", this);
}
this.parent.children.remove(this);
this.parent = newParent;
registerInParentsChilds();
}
/**
* Creates the root.
*
* @param bid the bid
* @return the domain
*/
public static Domain createRoot() {
return new Domain();
}
}
答案 2 :(得分:2)
我建议您首先获得仅限Java的OO模型,然后担心您使用哪种Hibernate注释。如果您发现需要合法地更改您的Hibernate模型以适应,您可以随时这样做。
最后,这类问题通常是通过Composite pattern的一些变化来解决的(不要过分关注模式事物,只关注它背后的结构和想法。)
使用关系数据库术语(以相当放松的方式):
一个。如果您的域(根域和子域)是 relationship (表中没有重复项且具有可识别主键的n元组集合)和
B中。您的域和子域具有相似的结构,然后
℃。您可以通过定义“parent”外键将所有这些存储在同一物理表中,以便一个元组的父FK映射到另一个元组的主键。
最重要的是,这种递归关系必须是非循环的。结构上如何解决问题取决于您的问题域(您是否有一个根域,或者您可以拥有多个不相关的根域?)根域可以通过具有NULL父外键或条件来表示其中根域元组的父外键等于其自己的主键。任何一方都有利弊(这是典型的愚蠢火焰战争的主题。)
答案 3 :(得分:1)
根据您需要进行的操作,有几种可能性。
最直接的方法是简单地建立父子一对多关联。根据您的需要,选择合适的类型:单向一对多,单向多对一或双向。
使树的所有节点与根节点具有多对一关系通常很有用。这允许在单个查询中非常容易地加载整个树。