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

Spring Boot中如何使用@JsonIgnore解决多对多关联序列化无限循环问题

问题根因

你当前在Product实体的listOfUsersForProduct字段上加了@JsonIgnore,导致查询产品时该字段会被直接忽略;如果去掉该注解,序列化时会出现「用户序列化关联产品→产品序列化关联用户」的无限递归问题,最终导致栈溢出。


方案1:使用@JsonIgnoreProperties做属性过滤(改造成本最低)

直接移除Product实体的@JsonIgnore,在两边的关联字段上分别添加@JsonIgnoreProperties过滤对方的关联属性,即可避免递归,且原有查询用户接口的返回逻辑完全不变。

代码修改示例:

User.java

@ManyToMany(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
@JoinTable( name = "user_product",
            joinColumns = @JoinColumn(name = "user_id"),
            inverseJoinColumns = @JoinColumn(name = "product_id"))
// 序列化时忽略产品对象中的用户关联字段
@JsonIgnoreProperties(value = "listOfUsersForProduct", allowSetters = true)
private List<Product> listOfProductsForUser;

Product.java

// 移除原有@JsonIgnore注解
@ManyToMany(fetch = FetchType.LAZY, cascade = CascadeType.ALL, mappedBy = "listOfProductsForUser")
// 序列化时忽略用户对象中的产品关联字段
@JsonIgnoreProperties(value = "listOfProductsForUser", allowSetters = true)
private List<User> listOfUsersForProduct;

注意:因为关联属性是懒加载,查询产品时需要主动加载关联的用户列表,避免懒加载异常。可在ProductRepository的查询方法上加注解:

@EntityGraph(attributePaths = "listOfUsersForProduct")
List<Product> findAll();

方案2:DTO/VO 分层封装(生产环境推荐)

不直接返回实体类给前端,定义专门的返回数据对象封装需要的字段,从根本上避免序列化递归问题,同时可以灵活控制返回字段,避免敏感字段泄露。

示例:

  1. 定义查询用户相关的DTO:
// 查询用户返回的完整DTO
public class UserDTO {
    private Long userId;
    private String userName;
    private Integer age;
    // 关联的产品简化信息,不需要嵌套用户列表
    private List<ProductSimpleDTO> productList;
    // 省略getter/setter
}

// 产品简化DTO,用于用户接口返回
public class ProductSimpleDTO {
    private Long productId;
    private String productName;
    // 省略getter/setter
}
  1. 定义查询产品相关的DTO:
// 查询产品返回的完整DTO
public class ProductDTO {
    private Long productId;
    private String productName;
    // 关联的用户简化信息,不需要再嵌套产品列表
    private List<UserSimpleDTO> userList;
    // 省略getter/setter
}

// 用户简化DTO,用于产品接口返回
public class UserSimpleDTO {
    private Long userId;
    private String userName;
    private Integer age;
    // 省略getter/setter
}
  1. 查询数据后手动或者用MapStruct等工具把实体类转换为对应DTO返回即可。

方案3:使用@JsonView控制序列化视图

通过定义不同的序列化视图,让同一个字段在不同接口返回时按需序列化,不需要新增额外类。

示例:

  1. 定义视图类:
public class View {
    // 查询用户接口的视图
    public interface UserView {}
    // 查询产品接口的视图
    public interface ProductView {}
}
  1. 实体类上加视图注解:

User.java

@JsonView(View.UserView.class)
private Long userId;
@JsonView(View.UserView.class)
private String userName;
@JsonView(View.UserView.class)
private Integer age;
@JsonView(View.UserView.class)
@ManyToMany(/* 原有配置保持不变 */)
private List<Product> listOfProductsForUser;

Product.java

// 产品基本字段两个视图都要返回
@JsonView({View.UserView.class, View.ProductView.class})
private Long productId;
@JsonView({View.UserView.class, View.ProductView.class})
private String productName;
// 关联用户只在查询产品视图返回,移除原有@JsonIgnore
@JsonView(View.ProductView.class)
@ManyToMany(/* 原有配置保持不变 */)
private List<User> listOfUsersForProduct;
  1. Controller接口上指定序列化视图:
// 查询用户接口
@GetMapping("/users")
@JsonView(View.UserView.class)
public List<User> listUsers() {
    return userRepository.findAll();
}

// 查询产品接口
@GetMapping("/products")
@JsonView(View.ProductView.class)
public List<Product> listProducts() {
    // 记得主动加载关联用户避免懒加载异常
    return productRepository.findAllWithUsers();
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.01 16:39:03