【问题标题】:How to work around Session.get() strange behaviour with composite-id?如何使用复合 ID 解决 Session.get() 的奇怪行为?
【发布时间】:2012-07-05 09:36:41
【问题描述】:

我正在尝试对声明复合 id 而没有映射复合标识符的对象执行获取会话。
使用的休眠版本是 3.5.5。

获取代码是通用的,读取包装实际数据的容器对象:

ClassMetadata metadata = 
          session.getSessionFactory().getClassMetadata(wrapper.getDomainClass());
Serializable id = metadata.getIdentifier(wrapper, EntityMode.POJO);
return session.get(wrapper.getDomainClass(), id, LockOptions.UPGRADE);

代码对实际映射一无所知,因此它必须查阅有关 id 的元数据。

如果映射定义如下:

<hibernate-mapping default-access="field">
  <class name="Wrapper"
      entity-name="Data"
      table="DATA">
    <composite-id>
      <key-property name="identifier" column="identifier" />
      <key-property name="version" column="version" />
    </composite-id>

    <component name="domainObject" class="Data">
      <property name="source" column="source" />
    </component>                    
  </class>
</hibernate-mapping>

没有复合标识符类,id 等于对象本身并且等于包装器引用。
当我执行 session.get() 而不是从数据库中获取对象时,它返回与传入 id 的对象相同的对象(不是相等的对象,而是相同的对象实例)。
更新: 实际上 session.get() 从数据库in 加载对象,传递的 id 对象并将其返回。我监督最初认为它会跳过加载。

目前我找到的解决方案是引入映射的复合标识符并将映射更改为:

<hibernate-mapping default-access="field">
  <class name="Wrapper"
      entity-name="Data1"
      table="DATA_1">
    <composite-id  class="SurrogateKey" mapped="true">
      <key-property name="identifier" column="identifier" />
      <key-property name="version" column="version" />
    </composite-id>

    <component name="domainObject" class="Data">
      <property name="source" column="source" />
    </component>                    
  </class>
</hibernate-mapping>

SurrogateKey 被定义为具有两个字段和 equals/hashcode 的对象。
使用 metadata.getIdentifier() 返回的更改 id 是 SurrogateKey 的一个实例,并且 session.get() 从数据库中获取对象(如果存在)。

映射修复的问题是条件和 HQL 的属性名称从 identifier 更改为 id.identifier,这实际上破坏了许多现有代码。

我目前正在探索的事情是:

  1. 有没有办法让 session.get() 在不声明 Id 类的情况下工作(我知道这是一种不鼓励的做法,但所需的更改量可能会令人望而却步)?
  2. 是否可以告诉 hibernate 像以前一样处理属性,而不添加 id。在他们面前?
  3. 将 hibernate 升级到 v4(由于依赖项目和审批流程并不容易)?
  4. 还有其他可用的选项/解决方法吗?

到目前为止,我只设法使上述解决方案有效,但我正在寻找侵入性较小的解决方案,并希望能提供任何线索、建议和指向相关文档的指针。

【问题讨论】:

    标签: java hibernate composite-id


    【解决方案1】:

    您的第一个代码 sn-p 中的 wrapper 是什么?如果它是一个附加实体(我怀疑它是),显然,session.get() 将返回具有相同标识符的附加实体,并且由于标识符是实体本身,它将返回给定的附加实体。会话中任何给定实体总是只有一个实例。

    现在,回答您的问题:

    1. AFAIK,它按预期工作。
    2. 没有。
    3. 这是一个问题吗?
    4. 最好的解决方法是做正确的事并停止使用复合标识符。按照 Hibernate 的建议,使用单列自动生成的 ID,一切都会变得简单得多。

    【讨论】:

    • 在 get 完成时包装器未附加到会话。它至少有一个我们想要替换的对象键。以前的版本可以在数据库中,也可以保存在同一个会话中。这是一个批量保存/更新过程,自动生成的 ID 不是一个选项,因为 ID 是由外部系统生成的。如果我们使用它们,那么我们将不得不切换到执行查询并手动批量关联对象。如果 1 仅对附加实体是正确的行为,但对于分离的实体,它看起来不正确,并且它对于映射和非映射键是不同的。
    • 使用注释,您可以在第二个问题中做您想做的事情,通过使用@Id 映射实体的多个字段,并指定具有相同属性名称和类型的 ID 类。不知道 XML 映射是否可行。
    • 我已经更新了初始帖子并发布了解决方案。现在对我来说,它看起来像是休眠中的一个错误。 Session 没有考虑实例化器行为的某些方面。
    【解决方案2】:

    问题来自于使用有意义的对象作为 session.get() 方法的 id。

    原来 session.get() 有副作用并修改了它的参数(这发生在 PojoInstantiator.instantiate() 的深处)。在加载时,当休眠检测到 id 类等于映射类时,它会跳过实例创建并使用传递给方法的 id 对象而不是实例化新实例。该对象通过数据库覆盖现有字段而得到补充。

    解决方案是,如果 metadata.getIdentifier() 在没有映射键时返回对象,则创建对象的克隆。该克隆将被 get() 补水并返回。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2022-07-26
      • 1970-01-01
      • 2013-10-21
      • 2016-03-09
      • 2017-05-25
      • 1970-01-01
      • 2019-12-05
      相关资源
      最近更新 更多