尝试删除实体时的java.lang.StackOverflowError

时间:2017-02-22 22:06:29

标签: java spring hibernate jpa

尝试删除实体时,会出现StackOverflowError。这种情况发生的唯一时间是我有一个包含2个外键引用的复合键。我在下面创建了一个最小的例子:

包含@OneToMany集合的第一个对象:

@Entity
public class Foo {

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private long id;

    @OneToMany(mappedBy = "id.foo", cascade = CascadeType.ALL, orphanRemoval = true)
    private List<Baz> baz;

    // Getters and setters omitted.
}

包含@OneToMany引用的第二个对象:

@Entity
public class Bar {

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private long id;

    @OneToMany(mappedBy = "id.bar", cascade = CascadeType.ALL, orphanRemoval = true)
    private List<Baz> baz;

    // Getters and setters omitted.
}

包含上述两个对象(FooBar)作为复合键的对象:

@Entity
public class Baz {

    @EmbeddedId
    private BazId id;

    // Getters and setters omitted.
}

最后,Baz的复合键类:

@Embeddable
public class BazId implements Serializable {

    private static final long serialVersionUID = -8869241450494423817L;

    @ManyToOne
    private Foo foo;

    @ManyToOne
    private Bar bar;

    // Getters and setters omitted
}

当我执行下面的测试时,我得到java.lang.StackOverflowError

// Spring Data JPA Repositories (extend CrudRepository)
@Autowired private FooRepository fooRepository;
@Autowired private BarRepository barRepository;
@Autowired private BazRepository bazRepository;

@Test
public void test() {
    final Foo foo = new Foo();
    fooRepository.save(foo);

    final Bar bar = new Bar();
    barRepository.save(bar);

    final Baz baz = new Baz(new BazId(foo, bar));
    bazRepository.save(baz);

    foo.getBaz().add(baz);
    bar.getBaz().add(baz);

    // These two deletes cause the StackOverflowError
    fooRepository.delete(foo);
    barRepository.delete(bar);
}

我尝试了什么:

  • 覆盖equals(Object o)toString()hashCode()

除了完全重做我的映射之外,我不确定还有什么可以尝试的。我需要像在我的真实项目中那样设置映射,我在User之后有一个Business所以这需要是两个实体之间的复合键。为什么这会导致无限递归?

修改

堆栈跟踪如果有帮助(部分原因。整件事不适合):

