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

Spring Rest API:调用getDoctorByName时Doctor的categories返回空数组

Spring Rest API查询医生时categories字段返回空数组问题排查与修复

问题描述

调用GET接口getDoctorByName查询医生信息时,除categories字段外,医生其他信息均正常返回,但categories始终返回空数组。医生记录创建、分类创建及为医生添加分类的功能均正常,数据库中已存在关联数据。

相关代码

Doctor实体类

@Data
@AllArgsConstructor
@NoArgsConstructor
@Entity
@Table(name = "doctor")
public class Doctor {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    private String email;
    private String password;
    @Column(name = "first_name")
    private String fname;
    @Column(name = "last_name")
    private String lname;
    private String description;
    private String number;
    @ManyToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER)
    @JoinTable(
            name = "doctor_category",
            joinColumns = @JoinColumn(name = "doctor_id"),
            inverseJoinColumns = @JoinColumn(name = "category_id")
    )
    private Set<Category> categories;
}

Category实体类

@Data
@AllArgsConstructor
@NoArgsConstructor
@Entity
@Table(name = "category")
public class Category {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    private String name;
    @ManyToMany(mappedBy = "categories", fetch = FetchType.LAZY)
    private Set<Doctor> doctors;
}

Repository层代码

@Repository
public interface CategoryRepository extends JpaRepository<Category, Long> {
    List<Category> findByName(String name);
}

@Repository
public interface DoctorRepository extends JpaRepository<Doctor, Long> {
    @EntityGraph(attributePaths = "categories")
    List<Doctor> findByFnameAndLname(String fname, String lname);
    @EntityGraph(attributePaths = "categories")
    List<Doctor> findByFname(String fname);
    @EntityGraph(attributePaths = "categories")
    List<Doctor> findByLname(String lname);
}

Category服务类

@Service
public class CategoryService {
    @Autowired
    private CategoryRepository categoryRepository;

    public Category createCategory(Category category) {
        return categoryRepository.save(category);
    }

    public Category editCategory(Long categoryId, Category myCategory) {
        Category category = categoryRepository.findById(categoryId)
                .orElseThrow(() -> new ResourceNotFoundException("ID为: " + categoryId + " 的分类不存在"));
        category.setName(myCategory.getName());
        categoryRepository.save(category);
        return category;
    }

    public List<Category> getCategoryByName(String name) {
        List<Category> categories;
        if (name != null) {
            categories = categoryRepository.findByName(name);
        } else {
            categories = categoryRepository.findAll();
        }
        return categories;
    }

    public HttpStatus deleteCategory(Long categoryId) {
        categoryRepository.deleteById(categoryId);
        return HttpStatus.OK;
    }
}

Doctor服务类

@Service
public class DoctorService {
    @Autowired
    private DoctorRepository doctorRepository;
    @Autowired
    private CategoryRepository categoryRepository;

    public Doctor createDoctor(Doctor doctor) {
        return doctorRepository.save(doctor);
    }

    public Doctor editDoctor(Long doctorId, Doctor myDoctor) {
        Doctor doctor = doctorRepository.findById(doctorId)
                .orElseThrow(() -> new ResourceNotFoundException("ID为: " + doctorId + " 的医生不存在"));
        doctor.setEmail(myDoctor.getEmail());
        doctor.setPassword(myDoctor.getPassword());
        doctor.setFname(myDoctor.getFname());
        doctor.setLname(myDoctor.getLname());
        doctor.setDescription(myDoctor.getDescription());
        doctor.setNumber(myDoctor.getNumber());
        return doctorRepository.save(doctor);
    }

    public Doctor addCategoryForDoctor(Long doctorId, Long categoryId) {
        Doctor doctor = doctorRepository.findById(doctorId)
                .orElseThrow(() -> new ResourceNotFoundException("ID为: " + doctorId + " 的医生不存在"));
        Category category = categoryRepository.findById(categoryId)
                .orElseThrow(() -> new ResourceNotFoundException("ID为: " + categoryId + " 的分类不存在"));
        Set<Category> categories = doctor.getCategories();
        categories.add(category);
        doctor.setCategories(categories);
        return doctorRepository.save(doctor);
    }

    public List<Doctor> getAllDoctors() {
        List<Doctor> doctors = doctorRepository.findAll();
        for (Doctor doctor : doctors) {
            // 调用getCategories()加载关联分类
            doctor.getCategories().size();
        }
        return doctors;
    }

