Java 如何更新深度克隆实体? 描述

Java 如何更新深度克隆实体? 描述,java,spring,multithreading,hibernate,jpa,Java,Spring,Multithreading,Hibernate,Jpa,我正在开发一个小的java游戏服务器。。。为了在另一个线程中更新和保存游戏,我不得不深度克隆我的一些实体。发生内部休眠异常的其他情况: 因此,我的流程目前看起来如下所示: 标记要更新的游戏实体 将这些实体传递到另一个线程 克隆这些实体 对克隆的实体调用“session.update” 一分钟后重复 它在简单类中工作得很好,但在关系方面存在一个巨大的问题 问题 当我深度克隆我的“chunk”实体(见下文)时,它的集合(inChunk)也会被深度克隆。 我使用深度克隆实体并将其传递给“sessi

我正在开发一个小的java游戏服务器。。。为了在另一个线程中更新和保存游戏,我不得不深度克隆我的一些实体。发生内部休眠异常的其他情况:

因此,我的流程目前看起来如下所示:

  • 标记要更新的游戏实体
  • 将这些实体传递到另一个线程
  • 克隆这些实体
  • 对克隆的实体调用“session.update”
  • 一分钟后重复
它在简单类中工作得很好,但在关系方面存在一个巨大的问题

问题 当我深度克隆我的“chunk”实体(见下文)时,它的集合(inChunk)也会被深度克隆。 我使用深度克隆实体并将其传递给“session.update”

在更新期间,区块始终插入其集合子项。它从不更新它们。 因为我每分钟都会重复这个更新过程(见上文),所以在第二个更新周期会导致“重复条目”异常


//运行数据库操作以在新线程中异步更新实体,完成后返回更新的实体
返回CompletableFuture.runAsync(()->{
var session=database.openSession();
session.beginTransaction();
试一试{
//保存实体
对于(var实体:实体)
更新(实体);
session.flush();
session.clear();
session.getTransaction().commit();
}捕获(例外e){
var messageComposer=新的例外消息编写器(e);
GameExtension.getInstance().trace(“更新:+messageComposer.toString());
session.getTransaction().rollback();
}
session.close();
})。然后应用(v->entities);
@实体
@表(name=“chunk”,uniqueConstraints={@UniqueConstraint(columnNames={“x”,“y”}),索引={@Index(columnList=“x,y”)})
@访问权限(值=AccessType.FIELD)
@选择BeforeUpdate(false)
公共类块扩展了HibernateComponent{
公共int x;
公共智力;
公开日期;
@OneToMany(fetch=FetchType.EAGER)
@JoinTable(name=“chunk\u identity”,joinColumns=@JoinColumn(name=“identity\u id”),inverseJoinColumns=@JoinColumn(name=“id”),inverseForeignKey=@ForeignKey(ConstraintMode.NO\u CONSTRAINT))
@Fetch(FetchMode.JOIN)
@批量大小(大小=50)
public Set inChunk=new LinkedHashSet();
@短暂的
public Set loadedBy=new LinkedHashSet();
公共块(){}
公共块(int x,int y,Date createdOn){
这个.x=x;
这个。y=y;
this.createdOn=createdOn;
}
}
/**
*表示{@link com.artemis.Entity}的ID,该ID对于每个实体都是唯一的,主要是数据库ID
*/
@实体
@表(name=“identity”)
@访问权限(AccessType.FIELD)
@SQLInsert(sql=“在重复密钥更新id=values(id),tag=values(tag),typeID=values(typeID)”上插入标识(tag,typeID,id)值(?,,?)
@选择BeforeUpdate(值=false)
公共类标识扩展组件{
@Id公共长Id;
公共字符串标签;
公共字符串类型ID;
公共标识(){}
公共标识(长id、字符串标记、字符串类型id){
this.id=id;
this.tag=tag;
this.typeID=typeID;
}
}
问题: 为什么hibernate总是插入我的孩子而不检查他们是否已经插入?我能做些什么来防止/解决这个问题

我尝试过的事情
  • 正在删除“@SelectBeforeUpdate”批注
  • 将级联添加到“Chunk.InChunk”与“All”或“Merge”的关系中
  • 运行“session.merge”而不是“session.update”,这会导致相同的重复条目异常,只需忽略其子项已插入的事实
他们都不工作

免责声明 我需要克隆实体,否则会导致内部休眠异常,请参见上面的链接

我正在使用一个名为“DeppClone”的库在另一个线程中克隆我的实体


如果需要更多信息,请写评论。这是一个复杂的问题,很难概括,但我希望我已经正确地描述了它。

你应该考虑实现两个主要的变化:

  • 关注点分离(读写)
  • 选择一致性原则
  • 使用#1,您不必再处理ConcurrentModificationException。修改实体只涉及一个组件或服务

    第二个组件或服务只读取单独上下文中的实体。所有实体必须是不变的,以避免实施事故

    两者都需要一个契约或接口(一般意义上说,不是Java接口),它将您带到#2。一旦发生更改,只读上下文中的任何加速技巧都必须刷新缓存或重新读取/合并更改。根据你的观点,你必须牺牲三者中的一个,这取决于你喜欢的一致性策略。由于没有关于写入、实体和读取约束数量的提示,因此我无法提供更详细的建议

    如果我现在必须实现它,那么至少有3个模块(Java 11/Jigsaw):

    • 仅包含接口的API模块,例如跨以下两个模块强制使用统一的get方法
    • Writer模块,使用所有的Hibernate魔法来编写实体和一种侦听器模式,这样下一个模块就可以注册自己了
    • 具有Hibernate magic的读卡器模块,用于读取实体并将其提供给其他人(通过REST、RCP等),向编写器注册自身,以便在发生更改时进行某种刷新
    读卡器模块还可以在启动时读取数据库,并使用编写器生成的事件。这些事件将是一种命令,在读卡器模块的内存中更改,而不是从数据库中重新读取。所以哟