Java 如何使用Hibernate为树建模?
我有一个叫做“域”的类。每个域可以有多个子域(相同类型) 我需要能够确定子域和根域。子域本身可以有子域。这可能有相当多的层次 例如:Java 如何使用Hibernate为树建模?,java,hibernate,spring,Java,Hibernate,Spring,我有一个叫做“域”的类。每个域可以有多个子域(相同类型) 我需要能够确定子域和根域。子域本身可以有子域。这可能有相当多的层次 例如: Rootdomain |- Subdomain 1 | |- Subdomain 2 | | | |- Subdomain 3 | |- Subdomain 4 | |- Subdomain 5 如何使用Hibernate注释为这样的Java类建模?建模将非常简单: @Entity class Domain { @ManyToOn
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;
}
@实体
类域{
@manytone//根据需要添加列定义
私有域父级;//父级==null的每个域都是根域,所有其他域都是子域
@OneToMany//根据需要添加列定义
私有列表子域;
}
请注意,parent
是负责数据库条目的属性,即需要为要存储的关系的子域设置parent
查询并不简单,因为SQL(以及HQL和JPQL)不容易支持树查询。Hibernate可以通过惰性地加载下一个级别来实现这一点,但是如果您想在一个查询中加载一组级别,这就变得很困难了。根据您需要执行的操作类型,有几种可能性 最简单的方法是简单地建立父子一对多的关联。根据需要执行的操作,选择适当的类型:单向一对多、单向多对一或双向
让树的所有节点与根节点具有多对一关系通常很有用。这允许在一个查询中非常轻松地加载整个树。我建议您首先正确使用纯Java的OO模型,然后再考虑使用哪种Hibernate注释。如果您发现需要合法地修改您的Hibernate模型以适应,您可以随时这样做 最后,这种类型的问题通常通过一些不同的方法来解决(不要过于关注模式,只关注其背后的结构和思想) 使用关系数据库行话(以相当轻松的方式): A.如果您的域(根域和子域)是关系(表中的n元组集合,没有重复项且主键可识别)和 B.您的域和子域具有相似的结构,然后 C.通过定义一个“父”外键,一个元组的父FK映射到另一个元组的主键,可以将所有元组存储在同一个物理表中
最重要的是,这种递归关系必须是非循环的。如何在结构上实现这一点取决于您的问题域(您是有一个根域,还是可以有多个不相关的根域?)根域可以通过具有空父外键或根域元组的父外键等于其主键的条件来表示。任何一个都有其优点和缺点(这是典型的愚蠢的火焰战争的主题)。如果您想使用Hibernate/JPA惰性初始化(这是正常情况),那么您应该不要使用复合模式 与Hibernate相关的问题是:在一个组合中,有一个组合具有其子组件的引用。 但是组件只是一个抽象类或接口,所以每个组件都是一个叶子或复合物。 如果您现在对复合的cild集使用惰性初始化,但是需要将一个具体的子级强制转换为叶或复合,那么您将得到一个强制转换异常,因为hibernate为无法强制转换为叶或复合的组件使用代理 复合模式的第二个缺点是,在其整个生命周期中,每个类都是一个叶或复合。 如果你的结构从未改变,那就好了。但是,如果某个叶必须成为复合叶,因为有人想要添加子节点/叶,那么它将不起作用
因此,如果您有一些动态结构,我建议使用在父节点和子节点之间具有双向关系的类节点。 如果您经常需要在代码中导航到父对象或子对象,那么这种关系应该是双向的。 维护这种关系有点棘手,所以我决定发布更多的代码
@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();
}
}
@实体
公共类域{
@身份证
私人长id;
/**父域,如果是根域,则可以为null*/
@许多酮
私有域父节点;
/**
*此域的子域。
*
*这是父关系的反面。
*
*孩子有责任管理这些家长和孩子!
*/
@NotNull
@OneToMany(mappedBy=“家长”)
private Set children=new HashSet();
/**
*不要使用这个构造函数!
*仅由Hibernate使用。
*/
域(){
}
/**
*实例化一个新域。
*域将与父域处于相同的状态。
*
*@param parent父域
*@see Domain#createRoot()
*/
公共域(最终域父级){
如果(parent==null)抛出新的IllegalArgumentException(“parent required”);
this.parent=parent;
registerInParentsChilds();
}
/**在其父域的子列表中注册此域*/
私有无效注册表项parentschilds(){
this.parent.children.add(this);
}
/**
*返回此域的不可修改的子项。
*
*@返回子节点。
*/
公共集getChildren(){
返回集合.unmodifiableSet(this.children);
}
/**
*将此域移动到新的父域。
*
*@param newParent新父级
*/
公共无效移动(最终域newParent){
Check.notNullArgument(newParent,“newParent”);
如果(!isPropertoVetTarget(newParent)/*检测圆…*/){
抛出新的IllegalArgumentException(“移动