如何使用Hibernate建模树?

Hed*_*dge 18 java spring hibernate

我有一个名为"Domain"的课程.每个域可以有多个子域(相同类型).

我需要能够确定子域和根域.子域可以自己拥有子域.这可能是相当多的深度.

例:

Rootdomain  
|- Subdomain 1  
|   |- Subdomain 2  
|   |
|   |- Subdomain 3
|
|- Subdomain 4
|   |- Subdomain 5
Run Code Online (Sandbox Code Playgroud)

如何使用Hibernate注释建模这样的Java类?

Tho*_*mas 18

建模非常简单:

@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;
}
Run Code Online (Sandbox Code Playgroud)

请注意,这parent是负责数据库条目的属性,即您需要parent为要存储的关系设置子域.

不太重要的是查询,因为SQL(以及HQL和JPQL)不容易支持树查询.Hibernate可以通过懒惰地加载下一级别来实现这一点,但是如果你想在一个查询中加载一堆级别,那就很难了.


Ral*_*lph 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();
    }
}
Run Code Online (Sandbox Code Playgroud)