首页 > 解决方案 > 如何在 Hibernate 中正确映射父子(同表)关系?

问题描述

我在尝试定义 aparent recordchild records父级之间的关系时遇到问题。我正在使用一个表,如果更新了一条记录,则插入一条新记录,并且旧记录变为非活动状态 -parent_id设置为id记录的。这是我的表的 CREATE 语句:

    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;

Entity在我的 java IDE 中有:

/*
 * 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) && (this.id == null || this.id.equals(other.id));
    }

    @Override
    public String toString() {
        return "com.vw.asa.entities.Templates[ id=" + id + " ]";
    }

}

我尝试将以下块添加到实体,但它生成了此块之后列出的错误:

    @JoinColumn(name = "parent_id", referencedColumnName = "id")
    @ManyToOne(optional = false,  fetch = FetchType.LAZY)
    private Integer parent;

为了补充这一点,我添加了以下 getter/setter:

    public Integer getParentId() {
        return getParent().getId();
    }

    public void setParentId(Integer parentId) {
        this.parent_id = parentId;
    }

这是编译过程中产生的错误:

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")

这显然与具有多个定义的 parent_id 有关,但是我如何构造该实体以便可以执行以下操作?

template.getParent().getId(); // returns parent_id
template.setParent(template); // set the parent object

是否有可以删除的方法/属性在这里是多余的?

编辑

需要注意的一件事是,我还没有明确定义父子关系如何工作的设计模式。

它最终可能是oldest记录将是父项,而记录newest是子项,或者current/master/active记录可能是父项,而树下的其余部分可能是子项。我还没有决定哪种方法是最好的方法。

标签: javahibernatespring-bootspring-mvcparent-child

解决方案


使用以下指定可插入和可更新的值。

@JoinColumn(referencedColumnName = "id", insertable = false, updatable = false)

这应该可以解决问题。


推荐阅读