Hi,
We've encountered a pretty strange error, it seems that the entity manager loads the entity from the second level cache ignoring the entity class
We're using Spring 3.1.3 with Hibernate 4.1.7 (though I tested it with 4.3.0 and the problem still exists). Let's suppose that we have a root entity:
Code:
@Entity
@Inheritance(strategy = InheritanceType.SINGLE_TABLE)
@Table(name = "test_entities")
@Cache(usage = CacheConcurrencyStrategy.READ_WRITE)
public class TestEntityRoot {
    @Id
    @GeneratedValue(generator = "entity_id_generator")
    @GenericGenerator(name = "entity_id_generator", strategy
        = "org.hibernate.id.enhanced.TableGenerator", parameters = {
            @Parameter(name = TableGenerator.CONFIG_PREFER_SEGMENT_PER_ENTITY, value = "true"),
            @Parameter(name = TableGenerator.INCREMENT_PARAM, value = "100"),
            @Parameter(name = TableGenerator.OPT_PARAM, value = "pooled")})
    private Long id;
    public Long getId() {
        return id;
    }
    public void setId(Long id) {
        this.id = id;
    }
}
and two child entities, A
Code:
@Entity
public class TestEntityA extends TestEntityRoot{
}
and B
Code:
@Entity
public class TestEntityB extends TestEntityRoot{
}
And we have the following (minimal) test class:
Code:
@Component
@Transactional
public class TestHibernate implements ITestHibernate {
    @PersistenceContext
    private EntityManager entityManager;
    public static void main(String[] args) {
        ITestHibernate test = new ClassPathXmlApplicationContext("classpath:/META-INF/context.xml").getBean(ITestHibernate.class);
        long id = test.createA();
        test.createB();
        test.loadAll();
        test.testLoad(id);
    }
    @Override
    public long createA() {
        TestEntityA entity = new TestEntityA();
        entityManager.persist(entity);
        return entity.getId();
    }
    @Override
    public void createB() {
        TestEntityB entity = new TestEntityB();
        entityManager.persist(entity);
    }
    @Override
    public void loadAll() {
        entityManager.unwrap(Session.class).createQuery("select E from " + TestEntityRoot.class.
            getName() + " E").setCacheable(true).list();
    }
    @Override
    public void testLoad(long id) {
        TestEntityB b = entityManager.find(TestEntityB.class, id);
        System.out.println(b);
    }
}
If we run this class it would fail in the testLoad() method with
Code:
Exception in thread "main" java.lang.ClassCastException: test.TestEntityA cannot be cast to test.TestEntityB
   at test.TestHibernate.testLoad(TestHibernate.java:57)
   at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
   at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
   at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
   at java.lang.reflect.Method.invoke(Method.java:606)
   at org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:318)
   at org.springframework.aop.framework.ReflectiveMethodInvocation.invokeJoinpoint(ReflectiveMethodInvocation.java:183)
   at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:150)
   at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:110)
   at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172)
   at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:202)
   at com.sun.proxy.$Proxy108.testLoad(Unknown Source)
   at test.TestHibernate.main(TestHibernate.java:33)
So, basically, the test creates two entities A and B, then loads them both using and HQL query and then tries to read B with the id of A. All four methods use the separate transactions and thus each has its own session.
If the @Cache annotation is commented then the test works as expected, i.e., the find() method returns null. But if the cache is enabled then it loads A despite the fact that it was told to load an instance of B. I debugged it a little and it seems that this problem is caused by the following lines in the org.hibernate.event.internal.DefaultLoadEventListener's loadFromSecondLevelCache method:
Code:
      final CacheKey ck = source.generateCacheKey(
            event.getEntityId(),
            persister.getIdentifierType(),
            persister.getRootEntityName()
      );
It seems that it ignores the entity name and uses the root entity instead. Is it a bug in Hibernate or am I missing something? And is there a fix or workaround to this issue besides disabling the second level cache?