【问题标题】:Springboot 2.1.9.RELEASE - Persist Entities with Parent Child relationship using JPASpringboot 2.1.9.RELEASE - 使用 JPA 持久化具有父子关系的实体
【发布时间】:2026-01-06 10:20:05
【问题描述】:

我在 mysql 中的 EmployeeAddress 表之间存在一对多关系。我希望当我用子数据持久保存父级时,首先创建父级,并在子表中自动使用其parent_id。我收到以下错误。

任何帮助将不胜感激

父代码:

@Entity
@Table(name = "employee")
public class Employee implements Serializable {
    private static final long serialVersionUID = 1L;
    @Id
    @Column(length = 6)
    @GeneratedValue(strategy = GenerationType.IDENTITY) 
    private long id;

    @Temporal(TemporalType.DATE)
    private Date dob;
    
    @Column(name="email_address")
    private String emailAddress;
    
    @Column(name="first_name")
    private String firstName;

    @Column(name="gender")
    private String gender;


    @Column(name="last_name")
    private String lastName;
    
    @Column(name="user_id")
    private String userName;

    @Column(name="password")
    private String password;

    @Column(name="email_verified")
    private String emailVerification;
   
    @OneToMany(targetEntity = Address.class, mappedBy = "employee", orphanRemoval = true, 
   cascade = 
    CascadeType.ALL, fetch = FetchType.EAGER)
    private Set<Address> addresses = null;

    public Employee() {
    }

    public Employee(long id) {
        this.id = id;
    }

孩子:

   @Entity
   @Table(name = "address")
   public class Address implements Serializable {
    private static final long serialVersionUID = 1L;

    @Id
    @Column(length = 6)
    @GeneratedValue(strategy = GenerationType.IDENTITY)     
    private long id;

    @Basic
    private String address1;

    @Basic
    private String address2;


    @Basic
    private String city;

    @Basic
    private String zip;

    
    @Basic
    @Column(name = "emp_id")
    private String empId;   

    @ManyToOne(fetch = FetchType.LAZY, optional = false)
    @JoinColumn(name = "emp_id", nullable = false, updatable=false, insertable=false)
    @JsonIgnore
    private Employee employee;
    
    @Basic
    private int state_id;

    public Address() {
    }

    public Address(long id) {
        this.id = id;       
    }

    public long getId() {
        return this.id;
    }

控制器:

@CrossOrigin (origins = "*" )
@PostMapping("/employee")
public Employee createEmployee(@Valid @RequestBody Employee employee) {
    return employeeRepository.save(employee);
} 

JSON 输入:

{
  "dob": "2001-11-12",
  "emailAddress": "aaa@gmail.com",
  "firstName": "John",
  "gender": "M",
  "lastName": "Wilson",
  "userName": "jon",
  "password": "pass",
  "emailVerification": null,
  "addresses": [
    {
      "address1": "123 My Street",
      "address2": null,
      "city": "MyCity1",
      "zip": "11111",
      "stateId": 1
    },
    {
      "address1": "567 My Street",
      "address2": null,
      "city": "MyCity2",
      "zip": "22222",
      "stateId": 1
    }
  ]
}

错误

Hibernate: 
    insert 
    into
        address
        (address1, address2, city, emp_id, state_id, zip) 
    values
        (?, ?, ?, ?, ?, ?)
2021-04-28 22:17:19.067 RACE 25508 --- [nio-9090-exec-2] o.h.type.descriptor.sql.BasicBinder      : binding parameter [1] as [VARCHAR] - [567 My Street]
2021-04-28 22:17:19.067 TRACE 25508 --- [nio-9090-exec-2] o.h.type.descriptor.sql.BasicBinder      : binding parameter [2] as [VARCHAR] - [null]
2021-04-28 22:17:19.067 TRACE 25508 --- [nio-9090-exec-2] o.h.type.descriptor.sql.BasicBinder      : binding parameter [3] as [VARCHAR] - [MyCity2]
2021-04-28 22:17:19.067 TRACE 25508 --- [nio-9090-exec-2] o.h.type.descriptor.sql.BasicBinder      : binding parameter [4] as [VARCHAR] - [null]
2021-04-28 22:17:19.067 TRACE 25508 --- [nio-9090-exec-2] o.h.type.descriptor.sql.BasicBinder      : binding parameter [5] as [INTEGER] - [1]
2021-04-28 22:17:19.067 TRACE 25508 --- [nio-9090-exec-2] o.h.type.descriptor.sql.BasicBinder      : binding parameter [6] as [VARCHAR] - [22222]
2021-04-28 22:17:19.071  WARN 25508 --- [nio-9090-exec-2] o.h.engine.jdbc.spi.SqlExceptionHelper   : SQL Error: 1048, SQLState: 23000
2021-04-28 22:17:19.071 ERROR 25508 --- [nio-9090-exec-2] o.h.engine.jdbc.spi.SqlExceptionHelper   : Column 'emp_id' cannot be null
2021-04-28 22:17:19.094  WARN 25508 --- [nio-9090-exec-2] .m.m.a.ExceptionHandlerExceptionResolver : Resolved [org.springframework.dao.DataIntegrityViolationException: could not execute statement; SQL [n/a]; constraint [null];

【问题讨论】:

    标签: java mysql json jpa


    【解决方案1】:

    一种选择是遍历所有地址并手动设置employee 属性。您应该手动执行此操作,因为 JSON 反序列化不会触及该字段,即使没有 @JsonIgnore 注释也是如此。但这只是问题的表面。

    您正在对 employee 属性设置 updatable=false, insertable=false。还有另一个属性String empId 映射到同一列,表明您要手动管理关系。列/属性的类型应该和相关实体的PK/id一致,所以long!而且,鉴于 JSON 也不包含 empId,您仍然需要手动设置它;但你确实在创作时知道它!如果您删除Address.empId 属性AND updatable=false, insertable=false AND 手动设置employee,它应该可以工作。

    如果您希望 Jackson 为您执行此操作,您可以在 Employee.addresses 属性上使用 @JsonManagedReference,在 Address.employee 上使用 @JsonBackReference除了上一段中讨论的任务 em>(删除updatable=false, insertable=falseString empId 属性)AND 当然删除@JsonIgnore。详细说明见here

    @JsonManagedReference
    @OneToMany(targetEntity = Address.class, mappedBy = "employee", orphanRemoval = true, cascade = CascadeType.ALL, fetch = FetchType.EAGER)
    private Set<Address> addresses = null;
    
    @JsonBackReference
    @ManyToOne(fetch = FetchType.LAZY, optional = false)
    @JoinColumn(name = "emp_id", nullable = false)
    private Employee employee;
    

    免责声明:我反对混合 JPA 和 Jackson 注释。这确实很方便,因为您不需要单独的 DTO 类,不需要映射逻辑,也不需要担心进行实体 ↔ DTO 转换所需的 CPU/内存。另一方面,数据传输和数据持久性是两个完全不同的关注点,因此 SOLID 被淘汰了。并且有可能发送不应该被客户端看到的字段(例如,User 实体的密码或电子邮件)。

    【讨论】:

      最近更新 更多