ManyToOne 关系中的 Jpa Stackoverflow 异常

Jpa Stackoverflow exception in ManyToOne relation

我有部分,里面可以有小节,里面可以有更深层次的部分。实体:

@Data
@Table(name = "section")
@Entity
@NoArgsConstructor
public class Section {
  @Id
  @GeneratedValue(strategy = GenerationType.IDENTITY)
  @Column
  private Long id;

  @Column private String title;

  @ManyToOne(fetch = FetchType.LAZY)
  @JoinColumn(name = "parent_id")
  private Section parent;

  @JsonIgnore
  @OneToMany(fetch = FetchType.EAGER, mappedBy = "parent")
  private Set<Section> children;

  public Section(Long id, String title, Section parent) {
    this.id = id;
    this.title = title;
    this.parent = parent;
  }
}

我将所有数据保存在 h2 数据库中用于测试目的。存储库 class:

@Repository
public interface SectionRepository extends JpaRepository<Section, Long> {
  List<Section> findAllByParentId(Long parentId);
}

我在 h2 中的内容:

我只想找到这些没有父级的部分(前两个在 parent_id 列中为空)。我是如何尝试的:

Section about = new Section(null, "about", null);
Section production = new Section(null, "production", null);

sectionRepository.save(about);
sectionRepository.save(production);

Section subsectionOne = new Section(null, "subsectionOne", production); // without adding
Section subsectionTwo = new Section(null, "subsectionTwo", production); // these two subsections
                                 sectionRepository.save(subsectionOne); // and saving them to db
                                 sectionRepository.save(subsectionTwo); // I don't receive any error on findAllByParentId(null) method call

