Hibernate代理继承问题

Chr*_*kov 3 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{...}
Run Code Online (Sandbox Code Playgroud)

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

FROM A
Run Code Online (Sandbox Code Playgroud)

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

<h:dataTable value="#{bean.entries}" var="entry">...</h:dataTable>
Run Code Online (Sandbox Code Playgroud)

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

<h:commandLink value="click" actionListener="#{bean.doSomething}">
    <f:setPropertyActionListener value="#{entry}" target="#{bean.selected}" />
</h:commandLink>
Run Code Online (Sandbox Code Playgroud)

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

public void doSomething(ActionEvent event){
    if(delegate.getSelected() instanceof B){
        // special
    }else{
        delegate.doSomething(event);
    }
}
Run Code Online (Sandbox Code Playgroud)

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

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

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

所有这些都在以下环境中:

  • WAS 8.0.0.1(=> 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对我来说很好!

mer*_*ike 13

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

如果,Hibernate将返回延迟加载代理而不是实体化实体

  1. 你明确要求代理 session.load()
  2. 作为由另一个加载实体的单值,延迟提取的关联引用的实体的替身
  3. 先前已使用上述方法在同一个休眠会话中创建了代理

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

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

class A {
    boolean isInstanceOf(Class<X extends A> clazz) {
        return clazz.isInstance(this);
    }
}
Run Code Online (Sandbox Code Playgroud)

然后写

if (entity.isInstanceOf(B.class)) {
    B b = (B) entity;
    // work with b
}
Run Code Online (Sandbox Code Playgroud)

代替

if (entity instanceof B) {
    ...
Run Code Online (Sandbox Code Playgroud)

  • `B.class.isInstance(this)`确实和`this instanceof B`有相同的效果,但是你正在做`实体instanceof B`,如果`entity`指的是延迟加载代理,`this!= entity` . (2认同)