java - 如何在 Hibernate 中正确映射父子(同表)关系?
问题描述
我在尝试定义 aparent record
和child 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
记录可能是父项,而树下的其余部分可能是子项。我还没有决定哪种方法是最好的方法。
解决方案
使用以下指定可插入和可更新的值。
@JoinColumn(referencedColumnName = "id", insertable = false, updatable = false)
这应该可以解决问题。
推荐阅读
- python - 数据采集和处理架构
- c# - 为什么我可以看到提供延迟执行 (LINQ) 的操作的结果?
- react-native - 使用 RealmDB React Native 推送通知
- java - Android - 执行 AsyncTask 线程的问题
- php - Livewire/Alpine Dom 模板未更新
- reactjs - 使用缩放后 Highstock 返回平移
- swift - 停止在 Swift 中收听 NSEvent
- wordpress - 我可以使用导入功能更新 Woocommerce 中的可变产品吗?
- python - 将 python scipy.optimize.minimize 与评估值和梯度的函数一起使用
- android-studio - Android Studio 如何获取我使用 Volley 通过 post 方法请求的会话 cookie