sectionRepository.findAllByParentId(null); // action
java.lang.WhosebugError: null
    at org.h2.util.ParserUtil.getSaveTokenType(ParserUtil.java:592) ~[h2-1.4.200.jar:1.4.200]
    at org.h2.command.Parser.read(Parser.java:5092) ~[h2-1.4.200.jar:1.4.200]
    at org.h2.command.Parser.readIf(Parser.java:5011) ~[h2-1.4.200.jar:1.4.200]
    at org.h2.command.Parser.readTerm(Parser.java:4306) ~[h2-1.4.200.jar:1.4.200]
    at org.h2.command.Parser.readFactor(Parser.java:3343) ~[h2-1.4.200.jar:1.4.200]
    at org.h2.command.Parser.readSum(Parser.java:3330) ~[h2-1.4.200.jar:1.4.200]
    at org.h2.command.Parser.readConcat(Parser.java:3305) ~[h2-1.4.200.jar:1.4.200]
    at org.h2.command.Parser.readCondition(Parser.java:3108) ~[h2-1.4.200.jar:1.4.200]
    at org.h2.command.Parser.readExpression(Parser.java:3059) ~[h2-1.4.200.jar:1.4.200]
    at org.h2.command.Parser.parseSelectExpressions(Parser.java:2931) ~[h2-1.4.200.jar:1.4.200]
    at org.h2.command.Parser.parseSelect(Parser.java:2952) ~[h2-1.4.200.jar:1.4.200]
    at org.h2.command.Parser.parseQuerySub(Parser.java:2817) ~[h2-1.4.200.jar:1.4.200]
    at org.h2.command.Parser.parseSelectUnion(Parser.java:2649) ~[h2-1.4.200.jar:1.4.200]
    at org.h2.command.Parser.parseQuery(Parser.java:2620) ~[h2-1.4.200.jar:1.4.200]
    at org.h2.command.Parser.parsePrepared(Parser.java:868) ~[h2-1.4.200.jar:1.4.200]
    at org.h2.command.Parser.parse(Parser.java:843) ~[h2-1.4.200.jar:1.4.200]
    at org.h2.command.Parser.parse(Parser.java:815) ~[h2-1.4.200.jar:1.4.200]
    at org.h2.command.Parser.prepareCommand(Parser.java:738) ~[h2-1.4.200.jar:1.4.200]
    at org.h2.engine.Session.prepareLocal(Session.java:657) ~[h2-1.4.200.jar:1.4.200]
    at org.h2.engine.Session.prepareCommand(Session.java:595) ~[h2-1.4.200.jar:1.4.200]
    at org.h2.jdbc.JdbcConnection.prepareCommand(JdbcConnection.java:1235) ~[h2-1.4.200.jar:1.4.200]
    at org.h2.jdbc.JdbcPreparedStatement.<init>(JdbcPreparedStatement.java:76) ~[h2-1.4.200.jar:1.4.200]
    at org.h2.jdbc.JdbcConnection.prepareStatement(JdbcConnection.java:352) ~[h2-1.4.200.jar:1.4.200]
    at com.zaxxer.hikari.pool.ProxyConnection.prepareStatement(ProxyConnection.java:337) ~[HikariCP-3.4.5.jar:na]
    at com.zaxxer.hikari.pool.HikariProxyConnection.prepareStatement(HikariProxyConnection.java) ~[HikariCP-3.4.5.jar:na]
    at org.hibernate.engine.jdbc.internal.StatementPreparerImpl.doPrepare(StatementPreparerImpl.java:149) ~[hibernate-core-5.4.25.Final.jar:5.4.25.Final]
    at org.hibernate.engine.jdbc.internal.StatementPreparerImpl$StatementPreparationTemplate.prepareStatement(StatementPreparerImpl.java:176) ~[hibernate-core-5.4.25.Final.jar:5.4.25.Final]
    at org.hibernate.engine.jdbc.internal.StatementPreparerImpl.prepareQueryStatement(StatementPreparerImpl.java:151) ~[hibernate-core-5.4.25.Final.jar:5.4.25.Final]
    at org.hibernate.loader.plan.exec.internal.AbstractLoadPlanBasedLoader.prepareQueryStatement(AbstractLoadPlanBasedLoader.java:198) ~[hibernate-core-5.4.25.Final.jar:5.4.25.Final]
    at org.hibernate.loader.plan.exec.internal.AbstractLoadPlanBasedLoader.executeQueryStatement(AbstractLoadPlanBasedLoader.java:162) ~[hibernate-core-5.4.25.Final.jar:5.4.25.Final]
    at org.hibernate.loader.plan.exec.internal.AbstractLoadPlanBasedLoader.executeLoad(AbstractLoadPlanBasedLoader.java:104) ~[hibernate-core-5.4.25.Final.jar:5.4.25.Final]
    at org.hibernate.loader.collection.plan.AbstractLoadPlanBasedCollectionInitializer.initialize(AbstractLoadPlanBasedCollectionInitializer.java:87) ~[hibernate-core-5.4.25.Final.jar:5.4.25.Final]
    at org.hibernate.persister.collection.AbstractCollectionPersister.initialize(AbstractCollectionPersister.java:710) ~[hibernate-core-5.4.25.Final.jar:5.4.25.Final]
    at org.hibernate.event.internal.DefaultInitializeCollectionEventListener.onInitializeCollection(DefaultInitializeCollectionEventListener.java:76) ~[hibernate-core-5.4.25.Final.jar:5.4.25.Final]
    at org.hibernate.event.service.internal.EventListenerGroupImpl.fireEventOnEachListener(EventListenerGroupImpl.java:102) ~[hibernate-core-5.4.25.Final.jar:5.4.25.Final]
    at org.hibernate.internal.SessionImpl.initializeCollection(SessionImpl.java:2163) ~[hibernate-core-5.4.25.Final.jar:5.4.25.Final]
    at org.hibernate.collection.internal.AbstractPersistentCollection.doWork(AbstractPersistentCollection.java:589) ~[hibernate-core-5.4.25.Final.jar:5.4.25.Final]
    at org.hibernate.collection.internal.AbstractPersistentCollection.withTemporarySessionIfNeeded(AbstractPersistentCollection.java:264) ~[hibernate-core-5.4.25.Final.jar:5.4.25.Final]
    at org.hibernate.collection.internal.AbstractPersistentCollection.initialize(AbstractPersistentCollection.java:585) ~[hibernate-core-5.4.25.Final.jar:5.4.25.Final]
    at org.hibernate.collection.internal.AbstractPersistentCollection.read(AbstractPersistentCollection.java:149) ~[hibernate-core-5.4.25.Final.jar:5.4.25.Final]
    at org.hibernate.collection.internal.PersistentSet.hashCode(PersistentSet.java:458) ~[hibernate-core-5.4.25.Final.jar:5.4.25.Final]
    at lv.sawex.core.sections.Section.hashCode(Section.java:10) ~[classes/:na]
    at lv.sawex.core.sections.Section.hashCode(Section.java:10) ~[classes/:na]
    at java.base/java.util.HashMap.hash(HashMap.java:339) ~[na:na]
    at java.base/java.util.HashMap.put(HashMap.java:607) ~[na:na]
    at java.base/java.util.HashSet.add(HashSet.java:220) ~[na:na]
    at java.base/java.util.AbstractCollection.addAll(AbstractCollection.java:352) ~[na:na]
    at org.hibernate.collection.internal.PersistentSet.endRead(PersistentSet.java:355) ~[hibernate-core-5.4.25.Final.jar:5.4.25.Final]
    at org.hibernate.engine.loading.internal.CollectionLoadContext.endLoadingCollection(CollectionLoadContext.java:239) ~[hibernate-core-5.4.25.Final.jar:5.4.25.Final]
    at org.hibernate.engine.loading.internal.CollectionLoadContext.endLoadingCollections(CollectionLoadContext.java:224) ~[hibernate-core-5.4.25.Final.jar:5.4.25.Final]
    at org.hibernate.engine.loading.internal.CollectionLoadContext.endLoadingCollections(CollectionLoadContext.java:198) ~[hibernate-core-5.4.25.Final.jar:5.4.25.Final]
    at org.hibernate.loader.plan.exec.process.internal.CollectionReferenceInitializerImpl.endLoading(CollectionReferenceInitializerImpl.java:154) ~[hibernate-core-5.4.25.Final.jar:5.4.25.Final]
    at org.hibernate.loader.plan.exec.process.internal.AbstractRowReader.finishLoadingCollections(AbstractRowReader.java:260) ~[hibernate-core-5.4.25.Final.jar:5.4.25.Final]
    at org.hibernate.loader.plan.exec.process.internal.AbstractRowReader.finishUp(AbstractRowReader.java:211) ~[hibernate-core-5.4.25.Final.jar:5.4.25.Final]
    at org.hibernate.loader.plan.exec.process.internal.ResultSetProcessorImpl.extractResults(ResultSetProcessorImpl.java:96) ~[hibernate-core-5.4.25.Final.jar:5.4.25.Final]
    at org.hibernate.loader.plan.exec.internal.AbstractLoadPlanBasedLoader.executeLoad(AbstractLoadPlanBasedLoader.java:105) ~[hibernate-core-5.4.25.Final.jar:5.4.25.Final]
    at org.hibernate.loader.collection.plan.AbstractLoadPlanBasedCollectionInitializer.initialize(AbstractLoadPlanBasedCollectionInitializer.java:87) ~[hibernate-core-5.4.25.Final.jar:5.4.25.Final]
    at org.hibernate.persister.collection.AbstractCollectionPersister.initialize(AbstractCollectionPersister.java:710) ~[hibernate-core-5.4.25.Final.jar:5.4.25.Final]
    at org.hibernate.event.internal.DefaultInitializeCollectionEventListener.onInitializeCollection(DefaultInitializeCollectionEventListener.java:76) ~[hibernate-core-5.4.25.Final.jar:5.4.25.Final]
    at org.hibernate.event.service.internal.EventListenerGroupImpl.fireEventOnEachListener(EventListenerGroupImpl.java:102) ~[hibernate-core-5.4.25.Final.jar:5.4.25.Final]
    at org.hibernate.internal.SessionImpl.initializeCollection(SessionImpl.java:2163) ~[hibernate-core-5.4.25.Final.jar:5.4.25.Final]
    at org.hibernate.collection.internal.AbstractPersistentCollection.doWork(AbstractPersistentCollection.java:589) ~[hibernate-core-5.4.25.Final.jar:5.4.25.Final]
    at org.hibernate.collection.internal.AbstractPersistentCollection.withTemporarySessionIfNeeded(AbstractPersistentCollection.java:264) ~[hibernate-core-5.4.25.Final.jar:5.4.25.Final]
    at org.hibernate.collection.internal.AbstractPersistentCollection.initialize(AbstractPersistentCollection.java:585) ~[hibernate-core-5.4.25.Final.jar:5.4.25.Final]
    at org.hibernate.collection.internal.AbstractPersistentCollection.read(AbstractPersistentCollection.java:149) ~[hibernate-core-5.4.25.Final.jar:5.4.25.Final]
    at org.hibernate.collection.internal.PersistentSet.hashCode(PersistentSet.java:458) ~[hibernate-core-5.4.25.Final.jar:5.4.25.Final]
    at lv.sawex.core.sections.Section.hashCode(Section.java:10) ~[classes/:na]
    at lv.sawex.core.sections.Section.hashCode(Section.java:10) ~[classes/:na]
    at java.base/java.util.HashMap.hash(HashMap.java:339) ~[na:na]
    at java.base/java.util.HashMap.put(HashMap.java:607) ~[na:na]
    at java.base/java.util.HashSet.add(HashSet.java:220) ~[na:na]
    at java.base/java.util.AbstractCollection.addAll(AbstractCollection.java:352) ~[na:na]
    at org.hibernate.collection.internal.PersistentSet.endRead(PersistentSet.java:355) ~[hibernate-core-5.4.25.Final.jar:5.4.25.Final]
    at org.hibernate.engine.loading.internal.CollectionLoadContext.endLoadingCollection(CollectionLoadContext.java:239) ~[hibernate-core-5.4.25.Final.jar:5.4.25.Final]
    at org.hibernate.engine.loading.internal.CollectionLoadContext.endLoadingCollections(CollectionLoadContext.java:224) ~[hibernate-core-5.4.25.Final.jar:5.4.25.Final]
    at org.hibernate.engine.loading.internal.CollectionLoadContext.endLoadingCollections(CollectionLoadContext.java:198) ~[hibernate-core-5.4.25.Final.jar:5.4.25.Final]
    at org.hibernate.loader.plan.exec.process.internal.CollectionReferenceInitializerImpl.endLoading(CollectionReferenceInitializerImpl.java:154) ~[hibernate-core-5.4.25.Final.jar:5.4.25.Final]
    at org.hibernate.loader.plan.exec.process.internal.AbstractRowReader.finishLoadingCollections(AbstractRowReader.java:260) ~[hibernate-core-5.4.25.Final.jar:5.4.25.Final]
    at org.hibernate.loader.plan.exec.process.internal.AbstractRowReader.finishUp(AbstractRowReader.java:211) ~[hibernate-core-5.4.25.Final.jar:5.4.25.Final]
    at org.hibernate.loader.plan.exec.process.internal.ResultSetProcessorImpl.extractResults(ResultSetProcessorImpl.java:96) ~[hibernate-core-5.4.25.Final.jar:5.4.25.Final]
    at org.hibernate.loader.plan.exec.internal.AbstractLoadPlanBasedLoader.executeLoad(AbstractLoadPlanBasedLoader.java:105) ~[hibernate-core-5.4.25.Final.jar:5.4.25.Final]
    at org.hibernate.loader.collection.plan.AbstractLoadPlanBasedCollectionInitializer.initialize(AbstractLoadPlanBasedCollectionInitializer.java:87) ~[hibernate-core-5.4.25.Final.jar:5.4.25.Final]
    at org.hibernate.persister.collection.AbstractCollectionPersister.initialize(AbstractCollectionPersister.java:710) ~[hibernate-core-5.4.25.Final.jar:5.4.25.Final]
    at org.hibernate.event.internal.DefaultInitializeCollectionEventListener.onInitializeCollection(DefaultInitializeCollectionEventListener.java:76) ~[hibernate-core-5.4.25.Final.jar:5.4.25.Final]
    at org.hibernate.event.service.internal.EventListenerGroupImpl.fireEventOnEachListener(EventListenerGroupImpl.java:102) ~[hibernate-core-5.4.25.Final.jar:5.4.25.Final]
    at org.hibernate.internal.SessionImpl.initializeCollection(SessionImpl.java:2163) ~[hibernate-core-5.4.25.Final.jar:5.4.25.Final]
    at org.hibernate.collection.internal.AbstractPersistentCollection.doWork(AbstractPersistentCollection.java:589) ~[hibernate-core-5.4.25.Final.jar:5.4.25.Final]
    at org.hibernate.collection.internal.AbstractPersistentCollection.withTemporarySessionIfNeeded(AbstractPersistentCollection.java:264) ~[hibernate-core-5.4.25.Final.jar:5.4.25.Final]
    at org.hibernate.collection.internal.AbstractPersistentCollection.initialize(AbstractPersistentCollection.java:585) ~[hibernate-core-5.4.25.Final.jar:5.4.25.Final]
    at org.hibernate.collection.internal.AbstractPersistentCollection.read(AbstractPersistentCollection.java:149) ~[hibernate-core-5.4.25.Final.jar:5.4.25.Final]
    at org.hibernate.collection.internal.PersistentSet.hashCode(PersistentSet.java:458) ~[hibernate-core-5.4.25.Final.jar:5.4.25.Final]
    at lv.sawex.core.sections.Section.hashCode(Section.java:10) ~[classes/:na]
    at lv.sawex.core.sections.Section.hashCode(Section.java:10) ~[classes/:na]
    at java.base/java.util.HashMap.hash(HashMap.java:339) ~[na:na]
    at java.base/java.util.HashMap.put(HashMap.java:607) ~[na:na]
    at java.base/java.util.HashSet.add(HashSet.java:220) ~[na:na]

