JPA将关联表复合主键改为Embeddable后save报id需手动赋值问题咨询
问题修复方案
核心错误原因
错误根源是Department类的addEmployee方法逻辑存在严重问题:
- 方法内部直接创建了无参
Employee实例,没有给该实例设置主键empNo,而你的Employee实体主键没有加@GeneratedValue注解,JPA要求必须手动赋值才能保存 - 方法直接覆盖了你传入的、已经配置好部门/员工/主键信息的
departmentEmployee对象,最终要级联保存的关联对象绑定的是这个无主键的新Employee实例,因此触发报错
修复步骤
1. 修正Department类的addEmployee方法
直接删除方法内无用的新实例创建逻辑,直接用传入的已配置好的关联对象,同时主动维护两端关联避免级联异常:
public void addEmployee(DepartmentEmployee departmentEmployee) { departmentEmployees.add(departmentEmployee); // 主动维护Employee侧的关联关系 departmentEmployee.getEmployee().getDepartmentEmployees().add(departmentEmployee); }
2. 可选优化:封装关联对象创建逻辑
如果想把关联对象的创建逻辑封装在实体内部,可以重载addEmployee方法,避免业务层重复写配置代码:
// 重载方法,直接传入员工和关联时间参数 public void addEmployee(Employee employee, LocalDate fromDate, LocalDate toDate) { DepartmentEmployee departmentEmployee = new DepartmentEmployee(this, employee); departmentEmployee.setFromDate(fromDate); departmentEmployee.setToDate(toDate); departmentEmployees.add(departmentEmployee); employee.getDepartmentEmployees().add(departmentEmployee); }
对应服务层代码可以简化为:
@Override @Transactional public Employee addEmployeeToDepartment(String deptNo, Long empNo, LocalDate fromDate, LocalDate toDate) { Department department = this.departmentRepository.findById(deptNo).orElseThrow(() -> new DepartmentNotFoundException(deptNo)); Employee employee = this.employeeRepository.findById(empNo).orElseThrow(() -> new EmployeeNotFoundException(empNo)); department.addEmployee(employee, fromDate, toDate); this.departmentRepository.save(department); return employee; }
3. 可选:检查Employee主键策略
如果你的业务中empNo是数据库自增生成的,需要给Employee的主键加上生成策略注解:
@Id @GeneratedValue(strategy = GenerationType.IDENTITY) // 按实际数据库类型调整为SEQUENCE等其他策略 private Long empNo;
如果是业务侧手动生成主键则不需要添加,只要保证保存前完成赋值即可。
验证
修改完成后再次调用保存接口,关联对象会绑定从数据库查询出来的、已有有效主键的Employee实例,不会再触发主键未赋值的报错。
内容的提问来源于stack exchange,提问作者Taulant Fazliu
相关产品推荐
相关产品推荐

