4

我在 GORM/Grails 中建模家谱时遇到问题。我知道建议使用直接无环图来建模这样的结构。

我从 Person 类开始:

class Persion {
  String name 
}

一个可以有以下关系:

  • 0 到 n 个孩子
  • 0 到 n 个兄弟姐妹(可能是姐妹或兄弟)
  • 0 到 n 个合作伙伴(这些可能是妻子或丈夫)
  • 0 到 n 个父母(可能是母亲或父亲)

1. 我该如何建模这样的关系结构?

2. 如何从这些结构中插入或删除人员?

3、如何保证生成的图没有环?

编辑:

假设我们有一个人 A:

  • 子女关系:

    • 如果将子 B 添加到 A,则子 B 必须将 A 作为父级。
  • 父母关系:

    • 如果将父 C 添加到 A,则 C 有一个子 A
  • 合作伙伴关系:

    • 如果将伙伴 D 添加到 A,则 D 有一个伙伴 A
4

4 回答 4

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
     }





    }
于 2013-10-26T01:24:29.513 回答
2

我不熟悉 GORM/Grails。研究表明它本质上是 Java。

这是我在纯 Java 环境中使用的那种结构。

一个Person包含所有关系和一些简单实用方法的对象(例如getSiblings遍历所有父母的孩子的你)。

这些关系将被管理为Sets 以减少您必须执行的引用完整性检查的数量(但它不会消除它)。

将使用工厂方法创建和销毁关系。我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);
  }

}

你问题的最后一部分:

  1. 如何保证生成的图没有环?

实际上 - 没有一些巨大的努力 - 你不能!我怀疑这是不可能的,但也要记住,真正的家谱中可能存在循环。用户意外创建循环是很常见的。以不止一种方式从同一个祖先继承下来也很常见——只要表亲结婚,这些都是完全合法的。

这不应该是你的结构的要求,它应该是你的结构的一个可发现的特征。

于 2013-10-30T11:41:05.057 回答
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* 表进行建模。

于 2013-10-22T22:27:30.383 回答
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)

我的结果是

苏甘坦

苏甘坦

苏甘坦

苏甘坦

希望这是您所期望的,并且对您有很大帮助

于 2013-10-31T11:11:09.380 回答