Note that there are some explanatory texts on larger screens.

plurals
  1. PO
    primarykey
    data
    text
    <p>For me the trick was to use many-to-many relationship. Suppose that your entity A is a division that can have sub-divisions. Then (skipping irrelevant details):</p> <pre><code>@Entity @Table(name = "DIVISION") @EntityListeners( { HierarchyListener.class }) public class Division implements IHierarchyElement { private Long id; @Id @Column(name = "DIV_ID") public Long getId() { return id; } ... private Division parent; private List&lt;Division&gt; subDivisions = new ArrayList&lt;Division&gt;(); ... @ManyToOne @JoinColumn(name = "DIV_PARENT_ID") public Division getParent() { return parent; } @ManyToMany @JoinTable(name = "DIVISION", joinColumns = { @JoinColumn(name = "DIV_PARENT_ID") }, inverseJoinColumns = { @JoinColumn(name = "DIV_ID") }) public List&lt;Division&gt; getSubDivisions() { return subDivisions; } ... } </code></pre> <p>Since I had some extensive business logic around hierarchical structure and JPA (based on relational model) is very weak to support it I introduced interface <code>IHierarchyElement</code> and entity listener <code>HierarchyListener</code>:</p> <pre><code>public interface IHierarchyElement { public String getNodeId(); public IHierarchyElement getParent(); public Short getLevel(); public void setLevel(Short level); public IHierarchyElement getTop(); public void setTop(IHierarchyElement top); public String getTreePath(); public void setTreePath(String theTreePath); } public class HierarchyListener { @PrePersist @PreUpdate public void setHierarchyAttributes(IHierarchyElement entity) { final IHierarchyElement parent = entity.getParent(); // set level if (parent == null) { entity.setLevel((short) 0); } else { if (parent.getLevel() == null) { throw new PersistenceException("Parent entity must have level defined"); } if (parent.getLevel() == Short.MAX_VALUE) { throw new PersistenceException("Maximum number of hierarchy levels reached - please restrict use of parent/level relationship for " + entity.getClass()); } entity.setLevel(Short.valueOf((short) (parent.getLevel().intValue() + 1))); } // set top if (parent == null) { entity.setTop(entity); } else { if (parent.getTop() == null) { throw new PersistenceException("Parent entity must have top defined"); } entity.setTop(parent.getTop()); } // set tree path try { if (parent != null) { String parentTreePath = StringUtils.isNotBlank(parent.getTreePath()) ? parent.getTreePath() : ""; entity.setTreePath(parentTreePath + parent.getNodeId() + "."); } else { entity.setTreePath(null); } } catch (UnsupportedOperationException uoe) { LOGGER.warn(uoe); } } } </code></pre>
    singulars
    1. This table or related slice is empty.
    plurals
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. VO
      singulars
      1. This table or related slice is empty.
    2. VO
      singulars
      1. This table or related slice is empty.
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload