You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Spring Boot向含外键的Reimbursement发POST请求报错:emp_id不能为空

问题:新增报销记录时提示“Column 'emp_id' cannot be null”

我有employee和reimbursement两张表,二者为一对多关联关系。向reimbursement发送POST请求时,出现错误提示“Column 'emp_id' cannot be null”(emp_id为外键)。


实体类代码

Employee类

@Entity
@Table(name = "employee")
public class Employee {
    @Id
    @GeneratedValue (strategy = GenerationType.IDENTITY)
    @Column(name = "emp_id")
    private long empId;
    @Column(name = "name", nullable = false)
    private String name;
    @Column(name = "email", nullable = false, unique = true)
    private String email;
    @Column(name = "password", nullable = false)
    private String password;
    @Column(name = "manager", nullable = false)
    @Value("false")
    private boolean manager;

    @OneToMany(mappedBy = "employee",
            cascade = CascadeType.ALL,
            fetch = FetchType.EAGER,
            orphanRemoval = true
    )
    @JsonManagedReference
    private Set<Reimbursement> reimbursements = new HashSet<>();

    public long getEmpId() {
        return empId;
    }

    public void setEmpId(long empId) {
        this.empId = empId;
    }

    // (...) 其他getter和setter方法

    public Set<Reimbursement> getReimbursements() {
        return reimbursements;
    }

    public void setReimbursements(Set<Reimbursement> reimbursements) {
        this.reimbursements = reimbursements;
    }
}

Reimbursement类

@AllArgsConstructor
@NoArgsConstructor
@Entity
@Table(
        name = "reimbursements"
)
public class Reimbursement {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    @Column(name = "description", nullable = false)
    private String description;
    @Column(name = "amount", nullable = false)
    private double amount;
    @Column(name = "status", nullable = false)
    @Value("false")
    private boolean status; //费用是否已报销

    @ManyToOne
    @JoinColumn(name = "emp_id", nullable = false, updatable = false)
    @JsonBackReference
    private Employee employee;

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    //(...) 其他getter和setter方法

    public Employee getEmployee() {
        return employee;
    }

    public void setEmployee(Employee employee) {
        this.employee = employee;
    }

}

控制器代码

@PostMapping
public ResponseEntity<Reimbursement> createReimbursement(@RequestBody Reimbursement reimbursement) {
    var data = reimbursementService.createReimbursement(reimbursement);
    return new ResponseEntity<>(data, HttpStatus.CREATED);
}

服务层代码

@Override
public Reimbursement createReimbursement(Reimbursement reimbursement) {
    return reimbursementRepository.save(reimbursement);
}

发送的POST请求JSON(id为1的员工已存在)

{
  "employee": {
    "emp_id": "1"
  },
  "description": "example",
  "amount": "123",
  "status": "false"
}

问题原因及解决办法

  1. JSON字段名与实体类属性不匹配
    Employee类中主键属性是empId(小驼峰格式),但请求JSON里写的是emp_id(下划线格式)。Spring默认的Jackson序列化规则会匹配小驼峰属性,导致该字段无法正确映射,最终Employee对象的empId为long类型默认值0,Hibernate无法识别这个无效ID,导致外键emp_id为空。

    解决:修改请求JSON的字段名,并修正数据类型(amount是double不用加引号,status是boolean直接传布尔值):

    {
      "employee": {
        "empId": 1
      },
      "description": "example",
      "amount": 123,
      "status": false
    }
    
  2. 未使用持久化的Employee对象
    即使字段名正确,传入的只是一个仅含empId的临时Employee对象,不是从数据库加载的持久化实例。Hibernate保存Reimbursement时,会认为这个Employee是新对象,无法正确关联到已存在的员工记录,导致外键赋值失败。

    解决:在服务层先从数据库查询对应员工的持久化对象,再绑定到报销记录:

    @Override
    public Reimbursement createReimbursement(Reimbursement reimbursement) {
        // 查询数据库中已存在的员工
        Employee existingEmployee = employeeRepository.findById(reimbursement.getEmployee().getEmpId())
                .orElseThrow(() -> new RuntimeException("指定员工不存在"));
        // 将持久化员工对象设置到报销记录
        reimbursement.setEmployee(existingEmployee);
        return reimbursementRepository.save(reimbursement);
    }
    
  3. @Value注解误用
    实体类中manager和status字段使用的@Value("false")是Spring的配置注入注解,不能用来设置实体类默认值。正确做法是直接在字段声明时赋值:

    // Employee类中
    @Column(name = "manager", nullable = false)
    private boolean manager = false;
    
    // Reimbursement类中
    @Column(name = "status", nullable = false)
    private boolean status = false;
    

内容的提问来源于stack exchange,提问作者sharpes

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.07 23:35:54