Java Hibernate JPA父子映射
我有一个父表,即audit_log(parent),其中包含一个列id。对于audit_log中的给定id,我有一个供应商id列表。我将它们存储在单独的表audit\u log\u vendorid(子表)中。我希望子表从父表获取id作为列之一(parent_id)。这是表模式 审计日志Java Hibernate JPA父子映射,java,mysql,spring,hibernate,jpa,Java,Mysql,Spring,Hibernate,Jpa,我有一个父表,即audit_log(parent),其中包含一个列id。对于audit_log中的给定id,我有一个供应商id列表。我将它们存储在单独的表audit\u log\u vendorid(子表)中。我希望子表从父表获取id作为列之一(parent_id)。这是表模式 审计日志 +-------+------------+------+-----+---------+-------+ | Field | Type | Null | Key | Default | Extra
+-------+------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+-------+------------+------+-----+---------+-------+
| id | bigint(19) | NO | PRI | NULL | |
+-------+------------+------+-----+---------+-------+
审核日志供应商ID
+-----------+------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+-----------+------------+------+-----+---------+----------------+
| id | bigint(19) | NO | PRI | NULL | auto_increment |
| vendor_id | bigint(19) | NO | | NULL | |
| parent_id | bigint(19) | NO | | NULL | |
+-----------+------------+------+-----+---------+----------------+
我已经定义了我的hibernate类如下
@Entity
@Table(name="audit_log")
public class AuditLog {
private List<AuditVendorPair> vendorIDs;
public AuditLog(List<AuditVendorPair> vendorIds) throws Exception {
this.vendorIDs = vendorIDs;
}
@OneToMany(cascade=CascadeType.ALL)
@JoinTable(name = "audit_log_vendorid",
joinColumns = { @JoinColumn(name = "parent_id", referencedColumnName="id") })
public List<AuditVendorPair> getVendors() {
return vendorIDs;
}
@Id @Column(name="ID")
public Long getId() {
return super.getId();
}
public void setHostServices(List<AuditVendorPair> vendorIDs){
this.vendorIDs = vendorIDs;
}
}
我很想知道我的注释是否正确。我基本上希望audit\u log表中的id由hibernate填充到audit\u log\u vendorid表的parent\u id字段中。不,它们不正确
audit\u log\u vendorid
不是联接表。联接表是未映射到实体的表,它包含映射到其他表的两个关联实体的ID
在AuditVendorPair中也不应该有parent_id字段。这不仅是因为它不遵守Java命名约定,还因为它应该被一个对AuditLog的引用所取代,并映射为manytone
因此,简而言之,您应该有一个双向的OneToMany关联,如中所述进行映射。我认为您忽略了JPA中的一个关键概念,即实体是对象,因此您永远不会让实体直接使用ID引用其父对象,您将引用该对象(JPA在查询DB时将使用ID) AuditVendorPair使用实体引用AuditLog,由于这是一个实体关系,因此必须使用@JoinColumn来指定名称 JPA/Hibernate的一些最佳实践
- 规范不需要参数构造函数
- 不要为主键设置setter,除非您在代码中生成它
- 不能对字段使用基本体。但是,如果数据库强制NOTNULL,您应该在getter和setter中使用原语long,这样您的IDE就可以警告您有关NPE,而不是等待测试失败
- 使用集合而不是列表,因为列表意味着顺序
- Done在字段名中使用下划线,但在指定列名称时使用下划线
- 请注意getVendors()中的空检查和集合创建,这是为了在首次创建对象时避免NPE。这意味着您可以使用相同的逻辑添加AuditVendorPair,而不管AuditLog是刚刚创建的还是从DB加载的。这还意味着我不创建setVendors(),除非我需要替换整个列表(这种情况很少发生),并且在替换时,通常需要显式删除列表中的每个实体
EntityManagerFactory emf = Persistence.createEntityManagerFactory("PU-Name");
EntityManager em = emf.createEntityManager();
try {
long primaryKey = 1L; // comes from somewhere else
long vendorId = 1L; // comes from somewhere
AuditLog log = em.find(AuditLog.class, primaryKey); // loaded from DB
AuditVendorPair pair = new AuditVendorPair();
pair.setAuditLog(log);
pair.setVendorId(vendorId);
em.getTransaction().begin();
em.persist(pair);
em.getTransaction().commit();
} finally {
em.close();
}
您必须在代码中连接实体,并且必须从数据库中加载任何现有实体。如果要存储新的AuditVendorPair,首先必须加载AuditLog对象,然后在新创建的AuditVendorPair上进行设置。通常情况下,供应商也是一个实体,因此也必须对其进行查找
注意:上面的示例没有维护AuditLog和AuditVendorPair之间的双向关系,因为AuditVendorPair没有添加到AuditLog上的供应商集合中。由于定义关系的列位于AuditVendorPair(已存储)上,因此这不是问题,因为下次从DB加载AuditLog实例时,AuditVendorPair将是供应商集合的一部分。但是,如果在关闭持久性上下文后使用AuditLog,则可能需要维护双向关系
注意:上面的示例假设您收到带有主键的请求。一般来说,您不应该在任何前端公开主键,在我们的系统中,我们为在UI中公开的每个实体生成一个唯一字段(UUID)。是的,audit\u log\u vendorid不是联接表。引用带有ManyToOne映射的AuditLog的原因是什么?我以为单向的“一个女人”就行了。使用外键,我们是否指示休眠,它需要从引用的列获取parent_id的值?如果我在audit\u log表中的主键大于ID,情况会发生变化吗?您可以使用单向OneToMany,但audit\u log\u vendorid中的父ID列将由OneToMany关联处理(您需要
@JoinColumn(“父ID”)
),并且不应在AuditVendorPair实体中进行第二次映射。如果您需要从AuditVendorPair实体访问父级,那么它应该是双向的。谢谢您提供的信息。当我尝试持久化AuditLog时,我得到了以下结果:com.mysql.jdbc.exceptions.jdbc4.MySQLIntegrityConstraintViolationException:Column'audit\u id'不能为null是否需要将外键引用从子父级\u id添加到父级id列?
@Entity
@Table(name="audit_log")
public class AuditLog {
@OneToMany(cascade= CascadeType.ALL, mappedBy = "auditLog")
private Collection<AuditVendorPair> vendorIDs;
@Id @Column(name="id")
private Long id;
public AuditLog() {
}
public Collection<AuditVendorPair> getVendors() {
if (vendorIDs == null) {
vendorIDs = new ArrayList<>();
}
return vendorIDs;
}
public long getId() {
return id;
}
}
@Entity
@Table(name = "audit_log_vendorid")
public class AuditVendorPair {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "id")
private Long id;
@JoinColumn(nullable = false, name = "parent_id")
@ManyToOne(optional = false)
private AuditLog auditLog;
@Column(name = "vendor_id")
private Long vendorId;
public AuditVendorPair() {
}
public long getVendorId() {
return vendorId;
}
public void setVendorId(long vendorId) {
this.vendorId = vendorId;
}
public AuditLog getAuditLog() {
return auditLog;
}
public void setAuditLog(AuditLog auditLog) {
this.auditLog = auditLog;
}
}
EntityManagerFactory emf = Persistence.createEntityManagerFactory("PU-Name");
EntityManager em = emf.createEntityManager();
try {
long primaryKey = 1L; // comes from somewhere else
long vendorId = 1L; // comes from somewhere
AuditLog log = em.find(AuditLog.class, primaryKey); // loaded from DB
AuditVendorPair pair = new AuditVendorPair();
pair.setAuditLog(log);
pair.setVendorId(vendorId);
em.getTransaction().begin();
em.persist(pair);
em.getTransaction().commit();
} finally {
em.close();
}