Hibernate @ManyToMany 坚持
Hibernate @ManyToMany persist
我对 @ManyToMany
关系有疑问。
我有一个实体 Category
在数据库中有预定义的插入。
我有实体 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.WhosebugError: 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.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.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]
如果将设置更改为列表,工作正常,为什么?
简明扼要:
使用
@NoArgsConstructor
@Getter
足以满足 serializing/deserializing 而不是您当前的 @Data
它对 Set
不起作用的原因是 @EqualsAndHashCode
在您的 @Data
.
在这里,每次插入 Trigger
时,JPA 首先获取您的 Trigger
及其依赖项 Category
。例如,您有 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<Trigger>
放在 Category
class 中,所以此 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
的哈希码,到这里就完成了
insert into trigger(id) values (?)
insert into trigger_categories(trigger_id, categories_id) values (?, ?)
但是第二次,如果你用之前的Category
插入一个新的Trigger
,这次之前的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
我对 @ManyToMany
关系有疑问。
我有一个实体 Category
在数据库中有预定义的插入。
我有实体 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.WhosebugError: 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.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.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]
如果将设置更改为列表,工作正常,为什么?
简明扼要: 使用
@NoArgsConstructor
@Getter
足以满足 serializing/deserializing 而不是您当前的 @Data
它对 Set
不起作用的原因是 @EqualsAndHashCode
在您的 @Data
.
在这里,每次插入 Trigger
时,JPA 首先获取您的 Trigger
及其依赖项 Category
。例如,您有 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<Trigger>
放在 Category
class 中,所以此 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
的哈希码,到这里就完成了
insert into trigger(id) values (?)
insert into trigger_categories(trigger_id, categories_id) values (?, ?)
但是第二次,如果你用之前的Category
插入一个新的Trigger
,这次之前的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