考虑一个具有Players
集合的Team
实体。Team
映射允许级联合并并将更新保存到Players
。
我有一个Team
实体,它在数据库中有 4 个Players
。
我的服务允许用户指定Team
和Player
,然后执行以下操作:
- 创建现有
Team
A 的克隆 B - 从 A 中删除
Player
C,并将其添加到 BTeam
集合中
以上所有操作都在一个事务中完成,该事务仅执行数据库查询。它不执行更新。
然后使用第二个事务将实体保存到数据库。但是,我不确定如何处理这个问题。
我现有的逻辑包含以下内容:
- 合并
Team
B,这将有效地保存团队 B,然后合并Player
C。Player
C 的版本号递增 - 合并
Team
A。但是,此操作会失败,因为休眠会话的Team
A 实例仍具有对旧Player
C 版本号的引用。
如何保存这两个实体?我是否必须单独保存Player
C 并避免使用级联?
在存储内容时,合并操作不应该是您的首选操作。它用于将分离的实体与实体管理器合并回去。一个对象在 JPA 中可以具有的不同状态的简短回顾(您可能需要阅读以下内容):
- 瞬态:尚未存储在数据库中的新对象(因此实体管理器不知道)。没有设置 id。
- 托管:实体管理器跟踪的对象。托管对象是您在事务范围内使用的对象,对托管对象所做的所有更改将在事务提交后自动存储。
- 已分离:以前托管的对象,在转换提交后仍可访问。(事务外部的托管对象。设置了 ID。
对于 Hibernate/JPA,瞬态对象和分离对象之间的区别在于它是否具有其 id 字段的值。
为了将对象存储在数据库中,该方法根据对象的状态而有所不同。
- 瞬态:实体管理器.持久。这将存储对象,并使其成为托管对象。
- 托管:无。事务完成后,将更新托管对象。
- 已分离:实体管理器.合并。由于实体管理器不再识别该对象,因此您需要重新引入它。但请注意,由于 persist-方法 使传递给它的对象成为托管对象,因此 merge-method不会。相反,它返回对象的托管副本。
因此,在此基础上,让我们看看您在这里要做什么。
首先,从实体管理器加载一个对象。假设您在事务上下文中执行此操作,则在管理此对象时。然后,复制对象,生成一个新的瞬态对象。然后,对这两个对象进行一些更改。
接下来,提交事务。然后,您的托管对象将更新,而您的暂时性对象将保持暂时性。然后,托管对象在事务中幸存下来,变得分离。然后存储瞬态对象,这里应该使用 persist-method,而不是 merge 方法。如果有的话,为了更新分离的对象(虽然应该已经更新了),你需要把它传递给 merge-method。
因此,为了从团队到玩家获得正确的级联,您需要将持久化和合并添加为级联操作。
此外,在制作克隆对象时,请确保不要复制 id,因为这会使它与 Hibernate 所能知道的所有旧对象相同。
如果 A 队中的玩家 C 和 B 队中的玩家 C 的版本号不同,则它不能是同一个对象。当您分离对象时,可能会发生一些事情,导致两个团队中的玩家 C 不是同一个对象(但不确定这一点......