首页 > 解决方案 > Springboot 2.1.9.RELEASE - 使用 JPA 持久化具有父子关系的实体

问题描述

我在mysql中的表Employee和表之间有一对多的关系。Address我希望当我使用子数据持久保存父级时,首先创建父级并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];

标签: javamysqljsonjpa

解决方案


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

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

如果您希望 Jackson 为您执行此操作,除了上一段中讨论的任务(删除和属性)删除当然之外,您可以@JsonManagedReferenceEmployee.addresses属性和@JsonBackReference上使用。在此处查看详细说明:Address.employeeupdatable=false, insertable=falseString empId@JsonIgnore

@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实体的密码或电子邮件)。


推荐阅读