Hibernate代理继承问题

时间:2011-09-28 17:13:45

标签: hibernate jpa cdi

我正在使用每个具体类的继承策略表,今天我遇到了一些非常奇怪的事情。我仍然不知道这个问题的原因,但让我解释它是什么......

我有以下课程:

@Entity
@Table(name="...")
@Inheritance(strategy=InheritanceType.JOINED)
public class A implements Serializable{...}

@Entity
@Table(name="...")
@PrimaryKeyJoinColumn(name="...")
public class B extends A{...}

看起来很简单,我用来检索条目的查询基本上是这样的:

FROM A

这也会返回B的实例,这是我所期望的。 这些条目被加载到视图的支持bean中(Bean是视图编组的,持久化上下文已经结束)。视图通过EL访问bean以检索dataTable的条目:

<h:dataTable value="#{bean.entries}" var="entry">...</h:dataTable>

在dataTable中,我有一个这样的commandLink:

<h:commandLink value="click" actionListener="#{bean.doSomething}">
    <f:setPropertyActionListener value="#{entry}" target="#{bean.selected}" />
</h:commandLink>

bean使用类型为A的对象,但是如果所选条目的类型是子类,则在调用actionListener-Expression时调用的CDI-Decorator会执行其他操作:

public void doSomething(ActionEvent event){
    if(delegate.getSelected() instanceof B){
        // special
    }else{
        delegate.doSomething(event);
    }
}

现在它变得复杂了。 if是“有时”输入但不是在预期时输入。调试显示delegate.getSelected()返回的对象属于A_javassisst类型,即使它应该是B的实例。最好的事情是,toString()方法返回B @ 123让我首先相信,对象属于B类,但它不是......

现在我们来问我的问题......那里到底发生了什么?我已经考虑过在保存dataTable的状态时可能会出现的一些序列化问题,但我不确定(数据表是否应该检索value-expression返回的值并使用这些值进行遍历或者可能会中断国家?)。

dataTable是一个PrimeFaces数据表,还没有尝试过JSF数据表,但它不能成为主要的错误......

以下环境中的所有这些:

  • WAS 8.0.0.1(=&gt; OpenWebBeans)
  • CODI 1.1.1
  • MyFaces 2.1.1
  • Hibernate 3.6.5

提前感谢您的帮助!

编辑:

我的所有对象都是B类,意味着db在表“b”中包含表“a”中每个条目的条目,但有时我从JPA / Hibernate返回的对象不是B的实例!我需要帮助,我不知道为什么会这样!!

编辑:

我的诊断错误,返回的类型是正确的!我真的有一个类型B的代理,而不是类型A的代理。我的问题是在setter之前调用了装饰的方法。这比与Hibernate相关的JSF更多!

我没有遇到你报告的问题和陷阱,使用instanceof对我来说很好用!

3 个答案:

答案 0 :(得分:13)

症状符合冬眠限制。简而言之,多态实体的延迟加载代理对instanceof的响应与它们代理的实体的响应不同。这是因为代理是在实体的实际类型尚未知晓的情况下实例化的,并且作为Java对象,在创建后无法更改其运行时类。

如果

,Hibernate将返回延迟加载代理而不是实体化实体
  1. 您明确要求使用session.load()
  2. 的代理
  3. 作为由来自另一个已加载实体的单值,延迟提取的关联引用的实体的替身
  4. 以前使用上述方法
  5. 在同一个hibernate会话中创建了代理

    案例2是最常见的。我已经编写了一个单元测试来检查我的映射是否存在多态惰性非空单值关联,以提醒我这种可能性。

    有一些方法可以使用instanceof和使用代理进行强制转换,但它们并非无足轻重。要进行强制转换,必须为多态实体声明代理接口,并且所有代码必须针对这些接口而不是实体类进行编程。然后,Hibernate将拥有一个代理实现实体可能拥有的所有接口,允许所有转换。对于instanceof,您可以声明:

    class A {
        boolean isInstanceOf(Class<X extends A> clazz) {
            return clazz.isInstance(this);
        }
    }
    

    然后写

    if (entity.isInstanceOf(B.class)) {
        B b = (B) entity;
        // work with b
    }
    

    而不是

    if (entity instanceof B) {
        ...
    

答案 1 :(得分:1)

使用多态时这就是hibernate的问题。

简而言之,在拖拽hibernate模型时,懒惰是真的,hibernate不会加载真正的类型 - 加载真实类型会引起数据库命中并且不会是懒惰的。如果要返回 real 类型,则必须检测类并将延迟设置为“proxy”。这样做不会给你的代理提供水合,直到你要求它为止,只有这样才能查询数据库以确定它的真实类型 - 然而这种行为只有当仪器拦截调用时才能进行检测。

在任何可以看到它的地方关闭懒惰并查看行为是否发生变化 - 它应该与懒惰关闭。

这是使用hibernate的一个陷阱。

答案 2 :(得分:0)

我认为 clazz.isInstance(this)有效,因为这是目标对象的实例。这与instanceof的区别。考虑到该对象在该调用中未被复制。