我的Spring Web应用程序允许用户更新“员工”记录以更改字段或添加与此“员工”记录相关的新“电话”记录。但是,当在添加新的“电话”记录后提交“雇员”记录以进行更新时,它将引发SQL错误异常。
问题在于,最终提交给数据库的SQL插入语句中未设置“电话”表上“雇员”表上的“ employee_id”外键。但是,在已更新/合并的“ EmployeeEntity”对象引用的“ PhoneEntity” JPA实体对象中,与employee_id
数据库字段关联的属性不为空,它设置为要更新的“ EmployeeEnity”对象/合并。
根据我对JPA的理解,在将实体记录的插入语句提交到数据库时,具有与数据库字段相关联的实体属性应该对其进行设置,但是在这种情况下,并不是导致此错误。 / p>
我尝试了调试器的逐步调试,并验证了创建的PhoneEntity
对象是EmployeeEntity
的{{1}}属性的成员,并且同样是{{ 1}}的{{1}}属性以双向关系设置为相同的phones
对象(具有相同的对象ID)。
我还设置了PhoneEntity
来查看将SQL语句提交到数据库,并且其中包含该语句(省略号是更多字段):
employee
这意味着它将为新的EmployeeEntity
对象插入新的hibernate.show_sql=true
。
尝试运行此插入语句后,它显示SQL错误“列'employee_id'不能为空”。但是,就像我之前说的,我已经与调试器进行了检查,并且Hibernate:
insert
into
phone
(id, employee_id, ...)
values
(?, ?, ...)
属性确实设置为phone
对象。
这是我的代码的简化示例:
PhoneEntity
使用具有以下SQL语句创建的结构的表。
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;
...
}
和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)
);
对象是通过转换类似的域对象创建的,这些对象又从http请求中反序列化。我将在代码的这一部分中添加更多内容,但是,正如我已经提到的,我已经与调试器确认,提交给合并的实际实体对象已经是我们期望的{{1 }}字段和 public void update(EmployeeDomain employee) {
EmployeeEntity entity = employeeDomainToEntity.transform(employee)
getEntityManager().merge(entity);
}
字段设置正确,因此最终实体应该正确。
答案 0 :(得分:3)
在官方JPA specification document(2.1版)“ 3.2.7.1合并分离的实体状态”(第85页)中,我们发现:
对于从
Y
到具有级联元素值X
或cascade=MERGE
的关系引用的所有实体cascade=ALL
,Y
递归合并为{{1} }。对于Y'
引用的所有此类Y
,X
设置为引用X'
。 (请注意,如果管理X,则X与X'是同一对象。)
这说明您在Y'
字段的注释中缺少cascade=MERGE
。
phones
或者,您也可以使用@OneToMany(mappedBy="employee", cascade = {CascadeType.PERSIST, CascadeType.MERGE})
private Set<PhoneEntity> phones = new HashSet<>();
。但是,这还包括诸如cascade=CascadeType.ALL
之类的操作,这些操作可能并非始终如此。
希望有帮助。
答案 1 :(得分:1)
我认为问题在于您正在使用合并。 实体的级联类型设置应为:
@OneToMany(mappedBy="employee", cascade = {CascadeType.PERSIST, CascadeType.MERGE})
private Set<PhoneEntity> phones = new HashSet<>();