【问题标题】:Hibernate @ManyToMany persist休眠@ManyToMany 坚持
【发布时间】:2021-12-07 07:01:12
【问题描述】:

我对@9​​87654322@relationShip 有疑问。

我有一个实体Category,在 DB 中有预定义的插入。

我有实体Trigger,它必须包含预定义的Category

@Entity
@Data
public class Category {

    @Id
    private Long id;

    private String name;

    @ManyToMany(cascade = CascadeType.ALL, mappedBy = "categories")
    private Set<Trigger> trigger;

@Entity
@Data
public class Trigger {

    @ManyToMany()
    private Set<Category> categories;
}



Trigger trigger = new Trigger();
trigger.setName(triggerDTO.getName());
trigger.setCategories(triggerDTO.getCategories()
            .stream()
            .map(c -> categoryRepository.findByName(c.getName()))
            .collect(Collectors.toSet()));

所以当我坚持第一个Trigger 时,一切都很好,但是当我尝试坚持下一个Trigger 时,我看到了:

java.lang.StackOverflowError: null
  at org.postgresql.jdbc.PgStatement.execute(PgStatement.java:401) ~[postgresql-42.2.23.jar:42.2.23]
  at org.postgresql.jdbc.PgPreparedStatement.executeWithFlags(PgPreparedStatement.java:164) ~[postgresql-42.2.23.jar:42.2.23]
  at org.postgresql.jdbc.PgPreparedStatement.executeQuery(PgPreparedStatement.java:114) ~[postgresql-42.2.23.jar:42.2.23]
  at com.zaxxer.hikari.pool.ProxyPreparedStatement.executeQuery(ProxyPreparedStatement.java:52) ~[HikariCP-4.0.3.jar:na]
  at com.zaxxer.hikari.pool.HikariProxyPreparedStatement.executeQuery(HikariProxyPreparedStatement.java) ~[HikariCP-4.0.3.jar:na]
  at org.hibernate.engine.jdbc.internal.ResultSetReturnImpl.extract(ResultSetReturnImpl.java:57) ~[hibernate-core-5.4.32.Final.jar:5.4.32.Final]
  at org.hibernate.loader.plan.exec.internal.AbstractLoadPlanBasedLoader.getResultSet(AbstractLoadPlanBasedLoader.java:390) ~[hibernate-core-5.4.32.Final.jar:5.4.32.Final]
  at org.hibernate.loader.plan.exec.internal.AbstractLoadPlanBasedLoader.executeQueryStatement(AbstractLoadPlanBasedLoader.java:163) ~[hibernate-core-5.4.32.Final.jar:5.4.32.Final]
  at org.hibernate.loader.plan.exec.internal.AbstractLoadPlanBasedLoader.executeLoad(AbstractLoadPlanBasedLoader.java:104) ~[hibernate-core-5.4.32.Final.jar:5.4.32.Final]
  at org.hibernate.loader.collection.plan.AbstractLoadPlanBasedCollectionInitializer.initialize(AbstractLoadPlanBasedCollectionInitializer.java:87) ~[hibernate-core-5.4.32.Final.jar:5.4.32.Final]
  at org.hibernate.persister.collection.AbstractCollectionPersister.initialize(AbstractCollectionPersister.java:710) ~[hibernate-core-5.4.32.Final.jar:5.4.32.Final]
  at org.hibernate.event.internal.DefaultInitializeCollectionEventListener.onInitializeCollection(DefaultInitializeCollectionEventListener.java:76) ~[hibernate-core-5.4.32.Final.jar:5.4.32.Final]
  at org.hibernate.event.service.internal.EventListenerGroupImpl.fireEventOnEachListener(EventListenerGroupImpl.java:99) ~[hibernate-core-5.4.32.Final.jar:5.4.32.Final]
  at org.hibernate.internal.SessionImpl.initializeCollection(SessionImpl.java:2163) ~[hibernate-core-5.4.32.Final.jar:5.4.32.Final]
  at org.hibernate.collection.internal.AbstractPersistentCollection$4.doWork(AbstractPersistentCollection.java:589) ~[hibernate-core-5.4.32.Final.jar:5.4.32.Final]
  at org.hibernate.collection.internal.AbstractPersistentCollection.withTemporarySessionIfNeeded(AbstractPersistentCollection.java:264) ~[hibernate-core-5.4.32.Final.jar:5.4.32.Final]
  at org.hibernate.collection.internal.AbstractPersistentCollection.initialize(AbstractPersistentCollection.java:585) ~[hibernate-core-5.4.32.Final.jar:5.4.32.Final]
  at org.hibernate.collection.internal.AbstractPersistentCollection.read(AbstractPersistentCollection.java:149) ~[hibernate-core-5.4.32.Final.jar:5.4.32.Final]
  at org.hibernate.collection.internal.PersistentSet.hashCode(PersistentSet.java:458) ~[hibernate-core-5.4.32.Final.jar:5.4.32.Final]
  at com.myapp.domain.trigger.Trigger.hashCode(Trigger.java:13) ~[classes/:na]
  at java.base/java.util.HashMap.hash(HashMap.java:340) ~[na:na]
  at java.base/java.util.HashMap.put(HashMap.java:612) ~[na:na]
  at java.base/java.util.HashSet.add(HashSet.java:221) ~[na:na]
  at java.base/java.util.AbstractCollection.addAll(AbstractCollection.java:336) ~[na:na]
  at org.hibernate.collection.internal.PersistentSet.endRead(PersistentSet.java:355) ~[hibernate-core-5.4.32.Final.jar:5.4.32.Final]
  at org.hibernate.engine.loading.internal.CollectionLoadContext.endLoadingCollection(CollectionLoadContext.java:239) ~[hibernate-core-5.4.32.Final.jar:5.4.32.Final]
  at org.hibernate.engine.loading.internal.CollectionLoadContext.endLoadingCollections(CollectionLoadContext.java:224) ~[hibernate-core-5.4.32.Final.jar:5.4.32.Final]
  at org.hibernate.engine.loading.internal.CollectionLoadContext.endLoadingCollections(CollectionLoadContext.java:198) ~[hibernate-core-5.4.32.Final.jar:5.4.32.Final]
  at org.hibernate.loader.plan.exec.process.internal.CollectionReferenceInitializerImpl.endLoading(CollectionReferenceInitializerImpl.java:154) ~[hibernate-core-5.4.32.Final.jar:5.4.32.Final]
  at org.hibernate.loader.plan.exec.process.internal.AbstractRowReader.finishLoadingCollections(AbstractRowReader.java:253) ~[hibernate-core-5.4.32.Final.jar:5.4.32.Final]
  at org.hibernate.loader.plan.exec.process.internal.AbstractRowReader.finishUp(AbstractRowReader.java:211) ~[hibernate-core-5.4.32.Final.jar:5.4.32.Final]
  at org.hibernate.loader.plan.exec.process.internal.ResultSetProcessorImpl.extractResults(ResultSetProcessorImpl.java:96) ~[hibernate-core-5.4.32.Final.jar:5.4.32.Final]
  at org.hibernate.loader.plan.exec.internal.AbstractLoadPlanBasedLoader.executeLoad(AbstractLoadPlanBasedLoader.java:105) ~[hibernate-core-5.4.32.Final.jar:5.4.32.Final]
  at org.hibernate.loader.collection.plan.AbstractLoadPlanBasedCollectionInitializer.initialize(AbstractLoadPlanBasedCollectionInitializer.java:87) ~[hibernate-core-5.4.32.Final.jar:5.4.32.Final]
  at org.hibernate.persister.collection.AbstractCollectionPersister.initialize(AbstractCollectionPersister.java:710) ~[hibernate-core-5.4.32.Final.jar:5.4.32.Final]
  at org.hibernate.event.internal.DefaultInitializeCollectionEventListener.onInitializeCollection(DefaultInitializeCollectionEventListener.java:76) ~[hibernate-core-5.4.32.Final.jar:5.4.32.Final]
  at org.hibernate.event.service.internal.EventListenerGroupImpl.fireEventOnEachListener(EventListenerGroupImpl.java:99) ~[hibernate-core-5.4.32.Final.jar:5.4.32.Final]
  at org.hibernate.internal.SessionImpl.initializeCollection(SessionImpl.java:2163) ~[hibernate-core-5.4.32.Final.jar:5.4.32.Final]
  at org.hibernate.collection.internal.AbstractPersistentCollection$4.doWork(AbstractPersistentCollection.java:589) ~[hibernate-core-5.4.32.Final.jar:5.4.32.Final]
  at org.hibernate.collection.internal.AbstractPersistentCollection.withTemporarySessionIfNeeded(AbstractPersistentCollection.java:264) ~[hibernate-core-5.4.32.Final.jar:5.4.32.Final]
  at org.hibernate.collection.internal.AbstractPersistentCollection.initialize(AbstractPersistentCollection.java:585) ~[hibernate-core-5.4.32.Final.jar:5.4.32.Final]
  at org.hibernate.collection.internal.AbstractPersistentCollection.read(AbstractPersistentCollection.java:149) ~[hibernate-core-5.4.32.Final.jar:5.4.32.Final]
  at org.hibernate.collection.internal.PersistentSet.hashCode(PersistentSet.java:458) ~[hibernate-core-5.4.32.Final.jar:5.4.32.Final]
  at com.myapp.domain.trigger.observation.component.Category.hashCode(Category.java:10) ~[classes/:na]
  at java.base/java.util.HashMap.hash(HashMap.java:340) ~[na:na]
  at java.base/java.util.HashMap.put(HashMap.java:612) ~[na:na]
  at java.base/java.util.HashSet.add(HashSet.java:221) ~[na:na]

如果更改设置为列表,工作正常,为什么?

【问题讨论】:

