title: How to correctly map a Parent-Child (Same Table) relationship in Hibernate?
body: <p>I have an issue trying to define a relationship between a <code>parent record</code> and <code>child records</code> of the parent. I am using one table, where if a record is updated, a new one is inserted, and the old becomes inactive - with the <code>parent_id</code> set to the <code>id</code> of the <strong>new</strong> record. Here is my CREATE statement for the table:</p>
  
  <pre class="lang-sql prettyprint-override"><code>    CREATE TABLE `templates` (
        `id` int unsigned NOT NULL AUTO_INCREMENT,
        `region_id` int unsigned NOT NULL,
        `name` varchar(80) NOT NULL,
        `description` text,
        `is_active` tinyint unsigned NOT NULL DEFAULT '0',
        `parent_id` int unsigned DEFAULT NULL,
        `created_by` int unsigned DEFAULT NULL,
        `updated_by` int unsigned DEFAULT NULL,
        `created` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
        `modified` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
        PRIMARY KEY (`id`),
        KEY `reg_id_idx` (`region_id`),
        CONSTRAINT `td_templates_cms_regions_2812` FOREIGN KEY (`region_id`) REFERENCES `cms_regions` (`id`)
      ) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8;
  </code></pre>
  
  <p>And the <code>Entity</code> I have in my java IDE:</p>
  
  <pre class="lang-java prettyprint-override"><code>/*
   * To change this license header, choose License Headers in Project Properties.
   * To change this template file, choose Tools | Templates
   * and open the template in the editor.
   */
  package com.vw.asa.entities.template;
  
  import com.vw.asa.entities.cms.CmsRegions;
  
  import javax.persistence.*;
  import javax.validation.constraints.NotNull;
  import javax.validation.constraints.Size;
  import javax.xml.bind.annotation.XmlRootElement;
  import javax.xml.bind.annotation.XmlTransient;
  import java.io.Serializable;
  import java.math.BigDecimal;
  import java.util.Date;
  import java.util.List;
  
  /**
   * @author Barry Chapman
   */
  @Entity
  @Table(name = "templates", schema = "asa")
  @XmlRootElement
  @NamedQueries({
          @NamedQuery(name = "Templates.findAll", query = "SELECT t FROM Templates t"),
          @NamedQuery(name = "Templates.findById", query = "SELECT t FROM Templates t WHERE t.id = :id"),
          @NamedQuery(name = "Templates.findByName", query = "SELECT t FROM Templates t WHERE t.name = :name"),
          @NamedQuery(name = "Templates.findByIsActive", query = "SELECT t FROM Templates t WHERE t.isActive = :isActive"),
          @NamedQuery(name = "Templates.findByParentId", query = "SELECT t FROM Templates t WHERE t.parentId = :parentId"),
          @NamedQuery(name = "Templates.findByCreatedBy", query = "SELECT t FROM Templates t WHERE t.createdBy = :createdBy"),
          @NamedQuery(name = "Templates.findByUpdatedBy", query = "SELECT t FROM Templates t WHERE t.updatedBy = :updatedBy"),
          @NamedQuery(name = "Templates.findByCreated", query = "SELECT t FROM Templates t WHERE t.created = :created"),
          @NamedQuery(name = "Templates.findByModified", query = "SELECT t FROM Templates t WHERE t.modified = :modified")})
  public class Templates implements Serializable {
  
      private static final long serialVersionUID = 1L;
  
      @Id
      @GeneratedValue(strategy = GenerationType.IDENTITY)
      @Basic(optional = false)
      @Column(name = "id")
      private Integer id;
  
      @Basic(optional = false)
      @NotNull
      @Size(min = 1, max = 80)
      @Column(name = "name")
      private String name;
  
      @Lob
      @Size(max = 65535)
      @Column(name = "description")
      private String description;
  
      @Basic(optional = false)
      @NotNull
      @Column(name = "is_active")
      private short isActive;
  
      /*@Column(name = "parent_id")
      private Integer parentId;*/
  
      @Column(name = "created_by")
      private Integer createdBy;
  
      @Column(name = "updated_by")
      private Integer updatedBy;
  
      @Basic(optional = false)
      @NotNull
      @Column(name = "created")
      @Temporal(TemporalType.TIMESTAMP)
      private Date created;
  
      @Basic(optional = false)
      @NotNull
      @Column(name = "modified")
      @Temporal(TemporalType.TIMESTAMP)
      private Date modified;
  
      @JoinColumn(name = "region_id", referencedColumnName = "id")
      @ManyToOne(optional = false, fetch = FetchType.LAZY)
      private CmsRegions regionId;
  
      @JoinColumn(name = "parent_id", referencedColumnName = "id")
      @ManyToOne(optional = false,  fetch = FetchType.LAZY)
      private Integer parent_id;
  
      public Templates() {
      }
  
      public Templates(Integer id) {
          this.id = id;
      }
  
      public Templates(Integer id, String name, short isActive, Date created, Date modified) {
          this.id = id;
          this.name = name;
          this.isActive = isActive;
          this.created = created;
          this.modified = modified;
      }
  
      public Integer getId() {
          return id;
      }
  
      public void setId(Integer id) {
          this.id = id;
      }
  
      public String getName() {
          return name;
      }
  
      public void setName(String name) {
          this.name = name;
      }
  
      public String getDescription() {
          return description;
      }
  
      public void setDescription(String description) {
          this.description = description;
      }
  
      public short getIsActive() {
          return isActive;
      }
  
      public void setIsActive(short isActive) {
          this.isActive = isActive;
      }
  
      public Integer getParentId() {
          return getParent().getId();
      }
  
      public void setParentId(Integer parentId) {
          this.parent_id = parentId;
      }
  
      public Integer getCreatedBy() {
          return createdBy;
      }
  
      public void setCreatedBy(Integer createdBy) {
          this.createdBy = createdBy;
      }
  
      public Integer getUpdatedBy() {
          return updatedBy;
      }
  
      public void setUpdatedBy(Integer updatedBy) {
          this.updatedBy = updatedBy;
      }
  
      public Date getCreated() {
          return created;
      }
  
      public void setCreated(Date created) {
          this.created = created;
      }
  
      public Date getModified() {
          return modified;
      }
  
      public void setModified(Date modified) {
          this.modified = modified;
      }
  
      public Templates getParent() {
          return parent;
      }
  
      public void setParent(Templates parent) {
          this.parent = parent;
          this.parent_id = parent.getId();
      }
  
      public CmsRegions getRegionId() {
          return regionId;
      }
  
      public void setRegionId(CmsRegions regionId) {
          this.regionId = regionId;
      }
  
      @Override
      public int hashCode() {
          int hash = 0;
          hash += (id != null ? id.hashCode() : 0);
          return hash;
      }
  
      @Override
      public boolean equals(Object object) {
          // TODO: Warning - this method won't work in the case the id fields are not set
          if (!(object instanceof Templates)) {
              return false;
          }
          Templates other = (Templates) object;
          return (this.id != null || other.id == null) &amp;&amp; (this.id == null || this.id.equals(other.id));
      }
  
      @Override
      public String toString() {
          return "com.vw.asa.entities.Templates[ id=" + id + " ]";
      }
  
  }
  </code></pre>
  
  <p>I attempted to add the following block to the Entity, but it generated the error listed after this block:</p>
  
  <pre class="lang-java prettyprint-override"><code>    @JoinColumn(name = "parent_id", referencedColumnName = "id")
      @ManyToOne(optional = false,  fetch = FetchType.LAZY)
      private Integer parent;
  </code></pre>
  
  <p>To supplement this, I added the following getter/setter:</p>
  
  <pre class="lang-java prettyprint-override"><code>    public Integer getParentId() {
          return getParent().getId();
      }
  
      public void setParentId(Integer parentId) {
          this.parent_id = parentId;
      }
  </code></pre>
  
  <p>This is the error generated during compile:</p>
  
  <pre><code>Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'entityManagerFactory' defined in class path resource [org/springframework/boot/autoconfigure/orm/jpa/HibernateJpaConfiguration.class]: Invocation of init method failed; nested exception is javax.persistence.PersistenceException: [PersistenceUnit: default] Unable to build Hibernate SessionFactory; nested exception is org.hibernate.MappingException: Repeated column in mapping for entity: com.vw.asa.entities.template.TdTemplates column: parent_id (should be mapped with insert="false" update="false")
  Caused by: javax.persistence.PersistenceException: [PersistenceUnit: default] Unable to build Hibernate SessionFactory; nested exception is org.hibernate.MappingException: Repeated column in mapping for entity: com.vw.asa.entities.template.TdTemplates column: parent_id (should be mapped with insert="false" update="false")
  </code></pre>
  
  <p>It is obviously something to do with parent_id having more than one definition, but how do I structure this entity so that the following operations could be performed?</p>
  
  <pre class="lang-java prettyprint-override"><code>template.getParent().getId(); // returns parent_id
  template.setParent(template); // set the parent object
  </code></pre>
  
  <p>Are there methods/properties that can be removed and are redundant in here?</p>
  
  <p><strong>EDIT</strong></p>
  
  <p>One thing to note, is I have not definitively defined the design pattern for how the parent-child relationship will work.</p>
  
  <p>It could end up being where the <code>oldest</code> record will be parent and the <code>newest</code> is the child, or the <code>current/master/active</code> record could be the parent, and the rest down the tree could be the children. I have yet to decide which method is the best approach here.</p>