java.lang.StackOverflowError
    at org.apache.tomcat.jdbc.pool.ProxyConnection.invoke(ProxyConnection.java:131)
    at org.apache.tomcat.jdbc.pool.JdbcInterceptor.invoke(JdbcInterceptor.java:108)
    at org.apache.tomcat.jdbc.pool.DisposableConnectionFacade.invoke(DisposableConnectionFacade.java:81)
    at com.sun.proxy.$Proxy73.prepareStatement(Unknown Source)
    at org.hibernate.engine.jdbc.internal.StatementPreparerImpl$5.doPrepare(StatementPreparerImpl.java:146)
    at org.hibernate.engine.jdbc.internal.StatementPreparerImpl$StatementPreparationTemplate.prepareStatement(StatementPreparerImpl.java:172)
    at org.hibernate.engine.jdbc.internal.StatementPreparerImpl.prepareQueryStatement(StatementPreparerImpl.java:148)
    at org.hibernate.loader.Loader.prepareQueryStatement(Loader.java:1929)
    at org.hibernate.loader.Loader.executeQueryStatement(Loader.java:1898)
    at org.hibernate.loader.Loader.executeQueryStatement(Loader.java:1876)
    at org.hibernate.loader.Loader.doQuery(Loader.java:919)
    at org.hibernate.loader.Loader.doQueryAndInitializeNonLazyCollections(Loader.java:336)
    at org.hibernate.loader.Loader.doQueryAndInitializeNonLazyCollections(Loader.java:306)
    at org.hibernate.loader.Loader.loadEntity(Loader.java:2204)
    at org.hibernate.loader.entity.AbstractEntityLoader.load(AbstractEntityLoader.java:60)
    at org.hibernate.loader.entity.AbstractEntityLoader.load(AbstractEntityLoader.java:50)
    at org.hibernate.persister.entity.AbstractEntityPersister.load(AbstractEntityPersister.java:3956)
    at org.hibernate.event.internal.DefaultLoadEventListener.loadFromDatasource(DefaultLoadEventListener.java:508)
    at org.hibernate.event.internal.DefaultLoadEventListener.doLoad(DefaultLoadEventListener.java:478)
    at org.hibernate.event.internal.DefaultLoadEventListener.load(DefaultLoadEventListener.java:219)
    at org.hibernate.event.internal.DefaultLoadEventListener.proxyOrLoad(DefaultLoadEventListener.java:278)
    at org.hibernate.event.internal.DefaultLoadEventListener.doOnLoad(DefaultLoadEventListener.java:121)
    at org.hibernate.event.internal.DefaultLoadEventListener.onLoad(DefaultLoadEventListener.java:89)
    at org.hibernate.internal.SessionImpl.fireLoad(SessionImpl.java:1129)
    at org.hibernate.internal.SessionImpl.internalLoad(SessionImpl.java:1022)
    at org.hibernate.type.EntityType.resolveIdentifier(EntityType.java:632)
    at org.hibernate.type.EntityType.resolve(EntityType.java:424)
    at org.hibernate.type.ComponentType.resolve(ComponentType.java:687)
    at org.hibernate.loader.Loader.extractKeysFromResultSet(Loader.java:848)
    at org.hibernate.loader.Loader.getRowFromResultSet(Loader.java:714)
    at org.hibernate.loader.Loader.processResultSet(Loader.java:972)
    at org.hibernate.loader.Loader.doQuery(Loader.java:930)
    at org.hibernate.loader.Loader.doQueryAndInitializeNonLazyCollections(Loader.java:336)
    at org.hibernate.loader.Loader.doQueryAndInitializeNonLazyCollections(Loader.java:306)
    at org.hibernate.loader.Loader.loadEntity(Loader.java:2204)
    at org.hibernate.loader.entity.AbstractEntityLoader.load(AbstractEntityLoader.java:60)
    at org.hibernate.loader.entity.AbstractEntityLoader.load(AbstractEntityLoader.java:50)
    at org.hibernate.persister.entity.AbstractEntityPersister.load(AbstractEntityPersister.java:3956)
    at org.hibernate.event.internal.DefaultLoadEventListener.loadFromDatasource(DefaultLoadEventListener.java:508)
    at org.hibernate.event.internal.DefaultLoadEventListener.doLoad(DefaultLoadEventListener.java:478)
    at org.hibernate.event.internal.DefaultLoadEventListener.load(DefaultLoadEventListener.java:219)
    at org.hibernate.event.internal.DefaultLoadEventListener.proxyOrLoad(DefaultLoadEventListener.java:278)
    at org.hibernate.event.internal.DefaultLoadEventListener.doOnLoad(DefaultLoadEventListener.java:121)
    at org.hibernate.event.internal.DefaultLoadEventListener.onLoad(DefaultLoadEventListener.java:89)
    at org.hibernate.internal.SessionImpl.fireLoad(SessionImpl.java:1129)
    at org.hibernate.internal.SessionImpl.internalLoad(SessionImpl.java:1022)
    at org.hibernate.type.EntityType.resolveIdentifier(EntityType.java:632)
    at org.hibernate.type.EntityType.resolve(EntityType.java:424)
    at org.hibernate.type.ComponentType.resolve(ComponentType.java:687)
    at org.hibernate.loader.Loader.extractKeysFromResultSet(Loader.java:848)
    at org.hibernate.loader.Loader.getRowFromResultSet(Loader.java:714)
    at org.hibernate.loader.Loader.processResultSet(Loader.java:972)
    at org.hibernate.loader.Loader.doQuery(Loader.java:930)
    at org.hibernate.loader.Loader.doQueryAndInitializeNonLazyCollections(Loader.java:336)

2 个答案:

答案 0 :(得分:1)

cascade = CascadeType.ALLBaz内的Foo集合上Bar会在尝试删除时导致无限递归。删除两个集合上的级联并手动删除Baz关系,而不是级联来解决此问题。

答案 1 :(得分:1)

Tho Hibernate声称支持在PK类中直接建模ManyToOne关系映射,无论是EmbeddedId还是IdClass。我建议不要设计特定的持久性提供程序的实现。以下示例针对JPA 2.0 / 2.1规范而设计,并提供了良好的可移植性。

@Entity
public class Foo {
    @Id @GeneratedValue(strategy = GenerationType.AUTO)
    private long id;

    @OneToMany(mappedBy = "foo", cascade = CascadeType.ALL)
    private List<Baz> baz;
}

酒吧

@Entity
public class Bar {
    @Id @GeneratedValue(strategy = GenerationType.AUTO)
    private long id;

    @OneToMany(mappedBy = "bar", cascade = CascadeType.ALL)
    private List<Baz> baz;
}

巴兹

@Entity
public class Baz {
    @EmbeddedId
    private BazId id;

    @ManyToOne 
    @MapsId("fooid")
    private Foo foo;

    @ManyToOne 
    @MapsId("barid")
    private Bar bar;
}

BazId

@Embeddable
public class BazId implements Serializable {

    private long fooid;

    private long barid;
}