java 如何使用 Hibernate 为树建模?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/7064691/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How to model a tree with Hibernate?
提问by Hedge
I've got a class called "Domain". Each Domain can have multiple sub-domains (of the same type).
我有一个名为“域”的类。每个域可以有多个子域(相同类型)。
I need to be able to determine sub-domains and root-domains. Sub-domains can have sub-domains themself. This can be quite a few levels deep.
我需要能够确定子域和根域。子域本身可以有子域。这可以是相当深的几个级别。
Example:
例子:
Rootdomain
|- Subdomain 1
| |- Subdomain 2
| |
| |- Subdomain 3
|
|- Subdomain 4
| |- Subdomain 5
How do I model such a Java-class with Hibernate annotations?
我如何使用 Hibernate 注释对这样的 Java 类进行建模?
回答by Thomas
The modelling would be quite simple:
建模将非常简单:
@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;
}
Note that parent
is the property responsible for the database entry, i.e. you need to set parent
for a subdomain for the relation to be stored.
请注意,这parent
是负责数据库条目的属性,即您需要parent
为要存储的关系设置子域。
What is not quite trivial is the queries, since SQL (and thus HQL and JPQL) doesn't easily support tree queries. Hibernate can do this by lazily loading the next level but if you want to load a bunch of levels in one query, that's where it becomes hard.
不是很简单的是查询,因为 SQL(以及 HQL 和 JPQL)不容易支持树查询。Hibernate 可以通过延迟加载下一个级别来做到这一点,但是如果您想在一个查询中加载一堆级别,那就变得困难了。
回答by Ralph
If you want to use Hibernate/JPA lazy initialization (that is the normal case) then you should not use the composite pattern.
如果要使用 Hibernate/JPA 延迟初始化(这是正常情况),则不应使用复合模式。
The Hibernate related problem with the Composite Patternis: in a Composite you have a Composite that has a reference its child Components. But the Component is only a abstract class or Interface, so every Component is a Leaf or a Composite. If you now use lazy initialisation for the cild set of Composite, but then some how need to cast a concrete child to Leaf or Composite, you will get an Cast Exception because hibernate uses a proxy for the component that can not be casted to Leaf or Composite.
与复合模式的 Hibernate 相关问题是:在复合中,您有一个复合,它有一个引用它的子组件。但是 Component 只是一个抽象类或接口,所以每个 Component 都是一个 Leaf 或一个 Composite。如果您现在对 Composite 的 cild 集使用延迟初始化,但随后需要将具体子项转换为 Leaf 或 Composite,您将得到一个 Cast Exception,因为 hibernate 使用了无法转换为 Leaf 的组件的代理或合成的。
The second drawback of the composite pattern is, that every class will be a Leaf or a Composite for its complete lifetime. That is fine if your structure never changes. But it will not work if a Leaf must become to an Composite because someone wants to add an sub-node/leaf.
复合模式的第二个缺点是,每个类在其整个生命周期中都是一个 Leaf 或一个 Composite。如果您的结构永远不会改变,那很好。但是如果因为有人想添加一个子节点/叶子而必须将叶子变成复合材料,那么它就行不通了。
So if you have some dynamic structure I recommend one class Node that have a bidirectional relatinship between parent and child nodes. The relationship should be bidirectional if you often need to navigate to parents or childs in your code. Maintaining that relationship is a bit tricky, so I decided to post some more code.
因此,如果您有一些动态结构,我推荐一个类 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();
}
}
回答by JB Nizet
There are several possibilities, depending on which kind of operations you need to make.
有多种可能性,具体取决于您需要进行的操作类型。
The most straightforward is to simply have a parent-child one-to-many association. Depending on what you need to do, choose the appropriate kind: unidirectional one-to-many, unidirectional many-to-one, or bidirectional.
最直接的就是简单地有一个父子一对多的关联。根据您需要执行的操作,选择合适的类型:单向一对多、单向多对一或双向。
It's often useful to have all the nodes of a tree have a many-to-one relationship with the root node. This allows loading a whole tree very easily, in a single query.
让树的所有节点与根节点具有多对一关系通常很有用。这允许在单个查询中非常轻松地加载整个树。
回答by luis.espinal
I'd suggest you first get the Java-only OO model right and then worry about which Hibernate annotations you use. If you find you need to legitimatelyalter your model for Hibernate to fit in, you can always do so.
我建议您首先获得纯 Java 的 OO 模型,然后再考虑使用哪些 Hibernate 注释。如果你发现你需要合法地改变你的模型以适应 Hibernate,你总是可以这样做。
In the end, this type of problem is typically solved with some variation of the Composite pattern(don't focus too much on the pattern thing, just on the structure and idea behind it.)
最后,这种类型的问题通常通过组合模式的一些变体来解决(不要过多关注模式的事情,只关注它背后的结构和想法。)
Using relational database lingo (in a rather relaxed manner):
使用关系数据库术语(以一种相当轻松的方式):
A. If your domains (root domains and sub-domains) are relations(collections of n-tuples in a table with no duplicates and with a discernible primary key) and
A. 如果您的域(根域和子域)是关系(表中的 n 元组集合,没有重复且具有可辨别的主键)和
B. your domains and sub-domains have similar structure, then
B. 你的域和子域有相似的结构,那么
C. You might be able to store all of them in the same physical table by defining a "parent"foreign key such that the parent FK of one tuple maps to the primary key of another one.
C. 您可以通过定义一个“父”外键将它们全部存储在同一个物理表中,以便一个元组的父 FK 映射到另一个元组的主键。
Most importantly, this recursive relationship must be acyclic. How you go about it structurally is up to your problem domain (do you have one root domain, or you can have multiple, unrelated root domains?) A root domain can be denotated by having either a NULL parent foreign key, or with a condition where a root domain tuple's parent foreign key equals its own primary key. Either one has pros and cons (which are the subject of typically stupid flame wars.)
最重要的是,这种递归关系必须是非循环的。您在结构上如何处理取决于您的问题域(您有一个根域,还是可以有多个不相关的根域?)根域可以通过具有 NULL 父外键或条件来表示其中根域元组的父外键等于它自己的主键。任何一个都有优点和缺点(这是典型的愚蠢的火焰War的主题。)