我在使用GORM / Grails建模家谱时遇到问题。我知道建议使用直接非循环图来模拟这样的结构。
我从Person类开始:
class Persion {
String name
}
人可以具有以下关系:
1。我如何建模这种关系结构?
2。如何在这些结构中插入或删除人员?
第3。如何保证结果图没有周期?
编辑:
假设我们有一个人A:
儿童关系:
家长关系:
合作伙伴关系:
答案 0 :(得分:3)
你可以像下面这样做。它相当于儿童,父母等。
class TreeNode
{
String name
/**
* This method deletes a node and all the relations that are bound to this node.
* @return
*/
def deleteNode() {
// delete all child relations
def myChildren = getChildren()
println "myChildren: "+myChildren*.name
myChildren.each { child ->
println "child: "+child.name
removeFromChildren(child)
}
// delete all parent relations
def myParents = getParents()
println "myParents: "+myParents*.name
myParents.each { parent ->
println "parent: "+parent.name
removeFromParents(parent)
}
delete(flush:true)
}
TreeSet<TreeNode> getChildren() {
TreeNodeChild.executeQuery("select tnc.child from TreeNodeChild tnc where tnc.node = :node", [node: this])
}
TreeNode removeFromChildren(TreeNode child) {
TreeNodeChild.findByNodeAndChild(this, child).delete(flush: true)
this
}
/**
* Add a node as type (i.e. child) to another node.
* @param child
* @return
*/
TreeNode addToChildren(TreeNode child) {
TreeNodeChild tnc = new TreeNodeChild(node: this, child: child)
if (tnc.validate()) {
if (!isCyclic(child, "type")) {
println ">>>>>>>> no cycle"
tnc.save(flush: true)
}
else {
println ">>>>>>>> !!!!!!! cycle found"
}
}
this
}
TreeSet<TreeNode> getParents() {
TreeNodeChild.executeQuery("select tnc.node from TreeNodeChild tnc where tnc.child = :child", [child: this])
}
TreeNode removeFromParents(TreeNode parent) {
TreeNodeChild.findByNodeAndChild(parent, this).delete(flush: true)
this
}
TreeNode addToParents(TreeNode parent) {
TreeNodeChild tnc = new TreeNodeChild(node: parent, child: this)
if (tnc.validate()) {
if (!parent.isCyclic(this, "type")) {
println ">>>>>>>> no cycle"
tnc.save(flush: true)
}
else {
println ">>>>>>>> !!!!!!! cycle found"
}
}
this
}
private boolean isCyclic(node) {
boolean cyclic = false
def myParents = this.getParents()
// if there are parents of this node
if (myParents.size() != 0) {
// if the new node is in the parents set of this node
if (myParents.contains(node)) {
cyclic = true
return cyclic
}
else {
// go into each parent of this node and test if new node is contained in their parents
myParents.each { parent ->
if (cyclic) {
return cyclic
}
cyclic = parent.isCyclic(node)
}
}
}
return cyclic
}
}
答案 1 :(得分:2)
最棘手的部分是确保没有任何递归。我能想到的最简单的建模方法是:
class Persion {
Person mother
Person father
String name
//The methods for the other collections would be the same
Set<Person> getChildren() {
PersonChild.executeQuery("select pc.child from PersonChild pc where pc.person = :person", [person: this])
}
Person removeFromChildren(Person child) {
PersonChild.findByPersonAndChild(this, child).delete()
this
}
Person addToChildren(Person child) {
//Something like this to prevent recursion
//Save should fail if the person already has that person as a child
List<Person> others = [mother, father]
others += siblings
others += partners
PersonChild pc = new PersonChild(person: this, child: child)
if (pc.validate()) {
if (!others.contains(child)) {
pc.save()
}
}
this
}
}
class PersionChild {
Person person
Person child
}
class PersionSibling {
Person person
Person sibling
}
class PersonPartner {
Person person
Person partner
}
您可以在Spring Security插件创建的默认UserRole表之后为Person *表建模。
答案 2 :(得分:2)
我不熟悉GORM / Grails。研究表明它本质上是Java。
这是我在普通Java环境中使用的那种结构。
一个Person
对象,它包含所有关系和一些简单的实用方法(例如遍历所有父项子项的getSiblings
)。
关系将作为Set
来管理,以减少您必须执行的参照完整性检查的数量(但不会消除它)。
将使用工厂方法创建和销毁关系。我在我的样本中使用了enum
。
为简洁起见,我没有实现getter和setter。您的最终解决方案应该正确使用它们。
我没有尝试过你可能需要的任何更微妙的机制 - 例如将孩子添加到特定的父母对。这是可以实现的,但在这种情况下不必要地复杂。
public class Person {
// Using HashSet to limit possibilities of cycles - we can assume no person exists twice in each.
private final Set<Person> parents = new HashSet<>();
private final Set<Person> partners = new HashSet<>();
private final Set<Person> children = new HashSet<>();
// Person details.
private final String name;
// Constructor.
public Person(String name) {
this.name = name;
}
// Extract all siblings as all children of all parents.
public Set<Person> getSiblings() {
Set<Person> siblings = new HashSet<>();
for (Person parent : parents) {
siblings.addAll(parent.children);
}
return siblings;
}
}
// A factory to handle family connections.
public enum Connection {
ParentOf {
@Override
void connect(Person a, Person b) {
// Connect through a's children and b's parents.
connect(a.children, b, b.parents, a);
}
@Override
void disconnect(Person a, Person b) {
// Connect through a's children and b's parents.
disconnect(a.children, b, b.parents, a);
}
},
PartnerOf {
@Override
void connect(Person a, Person b) {
// Connect through a's children and b's parents.
connect(a.partners, b, b.partners, a);
}
@Override
void disconnect(Person a, Person b) {
// Connect through a's children and b's parents.
disconnect(a.partners, b, b.partners, a);
}
},
ChildOf {
@Override
void connect(Person a, Person b) {
// The opposit of parent.
ParentOf.connect(b, a);
}
@Override
void disconnect(Person a, Person b) {
// The opposit of parent.
ParentOf.disconnect(b, a);
}
};
abstract void disconnect(Person a, Person b);
abstract void connect(Person a, Person b);
/**
* Connect b to a through aSet and a to b through bSet
*
* @param aSet The set in person a
* @param b The b person
* @param bSet The set in person b
* @param a The a person
*/
void connect(Set<Person> aSet, Person b, Set<Person> bSet, Person a) {
aSet.add(b);
bSet.add(a);
}
/**
* Reverse of connect.
*
* @param aSet The set in person a
* @param b The b person
* @param bSet The set in person b
* @param a The a person
*/
void disconnect(Set<Person> aSet, Person b, Set<Person> bSet, Person a) {
aSet.remove(b);
bSet.remove(a);
}
}
public class Tree {
// Safe version - ensuring relationships are maintained.
public void connect(Person a, Connection c, Person b) {
c.connect(a, b);
}
public void test() {
Person adam = new Person("Adam");
Person eve = new Person("Eve");
Person cain = new Person("Cain");
Person abel = new Person("Abel");
connect(adam, Connection.PartnerOf, eve);
connect(adam, Connection.ParentOf, cain);
connect(eve, Connection.ParentOf, cain);
connect(adam, Connection.ParentOf, abel);
connect(eve, Connection.ParentOf, abel);
}
}
问题的最后部分:
- 如何保证结果图没有循环?
醇>
实际上 - 没有多少努力 - 你做不到!我怀疑这是不可能的,但也记得在一个真正的家谱中可以有循环。用户偶然创建循环是很常见的。从不同的方式来自同一祖先也是很常见的 - 所有这一切都是表亲婚姻而且完全合法。
这不应该是您的结构的要求,它应该是您的结构的可发现功能。
答案 3 :(得分:1)
以下是我为您的问题所做的事情
域类
class Children {
String childrenName
static belongsTo = [person:Person]
}
class Parent {
String parentName
static belongsTo = [person:Person]
}
class Partner {
String partnerName
static belongsTo = [person:Person]
}
class Person {
String personName
static hasMany = [childrens: Children,
parents: Parent,
partners: Partner,
siblings: Sibiling]
}
class Sibiling {
String sibilingName
static belongsTo = [person:Person]
}
然后在控制器中我违反了标准规则在服务中执行此操作
def person = new Person();
person.personName = "Suganthan"
def children = new Children()
children.childrenName = "SuganthanchilName"
def parent = new Parent()
parent.parentName = "SuganthanParentName"
def partner = new Partner()
partner.partnerName = "SuganthanPartnerName"
def sibling = new Sibiling()
sibling.sibilingName = "SuganthanSibiligsName"
person.addToChildrens(children)
person.addToParents(parent)
person.addToPartners(partner)
person.addToSiblings(sibling)
person.save()
然后再次投入使用以获得所需的结果
def criteria = Children.createCriteria();
Children children = criteria.get {
eq('id',1 as long)
}
def criteria1 = Parent.createCriteria();
Parent parent = criteria1.get {
eq('id',1 as long)
}
def criteria2 = Partner.createCriteria();
Partner partner = criteria2.get {
eq('id',1 as long)
}
def criteria3 = Sibiling.createCriteria();
Sibiling sibiling = criteria3.get {
eq('id',1 as long)
}
println(children.person.personName)
println(parent.person.personName)
println(partner.person.personName)
println(sibiling.person.personName)
我的结果是
Suganthan
Suganthan
Suganthan
Suganthan
希望这是你所期待的并且有很多帮助