Given
* Batch fetch is enabled * 2nd Level cache is enabled * Polymorphic entity annotated with {{org.hibernate.annotations.Cache}}
When
* find entity by id using id from another type
Then
* ClassCastException occurs
{code:java}@Entity @Table(name = "THE_ENTITY") @DiscriminatorColumn(name = "DISC_COL", discriminatorType = DiscriminatorType.INTEGER) @Cache(usage = CacheConcurrencyStrategy.READ_WRITE) public abstract class AbstractEntity { @Id @Column(name = "ID") Integer id; }
@Entity @DiscriminatorValue("1") public class EntityA extends AbstractEntity { }
@Entity @DiscriminatorValue("2") public class EntityB extends AbstractEntity { }{code}
{code:java} @Override protected void configure(Configuration configuration) { super.configure(configuration);
configuration.setProperty(AvailableSettings.SHOW_SQL, Boolean.TRUE.toString()); configuration.setProperty(AvailableSettings.FORMAT_SQL, Boolean.TRUE.toString());
configuration.setProperty(AvailableSettings.DEFAULT_BATCH_FETCH_SIZE, "10"); configuration.setProperty(AvailableSettings.USE_SECOND_LEVEL_CACHE, Boolean.TRUE.toString()); configuration.setProperty(AvailableSettings.CACHE_REGION_FACTORY, JCacheRegionFactory.class.getName()); configuration.setProperty("hibernate.javax.cache.provider", EhcacheCachingProvider.class.getName()); } @Test public void hhhXXXXTest() throws Exception { Integer id = 123;
try (Session s = openSession()) { Transaction tx = s.beginTransaction(); EntityA entityA = new EntityA(); entityA.id = id; s.persist(entityA); tx.commit(); }
try (Session s = openSession()) { EntityA entityA = s.find(EntityA.class, id); assertThat(entityA.id).isEqualTo(id); }
try (Session s = openSession()) { EntityB entityB = s.find(EntityB.class, id); assertThat(entityB).isNull(); } }{code}
{noformat}java.lang.ClassCastException: class org.hibernate.bugs.EntityA cannot be cast to class org.hibernate.bugs.EntityB (org.hibernate.bugs.EntityA and org.hibernate.bugs.EntityB are in unnamed module of loader 'app') at org.hibernate.bugs.ORMUnitTestCase.hhhXXXXTest(ORMUnitTestCase.java:81) at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77) at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.base/java.lang.reflect.Method.invoke(Method.java:568) at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:59) at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12) at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:56) at org.hibernate.testing.junit4.ExtendedFrameworkMethod.invokeExplosively(ExtendedFrameworkMethod.java:45) at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17) at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:26) at org.junit.internal.runners.statements.RunAfters.evaluate(RunAfters.java:27) at org.junit.internal.runners.statements.FailOnTimeout$CallableStatement.call(FailOnTimeout.java:299) at org.junit.internal.runners.statements.FailOnTimeout$CallableStatement.call(FailOnTimeout.java:293) at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:264) at java.base/java.lang.Thread.run(Thread.java:833){noformat}
Some notes:
* This issue doesn’t happen in latest hibernate 5.6.x * This also happens with infinispan cache, not only ehcache. So, it doesn’t seem related to the cache engine used. * Disabling 2nd level cache avoids the issue * Disabling batch fetch also avoids the issue
Although trying to find EntityB with EntityA id could be considered an application/logic bug by itself, I would expect hibernate to have a consistent behaviour enabling or disabling batch fetch; or enabling or disabling 2nd level cache and do not throw ClassCastException, but rather return null.
Failing test case to be attached and also available at [https://github.com/ratoaq2/HHH-16565|https://github.com/ratoaq2/HHH-16565|smart-link] |
|