为什么在插入时未设置多对一关系的外键字段?

时间:2019-05-17 19:29:28

标签: java hibernate jpa jpa-2.0 jpa-2.1

我的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); } 字段设置正确,因此最终实体应该正确。

2 个答案:

答案 0 :(得分:3)

在官方JPA specification document(2.1版)“ 3.2.7.1合并分离的实体状态”(第85页)中,我们发现:

  

对于从Y到具有级联元素值Xcascade=MERGE的关系引用的所有实体cascade=ALLY递归合并为{{1} }。对于Y'引用的所有此类YX设置为引用X'。 (请注意,如果管理X,则X与X'是同一对象。)

这说明您在Y'字段的注释中缺少cascade=MERGE

正如 thann ngo 的回答所建议的,上述定义(或解释)因此翻译为:

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<>();