【问题标题】:Family Tree Model in GORM/Grails?GORM/Grails 中的家谱模型?
【发布时间】:2013-10-22 21:27:07
【问题描述】:

我在 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

【问题讨论】:

标签: java spring hibernate grails grails-2.0


【解决方案1】:

你可以像下面的课程那样做。对孩子、父母等都是等效的。

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
     }





    }

【讨论】:

    【解决方案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* 表进行建模。

    【讨论】:

    • 这是一种高效的实现方式吗?我的意思是我要查询的人的关系
    • 我不明白你的问题:“我的意思是你想查询人的关系”。如果您能想到更有效的方法,请告诉我。
    • 如何确定图中没有循环?你能解释一下你的代码 List others = [mother, Father] 做什么吗?你会怎么做删除?添加孩子时,您必须确保孩子中有父亲或母亲。你会怎么做?
    • 我觉得用父母代替爸爸妈妈会更简单!
    • 不,我不能。 stackoverflow 不是为了提供完整的代码。想办法
    【解决方案3】:

    我不熟悉 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. 如何保证生成的图没有环?

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

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

    【讨论】:

      【解决方案4】:

      这是我为您的问题所做的事情

      域类

      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)
      

      我的结果是

      苏甘坦

      苏甘坦

      苏甘坦

      苏甘坦

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

      【讨论】:

        猜你喜欢
        • 2012-05-13
        • 2023-03-24
        • 1970-01-01
        • 2017-07-26
        • 2023-03-08
        • 2018-09-06
        • 1970-01-01
        • 1970-01-01
        • 2022-12-16
        相关资源
        最近更新 更多