对于图,我有递归的父和子关系。当在单个hibernate会话上有一个具有50个或多个节点的大型图时,我会收到一条错误消息:“一个具有相同标识符值的不同对象已经与会话相关联”。这是因为Spring中的默认分配大小为50。通过将allocationSize设置为100并将增量设置为100,我克服了此错误。但这并不能解决问题的根源。我可以在一个会话中拥有任意的#节点。我使用saveAndFlush(NodeEntity)抛出此错误消息。
我的问题是,在达到分配大小限制之后,如何强制Hibernate从DB获取主密钥,并能够在单个会话中生成一个新的集主键?
Hibernate版本: hibernate-core-5.4.30.Final.jar
错误:
具有相同标识符值的不同对象已经与会话: graph.entity.NodeEntity#53相关联;嵌套异常是javax.persistence.EntityExistsException:--具有相同标识值的不同对象已经与会话:graph.entity.NodeEntity#53相关联
//GRAPH DATABASE.
CREATE TABLE IF NOT EXISTS node
(
id SERIAL NOT NULL,
name character varying(255) NOT NULL,
parent_id int,
CONSTRAINT node_pkey PRIMARY KEY (id),
CONSTRAINT node_parent_id_fk FOREIGN KEY (parent_id)
REFERENCES node (id) MATCH SIMPLE
ON UPDATE NO ACTION ON DELETE NO ACTION
);
CREATE TABLE IF NOT EXISTS graph
(
id SERIAL NOT NULL,
name character varying(255) NOT NULL,
node_id int,
CONSTRAINT graph_pkey PRIMARY KEY (id),
CONSTRAINT graph_node_fk FOREIGN KEY (node_id)
REFERENCES node (id) MATCH SIMPLE
ON UPDATE NO ACTION ON DELETE NO ACTION,
CONSTRAINT graph_node_id_uk UNIQUE (node_id)
);
public class NodeEntity {
@Id
@SequenceGenerator(name = "node_id_seq", sequenceName = "node_id_seq",allocationSize = 50)
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "node_id_seq")
@Column(name = "id")
int id;
@OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER,orphanRemoval=true)
@JoinColumn(name = "parent_id")
private List<NodeEntity> children = new LinkedList<NodeEntity>();
}
public class GraphEntity{
@Id
@SequenceGenerator(name = "graph_id_seq", sequenceName = "graph_id_seq")
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "graph_id_seq")
@Column(name = "id")
int id;
@OneToOne(cascade = CascadeType.ALL, orphanRemoval = true)
@JoinColumn(name = "NODE_ID", unique = true, nullable = true, insertable = true, updatable = true)
private NodeEntity rootNode;
}
// Way to reproduce this.
void generate150deepChild(int count,NodeEntity node){
if(count == 100){
return
}else{
NodeEntity newNode = new NodeEntity("Child " +count)
node.getChildren().add(newNode);
cout++;
generate150deepChild(count,newNode);
}
}
NodeEntity rootNode = new NodeEntity("ROOT");
// PLEASE NOT if # NodeEntity < 50 everything works fine.
generate150deepChild(0,rootNode);
// PLEASE NOT all ids are zero so they are new node.
GraphEntity graph = new GraphEntity("TEST");
graph.setRootNode(rootNode);
graphRepository.saveAndFlush(graph);
// THIS WILL GENERATE Duplicate Primary key for NodeEntity.实际堆栈跟踪:
Caused by: javax.persistence.EntityExistsException: A different object with the same identifier value was already associated with the session : [graph.entity.NodeEntity#131605]
at org.hibernate.internal.ExceptionConverterImpl.convert(ExceptionConverterImpl.java:123)
at org.hibernate.internal.ExceptionConverterImpl.convert(ExceptionConverterImpl.java:181)
at org.hibernate.internal.ExceptionConverterImpl.convert(ExceptionConverterImpl.java:188)
at org.hibernate.internal.SessionImpl.fireMerge(SessionImpl.java:823)
at org.hibernate.internal.SessionImpl.merge(SessionImpl.java:786)
at org.hibernate.engine.spi.CascadingActions$6.cascade(CascadingActions.java:261)
at org.hibernate.engine.internal.Cascade.cascadeToOne(Cascade.java:499)
at org.hibernate.engine.internal.Cascade.cascadeAssociation(Cascade.java:423)
at org.hibernate.engine.internal.Cascade.cascadeProperty(Cascade.java:220)
at org.hibernate.engine.internal.Cascade.cascadeCollectionElements(Cascade.java:532)
at org.hibernate.engine.internal.Cascade.cascadeCollection(Cascade.java:463)
at org.hibernate.engine.internal.Cascade.cascadeAssociation(Cascade.java:426)
at org.hibernate.engine.internal.Cascade.cascadeProperty(Cascade.java:220)
at org.hibernate.engine.internal.Cascade.cascade(Cascade.java:153)
at org.hibernate.event.internal.AbstractSaveEventListener.cascadeAfterSave(AbstractSaveEventListener.java:459)
at org.hibernate.event.internal.DefaultMergeEventListener.entityIsTransient(DefaultMergeEventListener.java:247)
at org.hibernate.event.internal.DefaultMergeEventListener.onMerge(DefaultMergeEventListener.java:175)
at org.hibernate.event.service.internal.EventListenerGroupImpl.fireEventOnEachListener(EventListenerGroupImpl.java:104)
at org.hibernate.internal.SessionImpl.fireMerge(SessionImpl.java:813)
at org.hibernate.internal.SessionImpl.merge(SessionImpl.java:786)
at org.hibernate.engine.spi.CascadingActions$6.cascade(CascadingActions.java:261)
at org.hibernate.engine.internal.Cascade.cascadeToOne(Cascade.java:499)
at org.hibernate.engine.internal.Cascade.cascadeAssociation(Cascade.java:423)发布于 2022-01-22 22:33:47
更改为使用GenerationType.SEQUENCE策略,它将自动为您处理:
@Id
@SequenceGenerator(name = "node_id_seq", sequenceName = "node_id_seq",allocationSize = 50)
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "node_id_seq")
@Column(name = "id")
int id;你只需要确保以下几点:
sequenceName中@SequenceGenerator中指定的序列)实际上已经配置并与您在@SequenceGenerator中配置的序列对齐。对于PostgreSQL,您可以这样做:alter sequence node_id_seq increment by 50;提示:
根据这,您可以通过配置以下设置,更改为使用pooled或pooled-lo算法来减少DB往返以获得ID:
<property name="hibernate.id.optimizer.pooled.preferred" value="pooled-lo" />https://stackoverflow.com/questions/70817376
复制相似问题