fixed-bug:JPA 关联关系的对象序列化循环引用问题
系列文章目录
文章目录
- 系列文章目录
- 前言
- 一、JPA-的关联关系映射:
- 二、JSON 序列化问题:
- 2.1 通过Jpa 获取到的对象会出现栈溢出现象
- 2.2 JSON.toJSONString() 会将 关联的对象一起输出
- 2.3 对象中的getXXX 方法也会输出,即使没有XXX 属性
- 三、处理方式:
- 3.1 对于不必要的字段不进行序列化:
- 总结
前言
在使用 FastJSON 序列化包含 @OneToMany、@ManyToMany 等 JPA 关联关系的对象时,出现循环嵌套问题会导致栈溢出或 JSON 结构无限嵌套。
一、JPA-的关联关系映射:
JPA(Java Persistence API)的关联关系映射用于描述实体之间的关系(如一对一、一对多、多对一、多对多),通过注解实现对象模型与数据库表结构的映射。
二、JSON 序列化问题:
2.1 通过Jpa 获取到的对象会出现栈溢出现象
在使用 JpaRepository 获取包含关联关系的对象时出现栈溢出(StackOverflowError),双向关联导致的循环引用引起的。当两个实体相互引用(如 @OneToMany 和 @ManyToOne 双向关联),在序列化或打印对象时会触发无限递归,最终导致栈溢出。
// 订单实体
@Entity
public class Order {@Idprivate Long id;@ManyToOneprivate User user; // 引用用户(多对一)// getter/setter
}// 用户实体
@Entity
public class User {@Idprivate Long id;@OneToMany(mappedBy = "user")private List<Order> orders; // 引用订单列表(一对多)// getter/setter
}
当执行 userRepository.findById(1L) 获取用户对象时:
- JPA 会加载 User 对象及其关联的 orders 列表(若为即时加载)。
- 每个 Order 对象又会引用回 User 对象。
- 当尝试序列化(如返回给前端)或打印对象时,会出现 User → orders → Order → user → User → … 的无限递归,最终导致栈溢出。
2.2 JSON.toJSONString() 会将 关联的对象一起输出
FastJSON 的 JSON.toJSONString() 方法在默认情况下会递归序列化对象的所有关联属性,包括 JPA 中通过 @OneToMany、@ManyToOne 等注解定义的关联对象。如果存在双向关联(如 A 包含 B,B 又包含 A),这种递归序列化会导致无限循环,最终抛出 StackOverflowError(栈溢出);
但是 FastJSON 默认会检测循环引用,并在 JSON 中用 KaTeX parse error: Expected '}', got 'EOF' at end of input: ref 标记(如 {"ref":“$.orders[0].user”}),避免无限递归。
2.3 对象中的getXXX 方法也会输出,即使没有XXX 属性
在 FastJSON 中,JSON.toJSONString() 序列化对象时,默认会优先通过 getter 方法(getXXX())获取属性值,而不仅仅依赖对象的字段(成员变量)。这就是为什么即使实体类中没有 XXX 字段,但只要存在 getXXX() 方法,序列化结果中就会出现 xxx 键(方法名去除 get 后首字母小写)。
原因解析
FastJSON 的序列化逻辑遵循 “** getter 优先 **” 原则,具体规则:
遍历类中所有的 getXXX() 方法(方法名以 get 开头,且无参数、返回值不为 void)。
对方法名进行转换:去除 get 后,将首字母小写,作为 JSON 中的键(例如 getFullName() → 键为 fullName)。
调用该 getter 方法获取值,并写入 JSON 字符串。
字段(成员变量)是否存在不影响,只要 getter 方法存在,就会被序列化
三、处理方式:
3.1 对于不必要的字段不进行序列化:
使用 @JSONField(serialize = false) 注解标记 getter 方法,或者标准字段属性;
注意事项:
- 仅对 FastJSON 生效:该注解是 FastJSON 的专属注解,若项目中使用其他序列化工具(如 Jackson),需要改用对应工具的注解(如 Jackson 的 @JsonIgnore)。
- 反序列化不受影响:serialize = false 仅控制 “对象→JSON” 的序列化过程,不影响 “JSON→对象” 的反序列化(即前端传入该字段时,仍可正常解析到对象中)。若需同时排除反序列化,可添加 deserialize = false
总结
在使用 FastJSON 序列化包含 @OneToMany、@ManyToMany 等 JPA 关联关系的对象时,核心问题是避免循环引用(如 A 包含 B,B 又包含 A)导致的栈溢出或 JSON 结构无限嵌套。
