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

如何解决Spring Boot中Jackson处理JPA复合主键的类型转换错误

问题根因
  • 直接使用JPA实体类作为接口请求体接收对象,Jackson反序列化时没有内置策略将请求传入的ID字符串转换为对应的Doctor/Shift关联实体
  • doctor、shift字段上配置的@JsonBackReference注解会强制Jackson在反序列化时忽略这两个字段,进一步导致字段无法赋值
  • @JsonIdentityInfo配置错误,property属性指定为doctor会将整个实体作为对象标识,不符合反序列化要求
最优解决方案(推荐)

使用DTO(数据传输对象)单独接收接口请求参数,不要直接暴露JPA实体作为入参,既可以规避反序列化问题,也可以避免内部实体结构对外暴露,降低耦合。
步骤如下:

  1. 定义请求DTO
@Data
public class ScheduleCreateRequest {
    @NotNull
    private Long doctorId;
    @NotNull
    private Long shiftId;
    @NotNull
    @JsonFormat(pattern = "dd-MM-yyyy")
    private LocalDate date;
}
  1. 调整Controller接口接收DTO
@PostMapping("/schedules")
public ResponseEntity<?> create(@RequestBody @Valid ScheduleCreateRequest request,
                                BindingResult bindingResult) {
    if (bindingResult.hasErrors()) {
        return new ResponseEntity<>(bindingResult.getAllErrors(), HttpStatus.BAD_REQUEST);
    }
    // 查询关联实体
    Doctor doctor = doctorRepository.findById(request.getDoctorId())
        .orElseThrow(() -> new RuntimeException("对应医生不存在"));
    Shift shift = shiftRepository.findById(request.getShiftId())
        .orElseThrow(() -> new RuntimeException("对应班次不存在"));
    // 组装业务实体
    ShiftDetail shiftDetail = new ShiftDetail();
    shiftDetail.setDoctor(doctor);
    shiftDetail.setShift(shift);
    shiftDetail.setDate(request.getDate());
    ShiftDetail result = shiftDetailService.create(shiftDetail);
    return new ResponseEntity<>(result, HttpStatus.CREATED);
}
  1. 可选:调整ShiftDetail实体的Jackson注解,避免序列化循环问题
    将@JsonBackReference替换为@JsonIgnoreProperties,同时修正@JsonIdentityInfo配置:
@JsonIdentityInfo(
        generator = ObjectIdGenerators.PropertyGenerator.class,
        property = "date"
)
public class ShiftDetail {
    @Id
    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "doctor_id")
    @NotNull
    // 忽略关联实体的懒加载代理属性,避免序列化报错
    @JsonIgnoreProperties({"hibernateLazyInitializer", "handler", "shiftDetails"})
    private Doctor doctor;
    @Id
    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "shift_id")
    @NotNull
    @JsonIgnoreProperties({"hibernateLazyInitializer", "handler", "shiftDetails"})
    private Shift shift;
    // 其余字段保持原有配置不变
}
临时解决方案(仅适合快速验证,不推荐生产使用)

如果不想重构使用DTO,可以自定义Jackson反序列化器,实现字符串ID到关联实体的转换:

  1. 定义Doctor反序列化器
@Component
public class DoctorDeserializer extends StdDeserializer<Doctor> {
    @Autowired
    private DoctorRepository doctorRepository;
    public DoctorDeserializer() {
        super(Doctor.class);
    }
    @Override
    public Doctor deserialize(JsonParser p, DeserializationContext ctxt) throws IOException {
        String id = p.getText();
        return doctorRepository.findById(Long.parseLong(id))
            .orElseThrow(() -> new RuntimeException("医生不存在"));
    }
}
  1. 给字段加上反序列化注解,同时移除原有@JsonBackReference注解:
@JsonDeserialize(using = DoctorDeserializer.class)
private Doctor doctor;

Shift字段同理配置对应反序列化器即可生效。

内容的提问来源于stack exchange,提问作者Bửu Đặng Hoàng

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.08 23:42:01