首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >JPA分配大小导致AllocationSize后自动生成的序列重用

JPA分配大小导致AllocationSize后自动生成的序列重用
EN

Stack Overflow用户
提问于 2022-01-22 21:42:27
回答 1查看 243关注 0票数 0

对于图,我有递归的父和子关系。当在单个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相关联

代码语言:javascript
运行
复制
 //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.

实际堆栈跟踪:

代码语言:javascript
运行
复制
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)
EN

回答 1

Stack Overflow用户

发布于 2022-01-22 22:33:47

更改为使用GenerationType.SEQUENCE策略,它将自动为您处理:

代码语言:javascript
运行
复制
@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;

你只需要确保以下几点:

  1. 不要手动设置每个节点的ID。
  2. DB中的相关序列(即在sequenceName@SequenceGenerator中指定的序列)实际上已经配置并与您在@SequenceGenerator中配置的序列对齐。对于PostgreSQL,您可以这样做:
代码语言:javascript
运行
复制
alter sequence node_id_seq increment by 50;

提示:

根据,您可以通过配置以下设置,更改为使用pooledpooled-lo算法来减少DB往返以获得ID:

代码语言:javascript
运行
复制
<property name="hibernate.id.optimizer.pooled.preferred" value="pooled-lo" />
票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/70817376

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档