springboot @OneToOne 解决JPA双向死循环/返回json数据死循环

项目场景:

在使用spring data jpa时,会用到@ManyToMany @ManyToOne @OneToMany @OneToOne,此时会有部分场景2个实体是平行的,如丈夫和妻子。


问题描述:

丈夫实体拥用妻子的实例,同样妻子也有丈夫的实例,那么层级就会一层层的往下循环,返回的json数据中就变成了嵌套死循环了。或者双向依赖死循环。

那么如何进行解决返回的json数据不会死循环?下面是示例代码

实体说明

@Entity
public class Husband{
	@OneToOne(cascade = CascadeType.ALL)
	@JoinColumn(name = "wife_id", referencedColumnName = "id")
	private Wife wife;
}

@Entity
public class Wife{
	@OneToOne(mappedBy = "wife")
	private Husband husband;
}

解决方案:

1. 转成DTO时并设置缺省

通常实体会有对应的DTO以对外或者前端进行展示数据,如果双向依赖的husband或者是wife是不会使用到的,在DTO建立时把可以把这个属性去掉

2. 使用@JsonIgnore

此方法与上述的同理,在husbandwife加上注解@JsonIgnore,则返回的数据中就不会包括此项属性,不会产生双向依赖的循环嵌套问题。

3. 使用@JsonIgnoreProperties(推荐)

此方案更合适,即可以看到关联到妻子/丈夫的实例,也不会产生死循环问题。如下代码

@Entity
public class Husband{
	@OneToOne(cascade = CascadeType.ALL)
	@JoinColumn(name = "wife_id", referencedColumnName = "id")
	@JsonIgnoreProperties({"husband"})
	private Wife wife;
}

@Entity
public class Wife{
	@OneToOne(mappedBy = "wife")
	@JsonIgnoreProperties({"wife"})
	private Husband husband;
}

@JsonIgnoreProperties({"husband"})
表示返回到前台的数据中,在wife实例中的husband属性不显示

反之
@JsonIgnoreProperties({"wife"})
表示返回到前台的数据中,在husband实例中的wife属性不显示

这样就解决问题了。
同样适应于其他双向依赖关联的实体注解,@ManyToMany @ManyToOne @OneToMany

### Spring Boot 启动时出现 `StackOverflowError` 的原因 `StackOverflowError` 是一种运行时异常,通常由递归调用过深或无限循环引起。在 Spring Boot 应用程序中,这种错误可能源于多种因素。 #### Lombok 使用中的潜在问题 当使用 Lombok 工具库时,可能会因为其自动生成的 getter 和 setter 方法引发意外行为。如果类之间存在复杂的相互依赖关系(例如双向关联),Lombok 自动生成的方法可能导致无限递归调用,从而触发 `StackOverflowError` [^2]。 #### 版本兼容性问题 某些情况下,Spring Boot 或其他相关依赖项之间的版本不匹配也可能导致此类错误。例如,在高版本的 Spring Boot 中可能存在尚未修复的 bug,或者与其他第三方库发生冲突。通过降级到更稳定的版本(如 2.1.5.RELEASE),可以有效解决问题 [^3]。 #### 双向 One-to-One 关联问题 在实体映射中,如果定义了双向的一对一 (`@OneToOne`) 关系而未正确处理序列化逻辑,则容易造成堆栈溢出。这是因为 Jackson 默认会尝试遍历整个对象图结构,而在遇到循环引用的情况下会产生无休止的递归调用 [^4]。 --- ### 解决方案 #### 方案一:调整 Lombok 配置 对于因 Lombok 导致的问题,可以通过显式声明方法来替代自动化的注解功能。具体做法如下: - **移除不必要的 `@ToString` 注解** 如果两个类互相持有对方实例变量并启用了 `@ToString(includeFieldNames=true)`,则会在打印日志时进入死循环。因此建议关闭此选项或将特定字段排除在外。 ```java import lombok.Data; import lombok.ToString; @Data public class Parent { private String name; @ToString.Exclude private Child child; // 排除子对象以防止循环引用 } ``` - **禁用 EqualsAndHashCode 自动实现** 类似于上述情况,默认生成 equals() 和 hashCode() 方法也有可能引入隐患。此时可以选择手动编写这些函数或是利用额外参数控制生成过程。 ```java import lombok.EqualsAndHashCode; import lombok.Getter; import lombok.Setter; @Getter @Setter @EqualsAndHashCode(exclude={"parent"}) public class Child { private int id; private Parent parent; // 被忽略掉参与比较运算的部分属性 } ``` #### 方案二:优化 JSON 处理策略 针对持久层设计中存在的嵌套关系,推荐采用以下方式之一规避风险: - **应用 Json 属性过滤机制** 借助 Jackson 提供的支持工具标记那些仅需单方向暴露的数据成员。 ```java import com.fasterxml.jackson.annotation.JsonBackReference; import com.fasterxml.jackson.annotation.JsonManagedReference; @Entity public class Department { @Id @GeneratedValue(strategy=GenerationType.IDENTITY) private Long departmentId; private String deptName; @OneToMany(mappedBy="department", cascade=CascadeType.ALL, fetch=FetchType.LAZY) @JsonManagedReference // 定义父节点端点 List<Employee> employeesList=new ArrayList<>(); } @Entity public class Employee { @Id @GeneratedValue(strategy=GenerationType.IDENTITY) private Long employeeId; private String empName; @ManyToOne(fetch= FetchType.EAGER) @JoinColumn(name="DEPARTMENT_ID") @JsonBackReference // 设置反向链接时不输出相关内容 private Department department; } ``` - **构建专门用于展示的信息载体 (DTO)** 创建独立的对象模型作为视图层交互媒介,避免直接操作原始数据库记录集。 ```java // DTO Example public record DepartmentDto(Long departmentId, String deptName){}; ``` 随后配合 MapStruct 等框架完成转换工作即可。 #### 方案三:验证依赖版本一致性 最后别忘了仔细核对自己项目的 POM 文件内容,确保选用恰当范围内的组件组合搭配起来测试效果最佳。 ```xml <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.1.5.RELEASE</version> <!-- 更改至稳定版 --> <type>pom</type> <scope>import</scope> </dependency> ``` --- ### 总结 综上所述,解决 Spring Boot 启动期间发生的 `StackOverflowError` 主要涉及三个方面的工作:一是排查是否存在不当使用的 Lombok 注解;二是改进基于 JPA/Hibernate 实现的关系型数据建模思路;三是确认所选技术栈各组成部分间能否良好协作运转正常[^1][^3][^4]。
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值