  • StackOverflowError 可能在您未在此处发布的代码中;因此,如果您不共享堆栈跟踪或代码,则很难提供帮助
  • 您能分享一下Trigger#hashCode()Category#hashCode() 的实现吗?
  • 我使用 lombok:@Data
  • 您可能应该阅读以下内容:thorben-janssen.com/…

标签: hibernate spring-data-jpa spring-data


【解决方案1】:

简而言之: 使用

@NoArgsConstructor
@Getter

足以序列化/反序列化而不是您当前的@Data


它不适用于Set 的原因是您的@Data 中的@EqualsAndHashCode

在这里,每次您插入Trigger,JPA 首先获取您的Trigger 及其依赖项Categorys。例如,您有 1 个Trigger 和 1 个Category

select
    trigger0_.id as id1_1_0_ 
from
    trigger trigger0_ 
where
    trigger0_.id=?

select
    category0_.id as id1_0_0_,
    category0_.name as name2_0_0_ 
from
    category category0_ 
where
    category0_.id=?

因为您将Set&lt;Trigger&gt; 放在Category 类中,所以这个Trigger 的哈希码将使用Lombok @EqualsAndHashCode 计算。这就是为什么JPA 需要加载这个Trigger 以便计算然后是哈希码

select
    trigger0_.categories_id as categori2_2_0_,
    trigger0_.trigger_id as trigger_1_2_0_,
    trigger1_.id as id1_1_1_ 
from
    trigger_categories trigger0_ 
inner join
    trigger trigger1_ 
        on trigger0_.trigger_id=trigger1_.id 
where
    trigger0_.categories_id=?

所以在你第一次插入的时候,没有Trigger,它可以很容易地计算出Category的hashcode,到这里就搞定了

insert into trigger(id) values (?)
insert into trigger_categories(trigger_id, categories_id) values (?, ?)

但是第二次,如果你插入一个新的Trigger和之前的Category,这一次之前的Category链接到了之前的Trigger,所以在计算hashCode的时候会出现循环

select
    trigger0_.categories_id as categori2_2_0_,
    trigger0_.trigger_id as trigger_1_2_0_,
    trigger1_.id as id1_1_1_ 
from
    trigger_categories trigger0_ 
inner join
    trigger trigger1_ 
        on trigger0_.trigger_id=trigger1_.id 
where
    trigger0_.categories_id=?

select
    categories0_.trigger_id as trigger_1_2_0_,
    categories0_.categories_id as categori2_2_0_,
    category1_.id as id1_0_1_,
    category1_.name as name2_0_1_ 
from
    trigger_categories categories0_ 
inner join
    category category1_ 
        on categories0_.categories_id=category1_.id 
where
    categories0_.trigger_id=?

如果你想保留@EqualsAndHashCode,尝试忽略关系字段,例如

@NoArgsConstructor
@Getter
@EqualsAndHashCode(of = {"id"})

或者按照你说的把它变成List

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2017-10-30
    • 1970-01-01
    • 2013-10-24
    • 1970-01-01
    • 2013-01-17
    • 1970-01-01
    • 2021-01-13
    相关资源
    最近更新 更多