【问题标题】:Hibernate Proxy Inheritance IssueHibernate 代理继承问题
【发布时间】:2011-11-27 01:41:02
【问题描述】:

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

我有以下课程:

@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() 返回的 Object 是 A_javassisst 类型,即使它应该是 B 的实例。最好的事情是,toString() 方法返回 B@123,这让我相信首先,该对象是 B 类型,但不是...

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

dataTable是PrimeFaces数据表,没试过JSF数据表但不能是primefaces的错……

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

  • WAS 8.0.0.1 (=> OpenWebBeans)
  • CODI 1.1.1
  • MyFaces 2.1.1
  • 休眠 3.6.5

提前感谢您的帮助!

编辑:

我所有的对象都是 B 类型,这意味着数据库包含表“b”中的一个条目,用于表“a”中的每个条目,但有时我从 JPA/Hibernate 返回的对象不是 B 的实例!请我在这方面需要帮助,我不知道为什么会发生这种情况!?!

编辑:

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

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

【问题讨论】:

    标签: hibernate jpa cdi


    【解决方案1】:

    这就是使用多态性时休眠的问题。

    简而言之,当拖网一个休眠模型时,惰性为真,休眠不加载真实类型 - 加载真实类型将需要数据库命中,这不会是惰性的。如果您希望返回 real 类型,则必须检测类并将惰性设置为“代理”。这样做的目的是在您请求代理之前不会对代理进行水合,然后才会查询数据库以确定其真实类型 - 但是,此行为仅适用于仪器,因为仪器拦截了调用。

    在你能看到的地方关闭惰性,看看行为是否改变 - 它应该与惰性关闭一起工作。

    这是使用休眠的陷阱之一。

    【讨论】:

      【解决方案2】:

      症状与休眠限制相匹配。简而言之,多态实体的延迟加载代理对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);
          }
      }
      

      然后写

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

      而不是

      if (entity instanceof B) {
          ...
      

      【讨论】:

      • clazz.isInstance 在我看来与使用 instanceof 具有相同的效果,因此这根本无济于事。看到我的编辑,陷阱似乎不再是陷阱了吗?
      • B.class.isInstance(this)确实和this instanceof B有同样的效果,但是你是在做entity instanceof B,如果entity指的是懒加载代理,this != entity。
      • 是的,你是对的,我没有那样想。无论如何它确实有效!谢谢你的帮助:)
      【解决方案3】:

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

      【讨论】:

        【解决方案4】:

        尝试取消代理您的对象:

        /**
             * 
             * @param <T>
             * @param entity
             * @return
             */
            @SuppressWarnings("unchecked")
            public static <T> T initializeAndUnproxy(T entity) {
                if (entity == null) {
                    // throw new NullPointerException("Entity passed for initialization is null");
                    return null;
                }
                Hibernate.initialize(entity);
                if (entity instanceof HibernateProxy) {
                    entity = (T) ((HibernateProxy) entity).getHibernateLazyInitializer().getImplementation();
                }
                return entity;
            }
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2015-12-26
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多