基于Spring REST与JPA实现主分类含子分类的JSON返回接口
解决方案:在Spring REST中返回包含子分类的主分类JSON
我来帮你实现这个需求,咱们一步步来:
1. 修改Category实体类,添加子分类关联
首先需要在Category实体里新增子分类的映射关系,同时处理JSON序列化时的循环引用问题:
@Where(clause = "active =1") @Entity @Table(name = "category", catalog = "businessin") public class Category implements java.io.Serializable { @Id @GeneratedValue(strategy = IDENTITY) @Column(name = "id", unique = true, nullable = false) private Integer id; private String name; private Integer parentId; private Integer active; private String pic; // 新增:映射子分类集合 @JsonManagedReference // 标记父端,序列化时会正常输出该字段 @OneToMany(fetch = FetchType.LAZY, mappedBy = "parentCategory") private List<Category> subCategories = new ArrayList<>(); // 新增:可选,映射父分类关系(如果业务需要) @JsonBackReference // 标记子端,序列化时自动忽略,避免循环引用死循环 @ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "parentId", insertable = false, updatable = false) private Category parentCategory; @JsonIgnore @OneToMany(fetch = FetchType.LAZY, mappedBy = "category") private List<Product> products = new ArrayList<Product>(); // 别忘了生成subCategories和parentCategory的getter/setter方法 }
这里的关键说明:
@JsonManagedReference和@JsonBackReference是Jackson用来解决双向关联序列化循环问题的注解,确保JSON生成时不会陷入死循环insertable = false, updatable = false是因为我们已经手动维护了parentId字段,不需要JPA来管理这个外键的插入更新,避免冲突
2. 优化CategoryRepository的查询方法
为了避免N+1查询问题,我们用Fetch Join一次性加载主分类和对应的子分类:
public interface CategoryRepository extends JpaRepository<Category, Integer> { // 查询所有主分类(parentId=0),并关联加载子分类 @Query("SELECT c FROM Category c LEFT JOIN FETCH c.subCategories WHERE c.parentId = 0") List<Category> findAllMainCategoriesWithSubCategories(); }
如果不需要一次性加载,也可以用简单的方法命名查询:List<Category> findByParentId(Integer parentId);,但这种方式在访问子分类时会触发懒加载,产生额外的数据库查询,推荐用Fetch Join的方式。
3. 在REST控制器中返回结果
最后在控制器里调用Repository的方法,直接返回主分类列表即可:
@RestController @RequestMapping("/api/categories") public class CategoryController { private final CategoryRepository categoryRepository; // 推荐构造注入,替代@Autowired public CategoryController(CategoryRepository categoryRepository) { this.categoryRepository = categoryRepository; } @GetMapping("/main") public ResponseEntity<List<Category>> getMainCategories() { List<Category> mainCategories = categoryRepository.findAllMainCategoriesWithSubCategories(); return ResponseEntity.ok(mainCategories); } }
这样返回的JSON结构就会和你期望的一致:
[ { "id": 1, "name": "Electronics", "parentId": 0, "active": 1, "pic": null, "subCategories": [ { "id": 2, "name": "Mobile", "parentId": 1, "active": 1, "pic": null }, { "id": 3, "name": "Laptops", "parentId": 1, "active": 1, "pic": null } ] } ]
额外可选方案:用DTO解耦实体与返回结构
如果不想修改实体类(比如担心影响现有业务逻辑),可以创建一个DTO类来封装返回数据:
public class CategoryWithSubDTO { private Integer id; private String name; private Integer parentId; private Integer active; private String pic; private List<CategoryWithSubDTO> subCategories; // 构造函数、getter/setter,或者用ModelMapper工具快速完成实体到DTO的转换 }
在Service层把Category实体转换为DTO后再返回,这样更灵活,也不会污染实体类的结构。
内容的提问来源于stack exchange,提问作者user9289559
相关产品推荐
相关产品推荐

