Search code examples
javahibernatecachingormhibernate-mapping

Hibernate inheritance and 2nd level cache proxies


Simple app works wrong with following entity structure

@Entity(name="some_table")
@Inheritance(strategy= InheritanceType.SINGLE_TABLE)
@DiscriminatorColumn( name="TYPE", discriminatorType=DiscriminatorType.STRING )
abstract class EntityBase {
    @Id
    @GeneratedValue(strategy= GenerationType.AUTO)
    @Column
    private int id;
}

@Entity
@DiscriminatorValue("EntityA")
@Cacheable
class EntityA extends EntityBase {
    @Column
    private int aColumn;
...
}

@Entity
@DiscriminatorValue("EntityB")
@Cacheable
class EntityB extends EntityBase {
    @Column
    private int bColumn;
...
}

@Entity(name="holder_table")
@Cacheable
class HolderEntity {
    @Id
    @GeneratedValue(strategy= GenerationType.AUTO)
    @Column
    private int id;

    @ManyToOne(fetch=FetchType.LAZY)
    EntityBase holdedEntity;

    ...
}

For first load or without cache everything works well

After loading of HolderEntity instance from cache, holdedEntity field initialised by object with type EntityBase (abstract class).

Pseudocode:

def a = HolderEntity.get(1)
assert a.holdedEntity.class!=EntityBase //ok
a = HolderEntity.get(1) // load from cache
assert a.holdedEntity.class!=EntityBase //fails (actually EntityBase_$$_jvstbbe_0)

During loading from cache hibernate construct entity using special logic: for field it detect class type by variable type (it's EntityBase class) instead of discriminator (final Type[] types = subclassPersister.getPropertyTypes(); in DefaultLoadEventListener) and call method
SessionImpl.internalLoad(String entityName, Serializable id, boolean eager, boolean nullable) witch "instantiate" abstract class and init fields by data from hibernate cache

It works same for lazy & eager loading of holdedEntity Class type stored in AbstractEntityPersister.EntityMetamodel. It looks like, field type for cache is static, but it's should depend on instance of field

How to solve it without disabling of hibernate L2 cache?

Hibernate 4.3.8 (4.3.6 & 4.3.11 tested also)

Update: test class


Solution

  • That's because Hibernate uses proxies for lazy associations. If you want to disable proxies, you need to add the @Proxy annotation to your entity class mappings:

    @Proxy(lazy=false)
    

    If you run this GitHub test, you' see that @Proxy(lazy=false) solves your problem.