Hibernate一对一关联出现无限递归(StackOverflowError)求助
嘿,我知道你现在被一对一关联的无限递归问题搞头大了——毕竟之前一对多的问题用@JsonManagedReference和@JsonBackReference解决了,没想到一对一也踩了同样的坑。别担心,这几个方法能帮你搞定!
问题根源
你遇到的StackOverflowError本质是Jackson序列化时的循环引用:Employee里持有Task的引用,Task又反过来持有Employee的引用,序列化时Jackson会不断在两个对象之间来回解析,最终耗尽栈内存。
下面是几种实用的解决方案,你可以根据自己的需求选择:
方案1:复用@JsonManagedReference和@JsonBackReference
这对注解不仅适用于一对多,一对一关联同样能用!核心是指定序列化的方向:
@JsonManagedReference:标记“主动”的一方,序列化时会正常包含关联对象@JsonBackReference:标记“被动”的一方,序列化时会被忽略,避免循环
你的场景里,Employee是关联的主控方(因为它有@JoinColumn指定外键),所以修改代码如下:
修改后的Employee.java
@Entity @Table(name = "employee") public class Employee { int id; @NotNull String firstName; private Task taskRefference; public Employee() { } public Employee(String firstName) { this.firstName = firstName; } @OneToOne(cascade = CascadeType.ALL) @JoinColumn(name = "task_id") @JsonManagedReference // 主控方加这个注解 public Task getTaskRefference() { return taskRefference; } public void setTaskRefference(Task taskRefference) { this.taskRefference = taskRefference; } }
修改后的Task.java
@Entity @Table(name = "task") public class Task { int id; @NotNull String title; @NotNull String assignees; private Employee employee; public Task(String title, String assignees) { this.title = title; this.assignees = assignees; } @OneToOne(mappedBy = "taskRefference") @NotFound(action = NotFoundAction.IGNORE) @JsonBackReference // 被控方加这个注解 public Employee getEmployee() { return employee; } public void setEmployee(Employee employee) { this.employee = employee; } }
这样修改后,序列化Employee时会正常包含关联的Task,但序列化Task时不会包含Employee,完美避免循环。
方案2:用@JsonIgnore直接忽略一方引用
如果你的业务场景不需要在序列化Task时包含Employee,最简单的方法就是在Task的employee属性上加上@JsonIgnore,直接跳过这个字段的序列化:
@Entity @Table(name = "task") public class Task { // ... 其他代码不变 @OneToOne(mappedBy = "taskRefference") @NotFound(action = NotFoundAction.IGNORE) @JsonIgnore // 直接忽略这个关联 public Employee getEmployee() { return employee; } // ... 其他代码不变 }
这个方法最简洁,但缺点是单向的——如果之后需要序列化Task时带出Employee,就不适用了。
方案3:用@JsonIdentityInfo处理双向序列化
如果你的业务需要双向都能序列化(比如既想通过Employee查Task,也想通过Task查Employee),可以用@JsonIdentityInfo让Jackson识别重复引用,用对象ID代替重复的对象内容:
修改后的Employee.java
@Entity @Table(name = "employee") @JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator.class, property = "id") public class Employee { // ... 所有代码不变 }
修改后的Task.java
@Entity @Table(name = "task") @JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator.class, property = "id") public class Task { // ... 所有代码不变 }
这样序列化时,第一次出现Employee或Task会输出完整对象,之后再遇到相同对象时,只会输出它的id,既保留了双向关联的信息,又避免了无限递归。
内容的提问来源于stack exchange,提问作者Stiliyan Koev