spring.jpa.show-sql=true Sql 结果:最后一个 sql 查询递归重复。

Hibernate: select section0_.id as id1_1_, section0_.parent_id as parent_i4_1_, section0_.title as title2_1_, section0_.titlelv as titlelv3_1_ from section section0_ left outer join section section1_ on section0_.parent_id=section1_.id where section1_.id is null
Hibernate: select children0_.parent_id as parent_i4_1_0_, children0_.id as id1_1_0_, children0_.id as id1_1_1_, children0_.parent_id as parent_i4_1_1_, children0_.title as title2_1_1_, children0_.titlelv as titlelv3_1_1_ from section children0_ where children0_.parent_id=?
Hibernate: select children0_.parent_id as parent_i4_1_0_, children0_.id as id1_1_0_, children0_.id as id1_1_1_, children0_.parent_id as parent_i4_1_1_, children0_.title as title2_1_1_, children0_.titlelv as titlelv3_1_1_ from section children0_ where children0_.parent_id=?

问题出在哪里?

根据使用的堆栈跟踪和 Lombok 注释判断,问题可能与 hashCode() 的递归调用有关。 Lombok 的 @Data 将为 equalshashCode 方法生成实现,默认情况下,这些实现将使用所有声明的字段,包括您的 parentchildren .这意味着尝试计算“production”条目的哈希码将触发“subsectionOne”/“subsectionTwo”的计算,通过 parent 属性.

返回到“production”

通过将 @EqualsAndHashCode.Exclude 添加到 children 字段从 hashCode 实施中排除集合应该可以解决问题