Spring Boot中Employee与Task一对一关系配置及序列化报错排查
问题描述
作为Spring Boot新手,在配置Employee与Task的一对一关联关系时,添加数据环节出现JSON反序列化错误,报错信息如下:
2023-03-28 13:24:46.853 WARN 8676 --- [nio-8082-exec-1] .w.s.m.s.DefaultHandlerExceptionResolver : Resolved [org.springframework.http.converter.HttpMessageNotReadableException: JSON parse error: Cannot construct instance of
com.example.SpingProject.Entity.Employee(although at least one Creator exists): no String-argument constructor/factory method to deserialize from String value ('2'); nested exception is com.fasterxml.jackson.databind.exc.MismatchedInputException: Cannot construct instance ofcom.example.SpingProject.Entity.Employee(although at least one Creator exists): no String-argument constructor/factory method to deserialize from String value ('2')at [Source: (org.springframework.util.StreamUtils$NonClosingInputStream); line: 4, column: 18] (through reference chain: com.example.SpingProject.DTO.TaskSaveDTO["employee"])]
相关代码
Task实体类
@Entity @Table(name="task") public class Task { @Id @Column(name = "task_id", length = 50) @GeneratedValue(strategy = GenerationType.AUTO) private int taskid; @Column(name = "task_name", length = 50) private String taskname; @Column(name = "description", length = 80) private String description; @OneToOne private Employee employee; }
Employee实体类
@Entity @Table(name="employee") public class Employee { @Id @Column(name = "employee_id", length = 50) @GeneratedValue(strategy = GenerationType.AUTO) private int employeeid; @Column(name = "employee_name", length = 50) private String employeename; @Column(name = "employee_address", length = 80) private String employeeaddress; @Column(name = "employee_mobile", length = 80) private int mobile; }
TaskServiceIMPL业务层代码
public String addTask(TaskSaveDTO taskSaveDTO) { Task task = new Task( taskSaveDTO.getTaskname(), taskSaveDTO.getDescription(), taskSaveDTO.getEmployee() ); taskRepo.save(task); return task.getTaskname(); }
问题原因
报错核心是:Jackson尝试将字符串'2'反序列化为Employee对象,但Employee类没有接收String类型参数的构造方法或工厂方法。这说明前端传递的JSON中,employee字段的值是字符串形式的员工ID(如"2"),但后端TaskSaveDTO里的employee字段是Employee类型,Jackson无法直接将字符串ID转换为Employee对象。
解决方案
方案1:调整DTO字段,用员工ID关联(推荐)
- 修改TaskSaveDTO,将
Employee employee替换为Integer employeeId - 业务层中通过ID查询对应Employee对象,再关联到Task:
public String addTask(TaskSaveDTO taskSaveDTO) { // 根据ID查询员工,不存在则抛出异常 Employee employee = employeeRepo.findById(taskSaveDTO.getEmployeeId()) .orElseThrow(() -> new RuntimeException("指定员工不存在")); Task task = new Task( taskSaveDTO.getTaskname(), taskSaveDTO.getDescription(), employee ); taskRepo.save(task); return task.getTaskname(); }
此时前端传递的JSON示例:
{ "taskname": "测试任务", "description": "任务详情", "employeeId": 2 }
方案2:配置Jackson支持ID反序列化关联对象
- 在Employee实体类添加注解,让Jackson识别ID作为对象标识:
@Entity @Table(name="employee") @JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator.class, property = "employeeid") public class Employee { // 原有字段保持不变 }
- 完善Task的关联映射配置(可选,避免关联查询问题):
@OneToOne @JoinColumn(name = "employee_id") // 指定数据库外键列名 private Employee employee;
同时需要确保Employee和Task实体都有无参构造方法(JPA要求实体必须提供无参构造,Jackson序列化也依赖此)。
方案3:添加String参数构造方法(不推荐)
给Employee添加接收String类型ID的构造方法,但这种方式容易引发类型转换问题,不符合代码规范,仅作临时参考:
public Employee(String employeeid) { this.employeeid = Integer.parseInt(employeeid); }
关键注意事项
- JPA实体类必须提供无参构造方法,否则Jackson序列化和JPA持久化都会出现异常,记得给Task和Employee补充无参构造。
- 前端传递关联对象时,优先传递ID而非完整对象,既减少数据传输量,也能避免大部分序列化/反序列化问题。
内容的提问来源于stack exchange,提问作者tutusfun

