[jboss-cvs] jboss-seam/examples/wiki/src/main/org/jboss/seam/wiki/core/nestedset ...

Christian Bauer christian at hibernate.org
Tue Dec 18 23:29:19 EST 2007


  User: cbauer  
  Date: 07/12/18 23:29:19

  Modified:    examples/wiki/src/main/org/jboss/seam/wiki/core/nestedset                 
                        package.html NestedSetNode.java
  Added:       examples/wiki/src/main/org/jboss/seam/wiki/core/nestedset                 
                        AbstractNestedSetDelegate.java
                        NestedSetNodeInfo.java NestedSetDelegateOwner.java
                        NestedSetDelegate.java
                        EmbeddableNestedSetDelegate.java
  Removed:     examples/wiki/src/main/org/jboss/seam/wiki/core/nestedset                 
                        AbstractNestedSetNode.java
                        DeleteNestedSetOperation.java
                        NestedSetResultTransformer.java
                        NestedSetNodeWrapper.java
                        NestedSetPostInsertEventListener.java
                        NestedSetNodeWrapperEntityConverter.java
                        NestedSetNodeDuplicator.java
                        NestedSetPostDeleteEventListener.java
                        NestedSetOperation.java
                        InsertNestedSetOperation.java
  Log:
  Major rewrite of the most of the application
  
  Revision  Changes    Path
  1.3       +52 -48    jboss-seam/examples/wiki/src/main/org/jboss/seam/wiki/core/nestedset/package.html
  
  (In the diff below, changes in quantity of whitespace are not shown.)
  
  Index: package.html
  ===================================================================
  RCS file: /cvsroot/jboss/jboss-seam/examples/wiki/src/main/org/jboss/seam/wiki/core/nestedset/package.html,v
  retrieving revision 1.2
  retrieving revision 1.3
  diff -u -b -r1.2 -r1.3
  --- package.html	17 Oct 2007 15:07:54 -0000	1.2
  +++ package.html	19 Dec 2007 04:29:19 -0000	1.3
  @@ -181,7 +181,7 @@
   
   <pre>
       &#064;Entity
  -    &#064;Table("NODE")
  +    &#064;Table("ITEM")
       class Item {
   
           &#064;Id &#064;GeneratedValue
  @@ -201,14 +201,15 @@
   </pre>
   
   <p>
  -    You can now overlay a nested set by either implementing the {@link NestedSetNode} interface or by extending
  -    the default implementation, {@link AbstractNestedSetNode}:
  +    This implementation is based on mix-in and delegates. The <tt>ITEM</tt> table of the <tt>Item</tt> class will
  +    does not carry the left, right, and thread values. This job is delegated to an additional
  +    <tt>ItemNestedSetDelegate</tt> class:
   </p>
   
   <pre>
       &#064;Entity
  -    &#064;Table("NODE")
  -    class Item extends AbstractNestedSetNode&lt;Item> {
  +    &#064;Table("ITEM")
  +    class Item implements NestedSetDelegateOwner {
   
           &#064;Id &#064;GeneratedValue
           &#064;Column(name = "ID")
  @@ -218,22 +219,37 @@
           &#064;JoinColumn(name = "PARENT")
           private Item parent;
   
  -        &#064;OneToMany(mappedBy = "parent", cascade = CascadeType.PERSIST)
  +        &#064;OneToMany(mappedBy = "parent")
           &#064;org.hibernate.annotations.OnDelete(action = org.hibernate.annotations.OnDeleteAction.CASCADE)
           private Set&lt;Item> children;
   
  +        &#064;OneToOne(mappedBy="owner",
  +                       fetch = FetchType.EAGER,
  +                       cascade = {CascadeType.PERSIST, CascadeType.REMOVE})
  +        private ItemNestedSetDelegate nestedSetDelegate;
  +
           // Constructor, getters and setters
           ...
       }
   </pre>
   
   <p>
  -    This is a mapped and persistent superclass, so additional columns are either created or required on the <tt>NODE</tt>
  -    table, their default names are <tt>NS_THREAD</tt> (identifies a particular tree), <tt>NS_LEFT</tt>, and
  -    <tt>NS_RIGHT</tt>. You can override using the JPA <tt>@AttributeOverrides</tt> annotation on the <tt>Item</tt> entity
  -    class.
  +    The delegate class maintains the nested set information transparently and stores it in a separate table:
   </p>
   
  +<pre>
  +    &#064;Entity
  +    &#064;Table("ITEM_NESTED_SET")
  +    class ItemNestedSetDelegate extends AbstractNestedSetDelegate&lt;Item> {
  +
  +        protected ItemNestedSetDelegate() {}
  +
  +        public ItemNestedSetDelegate(Item owner) {
  +            super(owner);
  +        }
  +    }
  +</pre>
  +
   <p>
       It is recommended that you enable <tt>ON DELETE CASCADE</tt> as a foreign key option on the join column of the
       adjacency list. With this option, you can easily delete a node in the tree and have the guarantee that all its
  @@ -241,22 +257,23 @@
       persistence context after deletion or if you have the second-level cache enabled).
   </p>
   <p>
  -    Enabling cascading persistence on the collection of children is also recommended. If you do not enable it, you need to call
  -    <tt>entityManager.persist(item)</tt> in the right order, e.g.: You need to call persist(A) before you
  +    You need to call <tt>entityManager.persist(item)</tt> in the right order, e.g.: You need to call persist(A) before you
       call persist(B) and persist(C) if B and C are children of A. In any case, the order in which B and C are inserted is
       undefined, this is a Set in the example - and it doesn't matter. However, parents need to be inserted before children.
   </p>
   
   <p>
       The tree is manipulated through the <tt>parent</tt> property and <tt>children</tt> collection of each node. Remember
  -    to always set both references if you link a node to a parent, which is what the default implementations
  -    <tt>addChild()</tt> and <tt>removeChild()</tt> of {@link AbstractNestedSetNode} can do for you. If you want to
  -    save a new item, create it, link it to its parent with <tt>addChild()</tt>, persist it with the
  +    to always set both references if you link a node to a parent. If you want to save a new item, create it, link it
  +    to its parent with <tt>addChild()</tt>, persist it with the
       <tt>EntityManager</tt> and flush the persistence context.
       If you want to remove an item from the tree, unlink it with <tt>removeChild()</tt> from its parent, remove it
  -    with the <tt>EntityManager</tt>, then flush the persistence context. The nested set tree is automatically
  -    updated by the event listeners in this package, which you have to add to your Hibernate configuration, here
  -    for JPA with <tt>persistence.xml</tt>:
  +    with the <tt>EntityManager</tt>, then flush the persistence context.
  +</p>
  +
  +<p>
  +    The nested set tree table is automatically updated by the event listeners in this package, which you have to add
  +    to your Hibernate configuration, here for JPA with <tt>persistence.xml</tt>:
   </p>
   
   <pre>
  @@ -279,46 +296,33 @@
   </p>
   
   <p>
  -    To query for a subtree, use the <tt>NestedSetNodeWrapper</tt> and <tt>NestedSetResultTransformer</tt>
  +    To query for a subtree, use the <tt>NestedSetWrapper</tt> and <tt>NestedSetResultTransformer</tt>
       convenience classes. An example, loading the whole subtree starting at <tt>startNode</tt> (which would
       be an instance of <tt>Item</tt> you have already loaded):
   </p>
   
   <pre>
  -    select
  -      count(n1.id) as nestedSetNodeLevel,
  -      n1           as nestedSetNode
  -    from Item n1, Item n2
  -    where
  -      n1.nsThread = :thread and n2.nsThread = :thread
  -      and n1.nsLeft between n2.nsLeft and n2.nsRight
  -      and n2.nsLeft > :startLeft and n2.nsRight &lt; :startRight
  -    group by [allPropertiesOfItem]
  -    order by n1.nsLeft
  -
  -    // Now bind the thread, left, and right values of the startNode as query arguments to the parameters
  -
  -
  -    // This comparator sorts the Items by name!
  -    Comparator&lt;NestedSetNodeWrapper&lt;Item>> comp =
  -        new Comparator&lt;NestedSetNodeWrapper&lt;Item>>() {
  -            public int compare(NestedSetNodeWrapper&lt;Item> o, NestedSetNodeWrapper&lt;Item> o2) {
  -                // Sort nodes at the same level by name?
  -                // return o.getWrappedNode().getName().compareTo(o2.getWrappedNode().getName());
  -            }
  -        };
  +NestedSetQueryBuilder nsQuery = new NestedSetQueryBuilder(startNode);
  +Query nestedSetQuery =  session.createQuery(nsQuery.getSimpleQuery());
  +
  +// Bind parameters
  +nestedSetQuery.setParameter("nsThread", startNode.getNestedSetDelegate().getNsThread());
  +nestedSetQuery.setParameter("nsLeft", startNode.getNestedSetDelegate().getNsLeft());
  +nestedSetQuery.setParameter("nsRight", startNode.getNestedSetDelegate().getNsRight());
  +
  +// Apply transformer that marshalls flat table result into an in-memory tree
  +NestedSetNodeWrapper&lt;ItemNestedSetDelegate> startNodeWrapper = new NestedSetNodeWrapper&lt;ItemNestedSetDelegate>(startNode);
  +
  +nestedSetQuery.setResultTransformer( new NestedSetResultTransformer&lt;ItemNestedSetDelegate>(startNodeWrapper) );
   
  -    NestedSetNodeWrapper&lt;Item> startNodeWrapper = new NestedSetNodeWrapper&lt;Item>(startNode, comp);
  -    nestedSetQuery.setResultTransformer( new NestedSetResultTransformer&lt;Item>(startNodeWrapper) );
  -    nestedSetQuery.list();
  +nestedSetQuery.list();
   </pre>
   
   <p>
       You can now traverse the tree by accessing <tt>startNodeWrapper.getWrappedParent()</tt>,
       <tt>startNodeWrapper.getWrappedChildren()</tt>, <tt>startNodeWrapper.getLevel()</tt>, and
  -    <tt>startNodeWrapper.getWrappedNode()</tt>. All sub-children are initialized with this single query,
  -    and the <tt>startNodeWrapper.getWrappedChildrenSorted()</tt> collection is sorted with the
  -    supplied <tt>Comparator</tt>. Use this comparator for "in-level" sorting of nodes.
  +    <tt>startNodeWrapper.getWrappedNode()</tt>. These wrappers wrap nested set delegates, so you get the real node by calling
  +    <tt>getOwner()</tt> if necessary. All sub-children as well as their owners are initialized with this single query.
   </p>
   
   <p>
  
  
  
  1.3       +7 -146    jboss-seam/examples/wiki/src/main/org/jboss/seam/wiki/core/nestedset/NestedSetNode.java
  
  (In the diff below, changes in quantity of whitespace are not shown.)
  
  Index: NestedSetNode.java
  ===================================================================
  RCS file: /cvsroot/jboss/jboss-seam/examples/wiki/src/main/org/jboss/seam/wiki/core/nestedset/NestedSetNode.java,v
  retrieving revision 1.2
  retrieving revision 1.3
  diff -u -b -r1.2 -r1.3
  --- NestedSetNode.java	25 Aug 2007 17:59:20 -0000	1.2
  +++ NestedSetNode.java	19 Dec 2007 04:29:19 -0000	1.3
  @@ -1,159 +1,20 @@
  -/*
  - * JBoss, Home of Professional Open Source
  - *
  - * Distributable under LGPL license.
  - * See terms of license at gnu.org.
  - */
   package org.jboss.seam.wiki.core.nestedset;
   
  -import java.util.Collection;
  -
  -/**
  - * Interface implemented by domain model classes that represent a node in a nested set.
  - *
  - * @author Christian Bauer
  - */
   public interface NestedSetNode<N extends NestedSetNode> {
   
  -    /**
  -     * Every node in a nested set needs a stable identifier, the primary key. This currently
  -     * is limited to numeric (long) values because the nested set model uses the identifier
  -     * of a root node (a node with no parent) to also identify a particular tree (the thread).
  -     *
  -     * @return the stable primary key of the nested set node
  -     */
       public Long getId();
  -
  -    /**
  -     * A node can veto any nested set updates.
  -     * <p>
  -     * This is useful if a particular instance doesn't really belong into the tree but has
  -     * the same type. For example, if you have <tt>FileSystemNode</tt> implements
  -     * <tt>NestedSetNode</tt> and <tt>HistoricalFileSystemNode</tt> (for audit logging, saved
  -     * to a completely different table than the tree) then <tt>HistoricalFileSystemNode</tt>
  -     * can return a veto when it is saved, so that the main tree will not be updated.
  -     *
  -     * @return true if a particular instance should not trigger nested set tree updates
  -     */
  -    public boolean vetoNestedSetUpdate();
  -
  -    /**
  -     * Nested set updates require that direct DML is executed by event listeners, this is
  -     * the name of the entity that is used by the event listeners. You can in most cases
  -     * return the simple class name of an instance, unless you customize your persistent
  -     * entity identifiers or when inheritance is involved. If, for example, you have a
  -     * class named <tt>FileSystemNode</tt> that implements <tt>NestedSetNode</tt>, and this
  -     * class has subclasses <tt>RegularFile</tt> and <tt>Directory</tt>, all instances need
  -     * to return <tt>FileSystemNode</tt> so that all nodes in the tree can be reached when
  -     * the nested set manipulation occurs in event listeners.
  -     *
  -     * @return the persistent entity name of the superclass that implements this interface
  -     */
  -    public String getTreeSuperclassEntityName();
  -    public Class getTreeSuperclass();
  +    public NestedSetNodeInfo<N> getNodeInfo();
  +    public NestedSetNodeInfo<N> getParentNodeInfo();
   
       /**
        * Utility method required until TODO: http://opensource.atlassian.com/projects/hibernate/browse/HHH-1615
  -     * is implemented. If you query for nested set subtrees, you need to group by all properties of
  -     * the nested set node implementation class (in fact, the whole hierarchy). So for example,
  -     * this method would need to return all scalar property names and foreign key property names of
  -     * classes <tt>FileSystemNode</tt> and its potential subclasses <tt>RegularFile</tt> and
  -     * <tt>Directory</tt>. Yes, this is not not great.
  +     * is implemented. If you query for nested set subtrees, you need to GROUP BY all properties of
  +     * the implementor of this interface, including identifier, version, and foreign keys (many-to-one properties).
  +     * Yes, this is not great.
        *
        * @return all property names of scalar and foreign key properties of the nested set class hierarchy
        */
  -    public abstract String[] getTreeSuperclassPropertiesForGrouping();
  -
  -    /**
  -     * An implementation must return the parent instance of a node in the tree, this can be mapped
  -     * as a regular many-to-one. This property should be nullable, that is, the root node of a thread
  -     * (a thread represents a single tree) does not have a parent. Although not strictly required by
  -     * the nested set approach (children of a parent can be identified solely by their "left" and
  -     * "right" values), it simplifies regular navigation "up the tree".
  -     *
  -     * @return the parent of this nested set node
  -     */
  -    public N getParent();
  -    public void setParent(N parent);
  -
  -    /**
  -     * An implementation must return the direct children (the sublevel) of a nested set node. This
  -     * can be mapped as a regular one-to-many collection, however, you are free to chose the type
  -     * of collection: lists, bags, and sets all work. This collection is the collection you need
  -     * to modify if you want to link a child node to a parent (by adding an element) or if you
  -     * delete a node (by removing an element).
  -     *
  -     * @return the immediate children of a nested set node
  -     */
  -    public Collection<N> getChildren();
  -
  -    /**
  -     * Convenience method that should link a node into the tree by adding it to the <tt>children</tt>
  -     * collection and setting its <tt>parent</tt> to be <i>this</i>.
  -     *
  -     * @param child the child node to be added at this level in the tree
  -     */
  -    public void addChild(N child);
  -
  -    /**
  -     * Convenience method that should remove a node from this level of the tree by removing it from
  -     * the <tt>children</tt> collection and setting its <tt>parent</tt> to null. Called before a
  -     * node is finally deleted in a persistent fashion, in your application.
  -     *
  -     * @param child the child node that is removed from the tree
  -     * @return the removed child node
  -     */
  -    public N removeChild(N child);
  -
  -    /**
  -     * The root of a tree is a nested set node without a parent. Its identifier is also the thread
  -     * number of all nodes in that particular tree. So all children nodes (and their children, recursively)
  -     * need to have the same thread number. This should be mapped as a persistent property of the
  -     * implementor of this interface, not nullable and not updatable. Any updates that are required are
  -     * done transparently with event listeners.
  -     *
  -     * @return the non-nullable, persistent, and not updatable mapped persistent identifier for a particular tree
  -     */
  -    public Long getNsThread();
  -    public void setNsThread(Long nsThread);
  -
  -    /**
  -     * In the nested set model, each node requires two additional attributes right visit and left visit. The tree is
  -     * then traversed in a modified pre-order: starting with the root node, each node is visited twice. Whenever
  -     * a node is entered or exited during the traversal, the sequence number of all visits is saved in
  -     * the current node's right visit and left visit. This is the job of the event listeners, not yours. You can
  -     * retrieve the current value with this method.
  -     *
  -     * @return the left value of a node
  -     */
  -    public Long getNsLeft();
  -    public void setNsLeft(Long nsLeft);
  -
  -    /**
  -     * In the nested set model, each node requires two additional attributes right visit and left visit. The tree is
  -     * then traversed in a modified pre-order: starting with the root node, each node is visited twice. Whenever
  -     * a node is entered or exited during the traversal, the sequence number of all visits is saved in
  -     * the current node's right visit and left visit. This is the job of the event listeners, not yours. You can
  -     * retrieve the current value with this method.
  -     *
  -     * @return the left value of a node
  -     */
  -    public Long getNsRight();
  -    public void setNsRight(Long nsRight);
  -
  -    /**
  -     * The number of children of this node, only one level deep.
  -     *
  -     * @return the number of children of this node, one level deep.
  -     */
  -    public int getDirectChildCount();
  -
  -    /**
  -     * The number of child nodes of this node, total at all sub-levels. This can be calculated by taking
  -     * the left and right values: <tt>Math.floor((getNsRight() - getNsLeft()) / 2)</tt>
  -     *
  -     * @return the total number of children on all sub-levels
  -     */
  -    public int getTotalChildCount();
  +    public String[] getPropertiesForGroupingInQueries();
  +    public String[] getLazyPropertiesForGroupingInQueries();
       
   }
  
  
  
  1.1      date: 2007/12/19 04:29:19;  author: cbauer;  state: Exp;jboss-seam/examples/wiki/src/main/org/jboss/seam/wiki/core/nestedset/AbstractNestedSetDelegate.java
  
  Index: AbstractNestedSetDelegate.java
  ===================================================================
  /*
   * JBoss, Home of Professional Open Source
   *
   * Distributable under LGPL license.
   * See terms of license at gnu.org.
   */
  package org.jboss.seam.wiki.core.nestedset;
  
  import javax.persistence.*;
  
  /**
   * Utility class that implements the basic {@link NestedSetDelegate} interface.
   * <p>
   * Use this class if you already have an adjacency list model (parent/children
   * relationship mapped with a regular many-to-one property and a one-to-many collection) based
   * on a foreign key. You only need to add this superclass to a <tt>@OneToOne</tt> persistent delegate
   * entity class and you will be able to execute nested set queries on your trees and have the event listeners
   * update the nested set values of the tree (thread, left, right, of each node) if you add or
   * remove nodes.
   * 
   * @author Christian Bauer
   */
  @MappedSuperclass
  public abstract class AbstractNestedSetDelegate<N extends NestedSetDelegateOwner> implements NestedSetDelegate<N> {
  
      @Id
      @GeneratedValue(generator = "nestedSetOwnerGenerator")
      @org.hibernate.annotations.GenericGenerator(
          name = "nestedSetOwnerGenerator",
          strategy = "foreign",
          parameters = @org.hibernate.annotations.Parameter(name = "property", value = "owner")
      )
      @Column(name = "NESTED_SET_OWNER_ID")
      private Long id;
  
      @ManyToOne(fetch = FetchType.EAGER, optional = false)
      @JoinColumn(name = "NESTED_SET_OWNER_ID", updatable = false, insertable = false, unique = true)
      @org.hibernate.annotations.OnDelete(action = org.hibernate.annotations.OnDeleteAction.CASCADE)
      private N owner;
  
      @Column(name = "NS_THREAD", nullable = false, updatable = false)
      private Long nsThread = 0l;
  
      @Column(name = "NS_LEFT", nullable = false, updatable = false)
      private Long nsLeft = 0l;
  
      @Column(name = "NS_RIGHT", nullable = false,  updatable = false)
      private Long nsRight = 0l;
  
      protected AbstractNestedSetDelegate() {}
  
      public AbstractNestedSetDelegate(N owner) {
          this.owner = owner;
      }
  
      public AbstractNestedSetDelegate(N owner, AbstractNestedSetDelegate<N> copyDelegate) {
          this.owner = copyDelegate.getOwner();
          this.nsLeft = copyDelegate.nsLeft;
          this.nsRight = copyDelegate.nsRight;
          this.nsThread = copyDelegate.nsThread;
      }
  
      public Long getId() {
          return id;
      }
  
      public N getOwner() {
          return owner;
      }
  
      public Long getNsThread() {
          return nsThread;
      }
  
      public void setNsThread(Long nsThread) {
          this.nsThread = nsThread;
      }
  
      public Long getNsLeft() {
          return nsLeft;
      }
  
      public void setNsLeft(Long nsLeft) {
          this.nsLeft = nsLeft;
      }
  
      public Long getNsRight() {
          return nsRight;
      }
  
      public void setNsRight(Long nsRight) {
          this.nsRight = nsRight;
      }
  
  }
  
  
  
  1.1      date: 2007/12/19 04:29:19;  author: cbauer;  state: Exp;jboss-seam/examples/wiki/src/main/org/jboss/seam/wiki/core/nestedset/NestedSetNodeInfo.java
  
  Index: NestedSetNodeInfo.java
  ===================================================================
  package org.jboss.seam.wiki.core.nestedset;
  
  import org.hibernate.annotations.Parent;
  
  import javax.persistence.Embeddable;
  import javax.persistence.Column;
  
  @Embeddable
  public class NestedSetNodeInfo<N extends NestedSetNode> {
  
      @Parent
      private N owner;
  
      @Column(name = "NS_THREAD", nullable = false, updatable = false)
      private Long nsThread = 0l;
  
      @Column(name = "NS_LEFT", nullable = false, updatable = false)
      private Long nsLeft = 0l;
  
      @Column(name = "NS_RIGHT", nullable = false,  updatable = false)
      private Long nsRight = 0l;
  
      protected NestedSetNodeInfo() {}
  
      public NestedSetNodeInfo(N owner) {
          this.owner = owner;
      }
  
      public NestedSetNodeInfo(NestedSetNodeInfo<N> original) {
          this.owner = original.owner;
          this.nsLeft = original.nsLeft;
          this.nsRight = original.nsRight;
          this.nsThread = original.nsThread;
      }
  
      public N getOwner() {
          return owner;
      }
  
      private void setOwner(N owner) {
          this.owner = owner;
      }
  
      public Long getNsThread() {
          return nsThread;
      }
  
      public void setNsThread(Long nsThread) {
          this.nsThread = nsThread;
      }
  
      public Long getNsLeft() {
          return nsLeft;
      }
  
      public void setNsLeft(Long nsLeft) {
          this.nsLeft = nsLeft;
      }
  
      public Long getNsRight() {
          return nsRight;
      }
  
      public void setNsRight(Long nsRight) {
          this.nsRight = nsRight;
      }
  
      public String toString() {
          return "NSInfo LEFT: " + getNsLeft() + " RIGHT: " + getNsRight() + " THREAD: " + getNsThread();
      }
  }
  
  
  
  1.1      date: 2007/12/19 04:29:19;  author: cbauer;  state: Exp;jboss-seam/examples/wiki/src/main/org/jboss/seam/wiki/core/nestedset/NestedSetDelegateOwner.java
  
  Index: NestedSetDelegateOwner.java
  ===================================================================
  /*
   * JBoss, Home of Professional Open Source
   *
   * Distributable under LGPL license.
   * See terms of license at gnu.org.
   */
  package org.jboss.seam.wiki.core.nestedset;
  
  /**
   * Implemented by domain model classes if they support nested sets.
   * <p>
   * This is your starting point, add this interface to your domain model class and implement it.
   * </p>
   * <p>
   * You also need to implement a {@link org.jboss.seam.wiki.core.nestedset.NestedSetDelegate} class
   * for each domain model class that supports nested sets. You need to map a reference to this
   * delegate as follows:
   * </p>
   * <pre>
   * class MyDomainModelClass implements NestedSetDelegateOwner {
   *
   *     // Adjacency list for parent/child
   *     ...
   * 
   *     &#064;OneToOne(mappedBy="owner", fetch = FetchType.EAGER, cascade = {CascadeType.PERSIST, CascadeType.REMOVE})
   *     private MyDomainModelClassNestedSetDelegate nestedSetDelegate;
   * }
   * </pre>
   * <p>
   * Make sure that every instance of your domain model class has an instance of the delegate,
   * a <tt>@OneToOne</tt> to a persistent <tt>NestedSetDelegate</tt> instance (initialize it
   * in the constructor). In addition, you need to return the parents delegate (if there is any,
   * if not, then null) accordingly.
   * </p>
   *
   * @author Christian Bauer
   */
  public interface NestedSetDelegateOwner {
  
      public Long getId();
  
      public NestedSetDelegate getNestedSetDelegate();
      public NestedSetDelegate getParentNestedSetDelegate();
  
      /**
       * Utility method required until TODO: http://opensource.atlassian.com/projects/hibernate/browse/HHH-1615
       * is implemented. If you query for nested set subtrees, you need to GROUP BY all properties of
       * the implementor of this interface, including identifier, version, and foreign keys (many-to-one properties).
       * Yes, this is not great.
       *
       * @return all property names of scalar and foreign key properties of the nested set class hierarchy
       */
      public String[] getPropertiesForGroupingInQueries();
  }
  
  
  
  1.1      date: 2007/12/19 04:29:19;  author: cbauer;  state: Exp;jboss-seam/examples/wiki/src/main/org/jboss/seam/wiki/core/nestedset/NestedSetDelegate.java
  
  Index: NestedSetDelegate.java
  ===================================================================
  /*
   * JBoss, Home of Professional Open Source
   *
   * Distributable under LGPL license.
   * See terms of license at gnu.org.
   */
  package org.jboss.seam.wiki.core.nestedset;
  
  /**
   * Interface implemented by domain model classes that represent a node in a nested set.
   * Usually used as a delegate, that is, an existing class that already has a parent/children
   * adjacency list adds a <tt>@OneToOne</tt> association to a delegate that implements this
   * interface. This (more or less) transparently adds nested set information to each instance.
   *
   * @author Christian Bauer
   */
  public interface NestedSetDelegate<N extends NestedSetDelegateOwner> {
  
      /**
       * Same as <tt>getOwner().getId()</tt> but it makes our life easier.
       *
       * @return Identifier of the nested set node, i.e. the owner of the delegate.
       */
      public Long getId();
  
      /**
       * The domain model class that wishes to add nested set information should implement a
       * delegate to this interface. The domain model class also has to implement
       * <tt>NestedSetDelegateOwner</tt>. This method returns that owner.
       *
       * @return the owner of the <tt>NestedSetNodeDelegate</tt>, which implements <tt>NestedSetDelegateOwner</tt>
       */
      public N getOwner();
  
      /**
       * The root of a tree is a nested set node without a parent. Its identifier is also the thread
       * number of all nodes in that particular tree. So all children nodes (and their children, recursively)
       * need to have the same thread number. This should be mapped as a persistent property of the
       * implementor of this interface, not nullable and not updatable. Any updates that are required are
       * done transparently with event listeners.
       *
       * @return the non-nullable, persistent, and not updatable mapped persistent identifier for a particular tree
       */
      public Long getNsThread();
      public void setNsThread(Long nsThread);
  
      /**
       * In the nested set model, each node requires two additional attributes right visit and left visit. The tree is
       * then traversed in a modified pre-order: starting with the root node, each node is visited twice. Whenever
       * a node is entered or exited during the traversal, the sequence number of all visits is saved in
       * the current node's right visit and left visit. This is the job of the event listeners, not yours. You can
       * retrieve the current value with this method.
       *
       * @return the left value of a node
       */
      public Long getNsLeft();
      public void setNsLeft(Long nsLeft);
  
      /**
       * In the nested set model, each node requires two additional attributes right visit and left visit. The tree is
       * then traversed in a modified pre-order: starting with the root node, each node is visited twice. Whenever
       * a node is entered or exited during the traversal, the sequence number of all visits is saved in
       * the current node's right visit and left visit. This is the job of the event listeners, not yours. You can
       * retrieve the current value with this method.
       *
       * @return the left value of a node
       */
      public Long getNsRight();
      public void setNsRight(Long nsRight);
  
  }
  
  
  
  1.1      date: 2007/12/19 04:29:19;  author: cbauer;  state: Exp;jboss-seam/examples/wiki/src/main/org/jboss/seam/wiki/core/nestedset/EmbeddableNestedSetDelegate.java
  
  Index: EmbeddableNestedSetDelegate.java
  ===================================================================
  package org.jboss.seam.wiki.core.nestedset;
  
  import org.hibernate.annotations.Parent;
  
  import javax.persistence.Embeddable;
  import javax.persistence.Column;
  
  @Embeddable
  public class EmbeddableNestedSetDelegate<N extends NestedSetDelegateOwner> implements NestedSetDelegate<N> {
  
      @Parent
      private N owner;
  
      @Column(name = "NS_THREAD", nullable = false, updatable = false)
      private Long nsThread = 0l;
  
      @Column(name = "NS_LEFT", nullable = false, updatable = false)
      private Long nsLeft = 0l;
  
      @Column(name = "NS_RIGHT", nullable = false,  updatable = false)
      private Long nsRight = 0l;
  
      protected EmbeddableNestedSetDelegate() {}
  
      public EmbeddableNestedSetDelegate(N owner) {
          this.owner = owner;
      }
  
      public EmbeddableNestedSetDelegate(N owner, EmbeddableNestedSetDelegate<N> copyDelegate) {
          this.owner = copyDelegate.getOwner();
          this.nsLeft = copyDelegate.nsLeft;
          this.nsRight = copyDelegate.nsRight;
          this.nsThread = copyDelegate.nsThread;
      }
  
      public Long getId() {
          return getOwner().getId();
      }
  
      public N getOwner() {
          return owner;
      }
  
      private void setOwner(N owner) {
          this.owner = owner;
      }
  
      public Long getNsThread() {
          return nsThread;
      }
  
      public void setNsThread(Long nsThread) {
          this.nsThread = nsThread;
      }
  
      public Long getNsLeft() {
          return nsLeft;
      }
  
      public void setNsLeft(Long nsLeft) {
          this.nsLeft = nsLeft;
      }
  
      public Long getNsRight() {
          return nsRight;
      }
  
      public void setNsRight(Long nsRight) {
          this.nsRight = nsRight;
      }
  
  }
  
  
  



More information about the jboss-cvs-commits mailing list