    public List<Doctor> getDoctorByName(String fname, String lname) {
        List<Doctor> doctors;
        if (fname != null && lname != null) {
            doctors = doctorRepository.findByFnameAndLname(fname, lname);
        } else if (fname != null) {
            doctors = doctorRepository.findByFname(fname);
        } else if (lname != null) {
            doctors = doctorRepository.findByLname(lname);
        } else {
            doctors = doctorRepository.findAll();
        }
        return doctors;
    }

    public HttpStatus deleteDoctor(Long doctorId) {
        doctorRepository.deleteById(doctorId);
        return HttpStatus.OK;
    }
}

Doctor控制器

@RestController
@RequestMapping("/doctor")
public class DoctorController {
    @Autowired
    private DoctorService doctorService;

    @PostMapping
    public ResponseEntity<Doctor> createDoctor(@RequestBody Doctor doctor) {
        return ResponseEntity.ok(doctorService.createDoctor(doctor));
    }

    @PutMapping
    public ResponseEntity<Doctor> editDoctor(@RequestParam Long doctorId, @RequestBody Doctor doctor) {
        return ResponseEntity.ok(doctorService.editDoctor(doctorId, doctor));
    }

    @PutMapping("/add/category")
    public ResponseEntity<Doctor> addCategoryForDoctor(@RequestParam Long doctorId, @RequestParam Long categoryId) {
        return ResponseEntity.ok(doctorService.addCategoryForDoctor(doctorId, categoryId));
    }

    @GetMapping("/all")
    public ResponseEntity<List<Doctor>> getAllDoctors() {
        return ResponseEntity.ok(doctorService.getAllDoctors());
    }

    @GetMapping
    public ResponseEntity<List<Doctor>> getDoctorByName(@RequestParam(required = false) String fname,
                                                        @RequestParam(required = false) String lname) {
        return ResponseEntity.ok(doctorService.getDoctorByName(fname, lname));
    }

    @DeleteMapping
    public ResponseEntity<HttpStatus> deleteDoctor(@RequestParam Long doctorId) {
        return ResponseEntity.ok(doctorService.deleteDoctor(doctorId));
    }
}

Category控制器

@RestController
@RequestMapping("/category")
public class CategoryController {
    @Autowired
    private CategoryService categoryService;

    @PostMapping
    public ResponseEntity<Category> createCategory(@RequestBody Category category) {
        return ResponseEntity.ok(categoryService.createCategory(category));
    }

    @PutMapping
    public ResponseEntity<Category> editCategory(@RequestParam Long categoryId, @RequestBody Category category) {
        return ResponseEntity.ok(categoryService.editCategory(categoryId, category));
    }

    @GetMapping
    public ResponseEntity<List<Category>> getCategoryByName(@RequestParam(required = false) String name) {
        return ResponseEntity.ok(categoryService.getCategoryByName(name));
    }

    @DeleteMapping
    public ResponseEntity<HttpStatus> deleteCategory(@RequestBody Long categoryId) {
        return ResponseEntity.ok(categoryService.deleteCategory(categoryId));
    }
}

问题原因及修复方案

1. 双向关联导致循环序列化

Category实体中的doctors集合与Doctor实体中的categories集合形成双向关联,Jackson序列化Doctor对象时会触发循环引用,最终导致categories字段被自动置为空数组。

修复: 在Category实体的doctors字段上添加@JsonIgnore注解,阻止Jackson序列化该反向关联:

@ManyToMany(mappedBy = "categories", fetch = FetchType.LAZY)
@JsonIgnore
private Set<Doctor> doctors;

2. findAll()方法未加载关联数据

在getDoctorByName方法的分支中,当未传入fname和lname时会调用doctorRepository.findAll(),但该方法未配置@EntityGraph,即使Doctor实体的categories设置为FetchType.EAGER,JPA默认的findAll()查询也可能不会主动加载关联数据。

修复: 在DoctorRepository中覆盖findAll()方法并添加@EntityGraph:

@Repository
public interface DoctorRepository extends JpaRepository<Doctor, Long> {
    // 原有方法...
    
    @Override
    @EntityGraph(attributePaths = "categories")
    List<Doctor> findAll();
}

可选优化

getAllDoctors()中手动调用doctor.getCategories().size()触发懒加载的方式可以移除,因为添加了带@EntityGraph的findAll()方法后,关联数据会被一次性加载,性能更优:

public List<Doctor> getAllDoctors() {
    return doctorRepository.findAll();
}

内容的提问来源于stack exchange,提问作者Timo4ka

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.27 19:47:03