【问题标题】:JPA : What is the behaviour of merge with lazy initialized collection?JPA:与惰性初始化集合合并的行为是什么?
【发布时间】:2011-03-09 10:13:17
【问题描述】:

这是导致问题的序列:

  1. 我在数据库中有一个团队记录和 3 个玩家记录。团队实体有一个使用 FetchType.LAZY、CascadeType.ALL 的列表
  2. webui 上的搜索按钮被点击
  3. 在服务器端使用 JPA 查询被调用,找到所有的球队记录,在这种情况下,从查询返回的球队实体只有 1 条记录(其中有球员实体列表的代理)
  4. 将此 teamEntity 映射到 DTO,并将此 DTO 返回到 webui,跳过玩家实体列表的映射
  5. Webui 以 html 形式呈现 DTO,准备好接收来自用户的修改
  6. 用户修改团队的属性,例如成立日期
  7. webui 上的保存按钮被点击
  8. 将 DTO 转换为团队实体,用于更新现有团队记录
  9. 但是在这种情况下,如果我使用 em.merge(teamEntity),团队记录将被更新,但球员列表会发生什么?因为从 DTO 转换为团队实体时,teamEntity 有一个空的玩家实体列表。合并后,我注意到 teamEntity 的细节大小为 0。但在刷新该实体 em.refresh(teamEntity) 后,它将返回 3 的详细信息大小。

我很困惑:

  1. 为什么合并后大小为0?就像不再代表记录一样
  2. 在进行测试之前,我认为将删除详细信息,因为我将 teamEntity 与空详细信息合并。

请赐教:)

谢谢!

【问题讨论】:

    标签: jpa jpa-2.0


    【解决方案1】:

    JPA 规范说:

    合并操作的语义 应用于实体 X 如下:

    • 如果 X 是分离的实体,则状态 的 X 被复制到预先存在的 相同的托管实体实例 X' 身份或 X 的新托管副本 X' 已创建。

    • 如果 X 是一个新实体 实例,一个新的托管实体 实例 X' 被创建并且状态 的 X 被复制到新的托管 实体实例 X'。

    • 如果 X 是 移除的实体实例,一个 IllegalArgumentException 将是 由合并操作(或 事务提交将失败)。

    • 如果 X 是一个托管实体,它被忽略 合并操作,然而, 合并操作被级联到 关系引用的实体 从 X 如果这些关系有 被级联注释 元素值cascade=MERGEcascade=ALL注解。

    • 为所有人 关系引用的实体 Y 从具有级联元素的 X 值cascade=MERGEcascade=ALL, Y 递归地合并为 Y'。对所有人 X 引用的这样的 Y,X' 设置为 参考 Y'。 (注意,如果 X 是 托管然后 X 是相同的对象 X'。)

    • 如果 X 是合并到 X' 的实体, 引用另一个实体 Y, 其中cascade=MERGEcascade=ALL 是 未指定,则导航 来自 X' 的相同关联产生 对托管对象 Y' 的引用 与 Y 相同的持久身份。

    如您所见,这里没有魔法。分离实例的状态被复制到新创建的托管实例中。由于您的分离实例有一个空列表,因此托管实例也会有它。

    进一步的行为取决于关系的所有权,因为数据库中的表示反映了关系的拥有方:

    • 如果Team 是拥有方,TeamPlayers 之间的关系将在刷新期间被破坏(但Player 本身将继续存在,除非您的关系中有orphanRemoval = true)。
    • 否则Team 中的空列表不会影响数据库。

    如果在刷新上下文之前刷新TeamTeam 的所有属性都会被数据库中的值重写,因此Players 的列表会被恢复(因为尚未刷新玩家的空列表)。

    如果您在调用refresh() 之前调用flush(),并且Team 是拥有方,则列表将为空,因为在flush() 期间将关系破坏传播到数据库。

    【讨论】:

    • axtavt:谢谢您的解释!刚刚测试了你提到的所有可能性,现在开始了解情况。关于您的声明:“如果您在调用 refresh() 之前调用 flush(),并且 Team 是拥有方,则列表将为空,因为在 flush() 期间关系的破坏已传播到数据库”,如果orphanRemoval true 被使用。如果没有,孩子们会存活下来,并且刷新会将他们恢复到实体的玩家列表中。再次感谢!
    • 您使用什么 JPA 提供程序?我熟悉 Hibernate,没有 JPA,它也有合并操作和惰性集合,当我尝试从分离的实例中读取未初始化的集合时,我得到一个异常,而不是一个空集合。我认为这是提供商的错误。作为一种解决方案,在 teamPlayer 分离之前初始化该集合。
    猜你喜欢
    • 2015-05-24
    • 2012-09-14
    • 1970-01-01
    • 2016-06-25
    • 1970-01-01
    • 1970-01-01
    • 2023-03-10
    • 2014-05-03
    相关资源
    最近更新 更多