如何使用Hibernate对树进行建模?
我有一个名为“域”的类。每个域可以有多个子域(相同类型)。
我需要能够确定子域和根域。子域本身可以有子域。这可以是相当高的层次。
例:
Rootdomain
|- Subdomain 1
| |- Subdomain 2
| |
| |- Subdomain 3
|
|- Subdomain 4
| |- Subdomain 5
如何使用 Hibernate 注释对这样的 Java 类进行建模?
我有一个名为“域”的类。每个域可以有多个子域(相同类型)。
我需要能够确定子域和根域。子域本身可以有子域。这可以是相当高的层次。
例:
Rootdomain
|- Subdomain 1
| |- Subdomain 2
| |
| |- Subdomain 3
|
|- Subdomain 4
| |- Subdomain 5
如何使用 Hibernate 注释对这样的 Java 类进行建模?
建模将非常简单:
@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可以通过懒惰地加载下一个级别来做到这一点,但是如果你想在一个查询中加载一堆级别,那就是它变得困难的地方。
如果要使用Hibernate/JPA惰性初始化(这是正常情况),则不应使用复合模式。
与复合模式相关的休眠问题是:在复合中,您有一个复合,它具有对其子组件的引用。但组件只是一个抽象类或接口,因此每个组件都是叶或复合。如果您现在对复合的 cild 集使用延迟初始化,但随后需要如何将混凝土子项转换为叶或复合,则将得到一个“转换异常”,因为休眠对不能转换为“叶”或“复合”的组件使用代理。
复合模式的第二个缺点是,每个类在其整个生存期内都是 Leaf 或复合。如果你的结构永远不会改变,那很好。但是,如果叶必须成为复合节点,因为有人想要添加子节点/叶,它将不起作用。
因此,如果您有一些动态结构,我建议您使用一个在父节点和子节点之间具有双向关系的类 Node。如果经常需要在代码中导航到父项或子项,则关系应该是双向的。维持这种关系有点棘手,所以我决定发布更多的代码。
@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();
}
}