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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.15 19:50:00