Dmitry Aleksandrov
Dmitry Aleksandrov

Reputation: 700

JPA/Hibernate instance of not working

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

Upvotes: 2

Views: 4244

Answers (5)

Ben Thurley
Ben Thurley

Reputation: 7141

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.

Upvotes: 8

Amine ABBAOUI
Amine ABBAOUI

Reputation: 195

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;
    }

Upvotes: 1

Luka Gorgadze
Luka Gorgadze

Reputation: 161

Not sure, but I think this will work.

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

Upvotes: 4

Alexandre Cartapanis
Alexandre Cartapanis

Reputation: 1523

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-).

Upvotes: 0

Alan Hay
Alan Hay

Reputation: 23226

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 

Upvotes: 0

Related Questions