Spring Data JPA Hibernate双向@OneToOne懒加载查询出现实体无限循环问题
是否会触发无限递归
默认不会,现有配置已经规避了绝大多数递归场景:
- 两个关联属性上都加了
@ToString.Exclude,调用实体的toString()方法时不会遍历关联属性,不会触发栈溢出 - 关联都是懒加载配置,只有主动调用getter才会触发关联属性的加载,代码不主动嵌套调用getter就不会出现递归
- 只有当你把实体直接序列化(比如返回JSON响应)、或者手动写了嵌套调用关联属性的逻辑时,才会触发无限递归。调试器里看到的循环是调试工具主动展开了懒加载代理导致的,属于调试阶段的视觉现象,不影响正常业务代码运行。
该现象是否正常
属于JPA实现的正常特性,不是代码bug。
产生原因
- 持久化上下文的对象一致性保证:Hibernate的一级缓存(持久化上下文)会保证同一个会话内,相同主键的实体只会存在一个实例。当你查询Employee时,该实例会被存入一级缓存,后续触发加载EmployeeIdCard时,Hibernate发现EmployeeIdCard的employee关联指向的就是缓存中已有的Employee实例,会直接赋值该引用,自然形成双向持有的循环引用。
- 双向关联的编码逻辑:双向关联本身就要求双方互相持有对方的引用,不管是Hibernate自动赋值还是你手动调用
employee.setEmployeeIdCard(card); card.setEmployee(employee);设置关联,都会形成循环引用,这是双向关联的固有特性。
修复方案
根据使用场景选择对应方案即可:
- 调试场景无需处理:调试器展示的循环不影响代码运行,不需要额外修改。
- JSON序列化场景:在其中一方的关联属性上加序列化忽略注解,比如使用Jackson时:
- 方案1:直接在其中一方加
@JsonIgnore,比如给EmployeeIdCard的employee属性加@JsonIgnore,序列化时就会忽略该字段 - 方案2:使用配对注解:在Employee的employeeIdCard属性上加
@JsonManagedReference,在EmployeeIdCard的employee属性上加@JsonBackReference,序列化时自动截断反向关联
- 方案1:直接在其中一方加
- 业务逻辑优化:如果不需要从EmployeeIdCard侧查询关联的Employee,直接删掉EmployeeIdCard类中的employee属性,改成单向关联即可从根源上消除循环。
- DTO转换:如果不想修改实体配置,在返回数据前把实体转换成DTO对象,只保留需要的字段,避免直接返回实体类。
内容的提问来源于stack exchange,提问作者RJB
相关产品推荐
相关产品推荐

