1

Imagine a situation:

@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();

Now we call this:

p instance of A

always returns false!!

works ok on OpenJPA!

Should I file a bug? Hibernate 4.3.10

  • If you assign "p" to "new A()" then it is obviously an instanceof A, by basic Java (nothing to do with JPA). If "p" is not an instanceof A then what class is it? – Neil Stockton Jan 28 '16 at 13:31
  • instanceof returns an instance type, if you created this instance then it should return true, if not you then you should ask who did it. – Roman C Jan 28 '16 at 13:39
  • 1
    Do you really do `Parent p = new A();` or you get `A` from the database by Hibernate? – v.ladynev Jan 28 '16 at 13:42
  • Post your real code, the case you described here is obviously impossible. – Dragan Bozanovic Jan 28 '16 at 13:46
  • True, this is probably a proxy. See [this answer](http://stackoverflow.com/a/32785467/4754790) for more details. – Dragan Bozanovic Jan 28 '16 at 13:54
  • 1
    There are a lot of answers here. But it is unclear what is the question :) – v.ladynev Jan 28 '16 at 17:23
  • Actually the problem was a bit deeper and http://stackoverflow.com/questions/8770124/hibernate-4-classcastexception-on-lazy-loading-while-eager-works-fine helped me to resolve it. The "Parent" children were used inside of other entity and were set to load in lazy manner.. so it all failed. Thanks for suppoert anyway) – Dmitry Alexandrov Jan 29 '16 at 08:05
  • @DmitryAlexandrov that doesn't mean you have to turn off lazy loading. Don't throw the baby out with the bath water! Honestly instanceof is best avoided anyway. – Ben Thurley Jan 29 '16 at 11:07

5 Answers5

8

This is most likely because hibernate is returning a proxy.

Why does it do this? To implement lazy loading the framework needs to intercept your method calls that return a lazy loaded object or list of objects. It does this so it can first load the object from the DB and then allow your method to run. Hibernate does this by creating a proxy class. If you check the type in debug you should be able to see the actual type is a generated class which does not extend from your base class.

How to get around it? I had this problem once and successfully used the visitor pattern instead of using instanceof. It does add extra complication so it's not everyone's favorite pattern but IMHO it is a much cleaner approach than using instanceof.

If you use instanceof then you typically end up with if...else blocks checking for the different types. As you add more types you will have to re-visit each of these blocks. The advantage of the visitor pattern is that the conditional logic is built into your class hierarchy so if you add more types it makes it less likely you need to change everywhere that uses these classes.

I found this article useful when implementing the visitor pattern.

Draken
  • 3,134
  • 13
  • 34
  • 54
Ben Thurley
  • 6,943
  • 4
  • 31
  • 54
3

Not sure, but I think this will work.

public static boolean instanceOf(Object object, Class<?> superclass) {
    return superclass.isAssignableFrom(Hibernate.getClass(object));
}
Luka Gorgadze
  • 131
  • 1
  • 7
1

You can try to unproxy your object :

/**
     * 
     * @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;
    }
Amine ABBAOUI
  • 175
  • 1
  • 12
0

That is because Hibernate uses run time proxies and OpenJPA, while supporting the proxy approach, prefers either compile time or runtime byte code enhancement.

See:

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 
Alan Hay
  • 22,665
  • 4
  • 56
  • 110
0

Hibernate returns Proxied Object. Rather than implementing a Visitor pattern (as described here), you can use the isAssignableFrom() method on the class you want to test (https://docs.oracle.com/javase/8/docs/api/java/lang/Class.html#isAssignableFrom-java.lang.Class-).

Community
  • 1
  • 1
Alexandre Cartapanis
  • 1,513
  • 3
  • 15
  • 19
  • 2
    Interesting idea but how is isAssignableFrom going to work if instanceof fails? Surely they both check the class hierarchy? – Ben Thurley Jan 28 '16 at 16:57
  • http://stackoverflow.com/questions/496928/what-is-the-difference-between-instanceof-and-class-isassignablefrom isAssignableFrom can check class at runtime, wich is good as proxies are generated at runtime – Alexandre Cartapanis Jan 28 '16 at 18:42
  • 2
    `instanceof` also checks objects at runtime. – Dragan Bozanovic Jan 28 '16 at 21:53
  • 1
    I think what you mean is that instanceof requires the class name at compile time whereas isAssignableFrom can get the class from an object using getClass. That isn't the problem here though. The instanceof compiled but the runtime object doesn't belong to the type hierarchy due to a proxy. – Ben Thurley Jan 28 '16 at 22:15