I'm encountering an issue with changes made in 4.3.5.Final (and 4.2.12.Final) via HHH-9071 related to Proxy narrowing.
Given:
a class hierarchy where User is a parent class of AdvancedUser
a class UserConfig with a many-to-one to User
When executing a query for a specific UserConfig, the getUser() returns a proxy of User.class. However, immediately executing a second (polymorphic) query for that same user directly, fails to return the AdvancedUser instance. In 4.3.4.Final (and 4.2.11.Final), this same code returns the proper AdvancedUser instance from the second query.
Here's a snippit from a test case (see comment #2):
Code:
// QUERY [A]
{
DetachedCriteria criteria = DetachedCriteria.forClass(UserConfig.class);
criteria.add(Restrictions.eq("id", new Long(10)));
final List<UserConfig> rows = criteria.getExecutableCriteria(HibernateHelper.getSession()).list();
UserConfig uc = rows.get(0);
// (1) Returns a proxy for User.class
// --
User u = uc.getUser();
Assert.assertNotNull(u);
}
// QUERY [B]
try
{
DetachedCriteria criteria = DetachedCriteria.forClass(User.class);
criteria.add(Restrictions.eq("id", new Long(11)));
final List<User> rows = criteria.getExecutableCriteria(HibernateHelper.getSession()).list();
// (2) Returns an AdvancedUser in 4.3.4.Final (or 4.2.11.Final)
// Returns a proxy for User.class in 4.3.5.Final (or 4.2.12.Final)
// --
User u = rows.get(0);
Assert.assertEquals(106, p.getTypeCode().intValue());
// Always throws ClassCastException in 4.3.5.Final (or 4.2.12.Final)
// --
AdvancedUser au = (AdvancedUser)u;
Assert.assertNotNull(au);
}
catch(ClassCastException e)
{
Assert.fail(String.format("The User proxy is not an AdvancedUser instance but should be (HHH-9071). Hibernate version detected as '%s'. %s", Version.getVersionString(), e.getMessage()));
}
It's worth noting that swapping the order of the queries (B before A) causes both to return an AdvancedUser instance.
For reference, the mapping was not changed during the upgrade attempt and is as follows:
Code:
<hibernate-mapping package="com.foo" auto-import="true">
<class name="User" table="m_user" discriminator-value="not null" lazy="true" dynamic-update="true" polymorphism="implicit">
<id name="id" type="java.lang.Long" column="userId" unsaved-value="null">
<generator class="native" />
</id>
<discriminator column="type" type="java.lang.Integer" not-null="true"/>
<version name="lastModified" column="modifyDate" type="secondBasedTimestamp"/>
<property name="typeCode" type="java.lang.Integer" column="type" not-null="true" insert="false" update="false"/>
</class>
<subclass name="AdvancedUser" discriminator-value="106" extends="User">
<one-to-one name="currentDetail" outer-join="false" foreign-key="userId" property-ref="userIdAndStatus" lazy="proxy" cascade="all">
<formula>userId</formula>
<formula>'A'</formula>
</one-to-one>
</subclass>
<class name="UserConfig" table="m_userConfig" lazy="true">
<id name="id" type="java.lang.Long" column="userConfigId" unsaved-value="null">
<generator class="native"/>
</id>
<version name="lastModified" column="lastModifiedTime" type="secondBasedTimestamp"/>
<many-to-one name="user" foreign-key="userId" column="userId" insert="true" update="false" />
</class>
<class name="AdvancedUserDetail" table="m_advancedUserDetail" lazy="true">
<id name="id" type="java.lang.Long" column="advancedUserDetailId" unsaved-value="null">
<generator class="native"/>
</id>
<properties name="userIdAndStatus">
<many-to-one name="advancedUser" column="userId" insert="true" update="false" not-null="false"/>
<property name="statusCode" column="status" type="java.lang.String"/>
</properties>
</class>
</hibernate-mapping>
(keep in mind we're dealing with a legacy schema, which is the reason for the formula'd relations using status 'A')
And here are the hibernate config options:
Code:
<property name="hibernateProperties">
<props>
<prop key="hibernate.dialect">org.hibernate.dialect.MySQL5Dialect</prop>
<prop key="hibernate.bytecode.use_reflection_optimizer">true</prop>
<prop key="hibernate.jdbc.batch_size">0</prop>
<prop key="hibernate.jdbc.use_get_generated_keys">true</prop>
<prop key="hibernate.generate_statistics">false</prop>
<prop key="hibernate.cache.provider_class">org.hibernate.cache.EhCacheProvider</prop>
<prop key="hibernate.cache.region.factory_class">org.hibernate.cache.ehcache.SingletonEhCacheRegionFactory</prop>
<prop key="hibernate.cache.use_second_level_cache">true</prop>
<prop key="hibernate.cache.use_query_cache">true</prop>
<prop key="hibernate.cache.use_structured_entries">false</prop>
<prop key="hibernate.discriminator.ignore_explicit_for_joined">true</prop>
</props>
</property>