You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

SpringBoot获取Book实体时出现StackOverflowError求助

解决Spring双向多对多关联导致的StackOverflowError问题

问题原因

你遇到的StackOverflowError是双向多对多关联的循环序列化导致的:

  • BookEntity中包含List<Author>,Author中又包含List<BookEntity>,形成双向引用。
  • 当Spring的JSON转换器(如Jackson)序列化接口返回的BookEntity列表时,会递归遍历Book→Author→Book→Author...,不断调用双方的getter方法,最终耗尽栈内存引发溢出。
  • 另外你的双向多对多关联未指定mappedBy,JPA会自动生成两张中间表,这也是需要修正的潜在问题。

解决方案

方案1:用@JsonIgnore忽略一方的关联属性

在其中一方的关联字段上添加@JsonIgnore注解,阻止JSON序列化该字段,打断循环链。

比如修改Author类:

@Entity
public class Author {
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private long id;
    private String name;
    @ManyToMany
    @JsonIgnore // 忽略该字段的序列化
    private List<BookEntity> book;

    // 构造方法、getter、setter 保持不变
}

方案2:用@JsonManagedReference和@JsonBackReference(推荐)

通过这对注解区分双向关联的“管理端”和“反向端”,管理端会被正常序列化,反向端则被忽略,既保留业务数据又避免循环。

同时补充mappedBy指定关联维护方,避免生成冗余中间表:

// BookEntity类(管理端)
@Entity
public class BookEntity {
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private long id;
    private String title;
    @ManyToMany
    @JsonManagedReference // 标记为管理端,正常序列化
    private List<Author> author;

    // 构造方法、getter、setter 保持不变
}
// Author类(反向端)
@Entity
public class Author {
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private long id;
    private String name;
    @ManyToMany(mappedBy = "author") // 指定由BookEntity的author字段维护关联
    @JsonBackReference // 标记为反向端,序列化时忽略
    private List<BookEntity> book;

    // 构造方法、getter、setter 保持不变
}

方案3:使用DTO(数据传输对象)

创建与实体类分离的DTO对象,只封装接口需要返回的字段,通过实体转DTO的方式彻底避免循环引用,同时解耦数据库实体和接口返回结构。

示例:

// BookDTO.java
public class BookDTO {
    private long id;
    private String title;
    private List<AuthorDTO> authors;

    // 构造方法、getter、setter
    public BookDTO(long id, String title, List<AuthorDTO> authors) {
        this.id = id;
        this.title = title;
        this.authors = authors;
    }
}
// AuthorDTO.java
public class AuthorDTO {
    private long id;
    private String name;

    // 构造方法、getter、setter
    public AuthorDTO(long id, String name) {
        this.id = id;
        this.name = name;
    }
}

修改Controller中的方法:

@GetMapping
List<BookDTO> getAllBooks() {
    return bookRepository.findAll().stream()
            .map(book -> new BookDTO(
                    book.getId(),
                    book.getTitle(),
                    book.getAuthor().stream()
                            .map(author -> new AuthorDTO(author.getId(), author.getName()))
                            .toList()
            )).toList();
}

内容的提问来源于stack exchange,提问作者user21180921

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.01 06:16:08