[英]Hibernate Many-To-One Relationship without Foreign Key but with reverse foreign key
[英]Why isn't the foreign key field of a Many-to-One relationship being set on insert?
我的Spring Web应用程序允许用户更新“员工”记录以更改字段或添加与此“员工”记录相关的新“电话”记录。 但是,在添加新的“电话”记录后提交“雇员”记录以进行更新时,将引发SQL错误异常。
问题在于,最终提交给数据库的SQL插入语句中未设置“电话”表上“雇员”表上的“ employee_id”外键。 但是,在已更新/合并的“ EmployeeEntity”对象引用的“ PhoneEntity” JPA实体对象中,与employee_id
数据库字段关联的属性不为null,而是将其设置为要更新/合并的“ EmployeeEnity”对象。
根据我对JPA的理解,在将实体记录的插入语句提交到数据库时,具有与数据库字段相关联的实体属性应该对其进行设置,但是在这种情况下,并不是导致此错误。
我尝试了调试器的逐步调试,并验证了创建的PhoneEntity
对象是EmployeeEntity
的phones
属性的成员,并且已将同一PhoneEntity
的employee
属性设置为相同的EmployeeEntity
对象(具有相同的对象)双向关系)。
我还设置了hibernate.show_sql=true
来查看正在提交到数据库的SQL语句,它包括该语句(省略号是更多字段):
Hibernate:
insert
into
phone
(id, employee_id, ...)
values
(?, ?, ...)
这意味着它将为新的PhoneEntity
对象插入新的phone
。
尝试运行此插入语句后,它给出SQL错误“列'employee_id'不能为空”。 但是,就像我之前说过的,我已经与调试器进行了检查,并且employee
属性确实设置为EmployeeEntity
对象。
这是我的代码的简化示例:
@Entity
@Table(name = "employee")
public class EmployeeEntity implements Serializable {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "id", unique = true, nullable = false)
private Integer id;
@OneToMany(mappedBy="employee", cascade = {CascadeType.PERSIST})
private Set<PhoneEntity> phones = new HashSet<>();
...
}
@Entity
@Table(name = "phone")
public class PhoneEntity implements Serializable {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "id", unique = true, nullable = false)
private Integer id;
@ManyToOne
@JoinColumn(name = "employee_id", nullable = false)
private EmployeeEntity employee;
...
}
具有具有以下SQL语句创建的结构的表。
CREATE TABLE employee (
id INT NOT NULL AUTO_INCREMENT PRIMARY KEY,
...
);
CREATE TABLE phone (
id INT NOT NULL AUTO_INCREMENT PRIMARY KEY,
employee_id INT NOT NULL,
...
FOREIGN KEY(employee_id) REFERENCES employee(id)
);
以下是它实际将更新提交给实体管理器以对数据库进行更新的位置。
public void update(EmployeeDomain employee) {
EmployeeEntity entity = employeeDomainToEntity.transform(employee)
getEntityManager().merge(entity);
}
EmployeeEntity
和PhoneEntity
对象是通过转换从http请求反过来反序列化的相似域对象而创建的。 我会在代码的这一部分中添加更多内容,但是,正如我已经提到的那样,我已经与调试器确认,提交给合并的实际实体对象已经采用了我们在phones
字段和employee
字段设置正确,因此最终实体应该正确。
在正式的JPA规范文档 (版本2.1)的“ 3.2.7.1合并分离的实体状态 ”部分(第85页)中,我们发现:
对于由具有级联元素值
cascade=MERGE
或cascade=ALL
X
的关系引用的所有实体Y
,Y
递归合并为Y'
。 对于所有这样的Y
通过引用X
,X'
被设置为引用Y'
。 (请注意,如果管理X,则X与X'是同一对象。)
这说明您在phones
字段的注释中缺少cascade=MERGE
。
如thanh ngo的答案中所建议,上述定义(或解释)因此转换为:
@OneToMany(mappedBy="employee", cascade = {CascadeType.PERSIST, CascadeType.MERGE})
private Set<PhoneEntity> phones = new HashSet<>();
或者,您也可以使用cascade=CascadeType.ALL
。 但是,这也将包括CascadeType.REMOVE
操作,这些操作可能并非总是如此。
希望能帮助到你。
我认为问题在于您正在使用合并。 实体的级联类型设置应为:
@OneToMany(mappedBy="employee", cascade = {CascadeType.PERSIST, CascadeType.MERGE})
private Set<PhoneEntity> phones = new HashSet<>();
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.