如何在Repository单元测试中模拟Lombok @Data引发的栈溢出错误
JPA双向关联+Lombok @Data栈溢出的测试验证方案
问题根源
@Data注解会自动生成equals()、hashCode()和toString()方法,而你的实体存在双向关联:
- UserEntity ↔ HometownEntity(一对多双向)
- UserEntity ↔ HobbyAndInterestEntity(多对多双向)
这些自动生成的方法会触发循环调用:比如User的toString调用Hometown的toString,后者又调回User的toString,无限递归直接撑爆调用栈,抛出StackOverflowError。
编写测试验证异常场景
你可以写一个测试,主动触发会引发递归的方法,验证用@Data时会抛出异常,换成@Getter+@Setter时测试正常通过。
测试代码示例
import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertThrows; @Test void testStackOverflowWithDataAnnotation() { // 先保存测试数据 UserEntity savedUser = saveUserEntity(); // 从仓库加载实体,确保关联关系被初始化(LAZY类型需手动触发加载) UserEntity loadedUser = userRepository.findById(savedUser.getId()).orElseThrow(); // 触发toString(),触发双向关联的递归调用 assertThrows(StackOverflowError.class, loadedUser::toString); // 若业务场景用到equals/hashCode,也可补充对应测试 // assertThrows(StackOverflowError.class, () -> loadedUser.equals(loadedUser)); }
注意事项
- 处理LAZY加载:如果关联是LAZY fetch类型,需要先调用关联字段的get方法(比如
loadedUser.getHometownEntity().getCity()),确保关联实体被加载,否则递归不会立即触发。 - 验证修复效果:当你把实体的@Data替换成@Getter+@Setter,或者用@Data但给关联字段加排除注解,这个测试就不会再抛出异常,证明修复有效。
可选修复方案
要是想继续用@Data而不换注解,给双向关联的字段加排除注解即可:
@Entity @Table(name = "users") @Builder @Data @AllArgsConstructor @NoArgsConstructor public class UserEntity { // ...其他字段 @ManyToOne(fetch = FetchType.LAZY, cascade = CascadeType.MERGE) @JoinColumn(name = "hometown_id", referencedColumnName = "id") @EqualsAndHashCode.Exclude @ToString.Exclude private HometownEntity hometownEntity; @ManyToMany @JoinTable( name = "users_hobbies", joinColumns = @JoinColumn(name = "user_id", referencedColumnName = "id"), inverseJoinColumns = @JoinColumn(name = "hobby_and_interest_id", referencedColumnName = "id")) @EqualsAndHashCode.Exclude @ToString.Exclude private Set<HobbyAndInterestEntity> hobbyAndInterestEntities; }
对应的HometownEntity和HobbyAndInterestEntity里的反向关联字段,也要添加同样的@EqualsAndHashCode.Exclude和@ToString.Exclude注解。
内容的提问来源于stack exchange,提问作者Francislainy Campos
相关产品推荐
相关产品推荐

