Spring Boot一对一关联新增Task时出现反序列化错误求助
Spring Boot一对一关联新增Task时JSON反序列化错误解决
问题场景
开发Spring Boot项目,Student与Task为一对一关联,新增Task时出现JSON解析错误。
错误日志
2023-07-22 08:38:31.269 WARN 3500 --- [nio-8070-exec-4] .w.s.m.s.DefaultHandlerExceptionResolver : Resolved [org.springframework.http.converter.HttpMessageNotReadableException: JSON parse error: Cannot construct instance of `com.example.SpringAppliction.entity.Student` (although at least one Creator exists): no int/Int-argument constructor/factory method to deserialize from Number value (1); nested exception is com.fasterxml.jackson.databind.exc.MismatchedInputException: Cannot construct instance of `com.example.SpringAppliction.entity.Student` (although at least one Creator exists): no int/Int-argument constructor/factory method to deserialize from Number value (1) at [Source: (org.springframework.util.StreamUtils$NonClosingInputStream); line: 6, column: 21] (through reference chain: com.example.SpringAppliction.dto.TaskSaveDTO["student"])]
相关代码
Task控制器
@PostMapping(path = "save") public String saveTask(@RequestBody TaskSaveDTO taskSaveDTO) { String id = taskService.addTask(taskSaveDTO); return id; }
Entity - Task
@Id @Column(name = "task_id", length = 50) @GeneratedValue(strategy = GenerationType.AUTO) private int taskid; @Column(name="task_name") private String taskname; @Column(name="description") private String description; @Column(name="complete") private Boolean complete; @OneToOne @JoinColumn(name = "student_id") private Student student;
TaskSaveDTO
@AllArgsConstructor @NoArgsConstructor @Data public class TaskSaveDTO { private String taskname; private String description; private Boolean complete; private Student student; }
TaskService
public interface TaskService { String addTask(TaskSaveDTO taskSaveDTO); }
TaskServiceIMPL
public String addTask(TaskSaveDTO taskSaveDTO) { Task task = new Task( taskSaveDTO.getTaskname(), taskSaveDTO.getDescription(), taskSaveDTO.getComplete(), taskSaveDTO.getStudent() ); taskRepo.save(task); return task.getTaskname(); }
Entity - Student
@Id @GeneratedValue(strategy = GenerationType.AUTO) @Column(name="student_id", length = 50) private Long studentid; @Column(name="student_name", length = 50) private String studentname; @Column(name="address", length = 70) private String address; @Column(name="mobile", length = 12) private int mobile; @Column(name="activestate", columnDefinition = "TINYINT default 1") private boolean activestate; @OneToOne(mappedBy = "student") private Task task;
StudentSaveDTO
public class StudentSaveDTO { private Long studentid; private String studentname; private String address; private int mobile; private boolean activestate; }
StudentServiceIMPL
@Override public String addStudent(StudentSaveDTO studentSaveDTO) { Student student = new Student( studentSaveDTO.getStudentname(), studentSaveDTO.getAddress(), studentSaveDTO.getMobile(), studentSaveDTO.isActivestate() ); studentRepo.save(student); return student.getStudentname(); }
StudentController
@PostMapping(path = "save") public String saveStudent(@RequestBody StudentSaveDTO studentSaveDTO) { String id = studentService.addStudent(studentSaveDTO); return id; }
StudentRepo
@EnableJpaRepositories @Repository public interface StudentRepo extends JpaRepository<Student,Long > { }
TaskRepo
@EnableJpaRepositories @Repository public interface TaskRepo extends JpaRepository<Task,Long > { }
问题原因
错误核心是:Jackson无法将数字(比如前端传入的1)反序列化为Student实体对象。因为TaskSaveDTO中直接使用了Student实体类,而前端传递的是学生ID(数字),并非完整的Student JSON结构;同时Student实体没有提供接收单个Long类型参数的构造方法,导致Jackson无法完成转换。
解决方案
1. 修改TaskSaveDTO,用学生ID替代Student实体
将DTO中的Student字段替换为Long类型的studentId,符合前端传递ID的业务场景:
@AllArgsConstructor @NoArgsConstructor @Data public class TaskSaveDTO { private String taskname; private String description; private Boolean complete; private Long studentId; // 替换原有的Student student字段 }
2. 修改TaskServiceIMPL,通过ID查询学生并关联
在服务层根据传入的studentId从数据库查询对应的Student对象,再关联到Task:
@Autowired private StudentRepo studentRepo; @Override public String addTask(TaskSaveDTO taskSaveDTO) { // 根据ID查询学生,不存在则抛出异常 Student student = studentRepo.findById(taskSaveDTO.getStudentId()) .orElseThrow(() -> new RuntimeException("关联的学生不存在")); Task task = new Task( taskSaveDTO.getTaskname(), taskSaveDTO.getDescription(), taskSaveDTO.getComplete(), student ); taskRepo.save(task); return task.getTaskname(); }
3. 确保Task实体有对应构造方法
如果Task实体没有包含taskname、description、complete、student参数的构造方法,需要补充(或使用Lombok的@AllArgsConstructor注解)。
补充说明
在分层架构中,DTO的作用是传递数据,直接在DTO中使用实体类会耦合业务层与数据层,同时也不符合前端传递关联ID的常规场景。使用ID传递关联关系更简洁、高效,也避免了不必要的序列化/反序列化问题。
内容的提问来源于stack exchange,提问作者John Peter
相关产品推荐
相关产品推荐

