0

我正在尝试在没有映射复合标识符的情况下声明复合 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 被定义为具有两个字段和根据需要的等于/哈希码的对象。
通过 metadata.getIdentifier() 返回的更改 id 是 SurrogateKey 的一个实例,并且 session.get() 从数据库中获取对象(如果存在)。

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

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

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

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

4

2 回答 2

1

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

现在,回答您的问题:

  1. AFAIK,它按预期工作。
  2. 不。
  3. 这是一个问题吗?
  4. 最好的解决方法是做正确的事并停止使用复合标识符。按照 Hibernate 的建议,使用单列自动生成的 ID,一切都会简单得多。
于 2012-07-05T09:49:07.020 回答
0

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

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

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

于 2012-07-05T11:49:31.460 回答