繁体   English   中英

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

[英]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对象是EmployeeEntityphones属性的成员,并且已将同一PhoneEntityemployee属性设置为相同的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);
    }

EmployeeEntityPhoneEntity对象是通过转换从http请求反过来反序列化的相似域对象而创建的。 我会在代码的这一部分中添加更多内容,但是,正如我已经提到的那样,我已经与调试器确认,提交给合并的实际实体对象已经采用了我们在phones字段和employee字段设置正确,因此最终实体应该正确。

在正式的JPA规范文档 (版本2.1)的“ 3.2.7.1合并分离的实体状态 ”部分(第85页)中,我们发现:

对于由具有级联元素值cascade=MERGEcascade=ALL X的关系引用的所有实体YY递归合并为Y' 对于所有这样的Y通过引用XX'被设置为引用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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM