【问题标题】:JPA/Hibernate instance of not working不工作的 JPA/Hibernate 实例
【发布时间】:2016-01-28 13:07:31
【问题描述】:

想象一种情况:

@javax.persistence.Inheritance(strategy=javax.persistence.InheritanceType.JOINED)
@javax.persistence.DiscriminatorColumn
@javax.persistence.Entity
@javax.persistence.Table(name="PARENT")
public abstract class Parent{
...
}

@javax.persistence.Entity
@javax.persistence.Table(name="A")
public class A extends Parent{
...
}

@javax.persistence.Entity
@javax.persistence.Table(name="B")
public class B extends Parent{
...
}


Parent p = new A();

现在我们称之为:

p instance of A

总是返回 false!!

在 OpenJPA 上工作正常!

我应该提交错误吗?休眠 4.3.10

【问题讨论】:

  • 如果您将“p”分配给“new A()”,那么它显然是 A 的实例,由基本的 Java (与 JPA 无关)。如果“p”不是 A 的实例,那么它是什么类?
  • instanceof 返回一个实例类型,如果你创建了这个实例那么它应该返回 true,如果不是你那么你应该问是谁做的。
  • 你是真的做Parent p = new A();还是你从Hibernate的数据库中得到A
  • 贴出你的真实代码,你这里描述的情况显然是不可能的。
  • 这里有很多答案。但目前还不清楚问题是什么:)

标签: java hibernate jpa


【解决方案1】:

这很可能是因为休眠正在返回一个代理。

为什么要这样做?要实现延迟加载,框架需要拦截返回延迟加载对象或对象列表的方法调用。它这样做是为了首先从数据库中加载对象,然后允许您的方法运行。 Hibernate 通过创建代理类来做到这一点。如果您在调试中检查类型,您应该能够看到实际类型是生成的类,它不是从您的基类扩展的。

如何绕过它?我曾经遇到过这个问题并成功地使用了访问者模式而不是使用instanceof。它确实增加了额外的复杂性,因此它不是每个人都喜欢的模式,但恕我直言,它比使用instanceof 更干净。

如果您使用instanceof,那么您通常最终会使用if...else 块检查不同的类型。当您添加更多类型时,您将不得不重新访问这些块中的每一个。访问者模式的优点是条件逻辑内置于类层次结构中,因此如果您添加更多类型,则不太可能需要在使用这些类的任何地方进行更改。

我发现这个article 在实现访问者模式时很有用。

【讨论】:

    【解决方案2】:

    不确定,但我认为这会起作用。

    public static boolean instanceOf(Object object, Class<?> superclass) {
        return superclass.isAssignableFrom(Hibernate.getClass(object));
    }
    

    【讨论】:

      【解决方案3】:

      您可以尝试取消代理您的对象:

      /**
           * 
           * @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;
          }
      

      【讨论】:

        【解决方案4】:

        这是因为 Hibernate 使用运行时代理和 OpenJPA,同时支持代理方法,更喜欢编译时或运行时字节码增强。

        见:

        http://openjpa.apache.org/entity-enhancement.html

        //Hibernate
        Entity e = repository.load(entityId); // may return a proxy 
        
        //OpenJPA
        Entity e = repository.load(entityId); //will return an (enhanced) actual instance of E 
        

        【讨论】:

          【解决方案5】:

          Hibernate 返回代理对象。您可以在要测试的类 (https://docs.oracle.com/javase/8/docs/api/java/lang/Class.html#isAssignableFrom-java.lang.Class-) 上使用 isAssignableFrom() 方法,而不是实现访问者模式(如 here 所述)。

          【讨论】:

          • 有趣的想法,但是如果 instanceof 失败,isAssignableFrom 将如何工作?他们肯定都会检查类层次结构吗?
          • stackoverflow.com/questions/496928/… isAssignableFrom 可以在运行时检查类,这很好,因为代理是在运行时生成的
          • instanceof 还在运行时检查对象。
          • 我认为您的意思是 instanceof 在编译时需要类名,而 isAssignableFrom 可以使用 getClass 从对象中获取类。但这不是这里的问题。 instanceof 已编译,但运行时对象由于代理不属于类型层次结构。
          猜你喜欢
          • 1970-01-01
          • 2012-02-22
          • 2015-10-20
          • 2011-06-22
          • 1970-01-01
          • 1970-01-01
          • 2012-12-27
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多