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

Spring JPA调用findById触发StackOverflowError问题求助

关联实体toString循环调用导致StackOverflowError问题解决

问题现象

当Track实体存在与Source关联的数据时,调用Source Repository的findById或findAll查询会触发StackOverflowError,错误栈显示Track与Source的toString方法互相循环调用。

错误栈信息

Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Handler dispatch failed: java.lang.StackOverflowError] with root cause

java.lang.StackOverflowError: null at com.encode.atlas.encoder.entity.track.Track.toString(Track.java:7) ~[classes!/:0.0.1-SNAPSHOT] at java.base/java.lang.String.valueOf(String.java:4220) ~[na:na] at java.base/java.lang.StringBuilder.append(StringBuilder.java:173) ~[na:na] at java.base/java.util.AbstractCollection.toString(AbstractCollection.java:457) ~[na:na] at org.hibernate.collection.spi.PersistentBag.toString(PersistentBag.java:585) ~[hibernate-core-6.1.7.Final.jar!/:6.1.7.Final] at java.base/java.lang.StringConcatHelper.stringOf(StringConcatHelper.java:453) ~[na:na] at com.encode.atlas.encoder.entity.track.Source.toString(Source.java:9) ~[classes!/:0.0.1-SNAPSHOT] at java.base/java.lang.StringConcatHelper.stringOf(StringConcatHelper.java:453) ~[na:na] at com.encode.atlas.encoder.entity.track.Track.toString(Track.java:7) ~[classes!/:0.0.1-SNAPSHOT] at java.base/java.lang.String.valueOf(String.java:4220) ~[na:na] at java.base/java.lang.StringBuilder.append(StringBuilder.java:173) ~[na:na] at java.base/java.util.AbstractCollection.toString(AbstractCollection.java:457) ~[na:na] at org.hibernate.collection.spi.PersistentBag.toString(PersistentBag.java:585) ~[hibernate-core-6.1.7.Final.jar!/:6.1.7.Final] at java.base/java.lang.StringConcatHelper.stringOf(StringConcatHelper.java:453) ~[na:na] at com.encode.atlas.encoder.entity.track.Source.toString(Source.java:9) ~[classes!/:0.0.1-SNAPSHOT] at java.base/java.lang.StringConcatHelper.stringOf(StringConcatHelper.java:453) ~[na:na] at com.encode.atlas.encoder.entity.track.Track.toString(Track.java:7) ~[classes!/:0.0.1-SNAPSHOT] at java.base/java.lang.String.valueOf(String.java:4220) ~[na:na] at java.base/java.lang.StringBuilder.append(StringBuilder.java:173) ~[na:na] at java.base/java.util.AbstractCollection.toString(AbstractCollection.java:457) ~[na:na] at org.hibernate.collection.spi.PersistentBag.toString(PersistentBag.java:585) ~[hibernate-core-6.1.7.Final.jar!/:6.1.7.Final] at java.base/java.lang.StringConcatHelper.stringOf(StringConcatHelper.java:453) ~[na:na] at com.encode.atlas.encoder.entity.track.Source.toString(Source.java:9) ~[classes!/:0.0.1-SNAPSHOT] at java.base/java.lang.StringConcatHelper.stringOf(StringConcatHelper.java:453) ~[na:na] at com.encode.atlas.encoder.entity.track.Track.toString(Track.java:7) ~[classes!/:0.0.1-SNAPSHOT] at java.base/java.lang.String.valueOf(String.java:4220) ~[na:na]

实体代码

Source实体

@Data
@Entity
public class Source {
    @Id  
    @GeneratedValue(strategy = GenerationType.IDENTITY) 
    private Long id;
    private String name;
    private String path;
    private Integer duration;
    @OneToMany(cascade = CascadeType.ALL,fetch = FetchType.LAZY)
    private List<Track> tracks = new ArrayList<>();
}

Track实体

@Data
@Entity
public class Track {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    @ManyToOne(cascade = CascadeType.ALL)
    @JsonBackReference
    private Source source;
    private Integer index;
    private String codecType;
    private String pixelFormat;
    private Integer width;
    private Integer height;
    private Integer bitrate;
    private Double fps;
    private String codec;
    private Integer sampleRate;
    private String channel;
}

查询代码

Source source = sourceRepository.findById(job.getSource().getId()).orElseThrow();

问题原因

Lombok的@Data注解会自动生成包含所有字段的toString方法,而Source和Track存在双向关联:

  1. Source的toString会遍历tracks集合,调用每个Track的toString
  2. Track的toString又会调用关联的Source的toString
  3. 如此循环往复,直到JVM栈内存耗尽,抛出StackOverflowError

注:@JsonBackReference仅解决JSON序列化时的循环问题,不影响toString方法;调整FetchType为LAZY/EAGER也无法避免,因为toString触发时Hibernate会加载关联对象,依然会进入循环。

解决方案

方案一:使用Lombok注解排除关联字段

在其中一方的关联字段上添加@ToString.Exclude,禁止该字段出现在自动生成的toString中,只需在任意一方添加即可:

修改Source实体

@Data
@Entity
public class Source {
    // 其他字段不变
    @OneToMany(cascade = CascadeType.ALL,fetch = FetchType.LAZY)
    @ToString.Exclude // 排除tracks字段在toString中生成
    private List<Track> tracks = new ArrayList<>();
}

或修改Track实体

@Data
@Entity
public class Track {
    // 其他字段不变
    @ManyToOne(cascade = CascadeType.ALL)
    @JsonBackReference
    @ToString.Exclude // 排除source字段在toString中生成
    private Source source;
}

方案二:手动编写toString方法

放弃Lombok自动生成的toString,手动编写只包含自身基本字段的toString方法,避免调用关联对象的toString:

Source手动实现toString

@Entity
public class Source {
    // 字段不变
    
    @Override
    public String toString() {
        return "Source{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", path='" + path + '\'' +
                ", duration=" + duration +
                '}';
    }
}

Track手动实现toString

@Entity
public class Track {
    // 字段不变
    
    @Override
    public String toString() {
        return "Track{" +
                "id=" + id +
                ", index=" + index +
                ", codecType='" + codecType + '\'' +
                ", pixelFormat='" + pixelFormat + '\'' +
                ", width=" + width +
                ", height=" + height +
                ", bitrate=" + bitrate +
                ", fps=" + fps +
                ", codec='" + codec + '\'' +
                ", sampleRate=" + sampleRate +
                ", channel='" + channel + '\'' +
                '}';
    }
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.24 10:07:02