I am getting a ClassCastException thrown when calling a method that simply returns 'this'. The object is being proxied with the CGLIBLazyInitializer and is loaded as an item in a collection where the many end of the one to many association is the supertype of the type of the object in question.
I think the error is due to the fact that the proxy type is a direct descendent of the base class (as this is the type of the one to many association) and the return type of the method being proxied is that of the sub-class.
To summarise the static structure, CreditFacilityConfiguration has a one to many relationship with CreditFacility. CreditFacility has two sub-types; CreditLimit and CreditLine. CreditFacility has an abstract method named baseLimit(). In CreditLine the implementation of this method returns parent.baseLimit(). In CreditLimit this method returns simply 'this'. Calling the method on a CreditLimit object from CreditFacilityConfiguration causes the ClassCastException.
I have included relevent code snippets and mapping files to clarify.
I can think of a workaround in the short term which is a bit fiddly - I would change the return type of the baseLimit method to CreditFacility. I would then have to use a visitor on the returned object because I need to call methods that are only declared on CreditLimit (and I can't downcast a proxy). I'm sure this will work but I suspect that this problem may need investigating further.
It appears that a similar problem in this area was reported and fixed in version 3.0 beta 4 (HHH-136). I've had a look at the code for this and it seems to confirm that returning the proxy when the return value from the method is the same as the proxied object would cause this exception in my scenario - the key difference being that the type of the many end of the one to many is the base class and the return type of the proxied method is a subclass.
Any information or suggestions would be greatly appreciated. Thanks.
Hibernate version: 3.0.5
Code:
from CreditFacilityConfigurationImpl.java
Code:
...
public void modifyReserveFacilityConfig(final Date effectiveDate,
final Double amount)
throws ConfigurationException {
LOGGER.entering(LOG_CLASSNAME, "modifyReserveFacilityConfig",
new Object[] { effectiveDate, amount });
CreditFacility effective = effectiveReserveFacility(effectiveDate);
if (effective != null) {
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("Found an effective reserve facility: " +
new BeanWrapper(effective));
}
final CreditLimit baseLimit = effective.baseLimit();
...
from CreditLimitImpl.java
Code:
...
/**
* There is one and only one limit per facility line, therefore this method
* will always return the current object.
*
* @see CreditLimit#baseLimit()
*/
public CreditLimit baseLimit() {
return this;
}
...
from CreditLineImpl.java
Code:
...
/**
* @see CreditFacility#baseLimit()
*/
public CreditLimit baseLimit() {
if (LOGGER.isTraceEnabled()) {
LOGGER.entering(LOG_CLASSNAME, "baseLimit");
}
final CreditLimit baseLimit = parentFacility().baseLimit();
if (LOGGER.isTraceEnabled()) {
LOGGER.exiting(LOG_CLASSNAME, "baseLimit",
new BeanWrapper(baseLimit));
}
return baseLimit;
}
...
Mapping documents:from CreditFacility.hbm.xml:
Code:
...
<hibernate-mapping default-cascade="none">
<class name="com.wcg.wsf.businesspartners.CreditFacilityImpl" table="CREDIT_FACILITY" dynamic-insert="false" dynamic-update="false">
<id name="id" type="java.lang.Long" unsaved-value="null">
<column name="ID" sql-type="NUMBER(19)"/>
<generator class="native">
</generator>
</id>
<set name="apportionments" order-by="PARENT_FACILITY_FK" lazy="false" fetch="select" inverse="true" cascade="all-delete-orphan">
<key foreign-key="CREDIT_LINE_PARENT_FAC">
<column name="PARENT_FACILITY_FK" sql-type="NUMBER(19)"/>
</key>
<one-to-many class="com.wcg.wsf.businesspartners.CreditLineImpl"/>
</set>
<many-to-one name="config" class="com.wcg.wsf.businesspartners.CreditFacilityConfigurationImpl" foreign-key="CREDIT_FACILITY_CONF_FK" lazy="proxy" fetch="select">
<column name="CONFIG_FK" not-null="true" sql-type="NUMBER(19)"/>
</many-to-one>
<joined-subclass name="com.wcg.wsf.businesspartners.CreditLineImpl" table="CREDIT_LINE" dynamic-insert="false" dynamic-update="false" abstract="false">
<key foreign-key="CREDIT_LINE_I_FKC">
<column name="ID" sql-type="NUMBER(19)"/>
</key>
<many-to-one name="parentFacility" class="com.wcg.wsf.businesspartners.CreditFacilityImpl" cascade="none" foreign-key="CREDIT_LINE_PARENT_FAC" lazy="proxy" fetch="select">
<column name="PARENT_FACILITY_FK" not-null="true" sql-type="NUMBER(19)"/>
</many-to-one>
</joined-subclass>
<joined-subclass name="com.wcg.wsf.businesspartners.CreditLimitImpl" table="CREDIT_LIMIT" dynamic-insert="false" dynamic-update="false" abstract="false">
<key foreign-key="CREDIT_LIMIT_I_FKC">
<column name="ID" sql-type="NUMBER(19)"/>
</key>
</joined-subclass>
</class>
</hibernate-mapping>
from CreditFacilityConfiguration.hbm.xml:
Code:
...
<hibernate-mapping default-cascade="none">
<class name="com.wcg.wsf.businesspartners.CreditFacilityConfigurationImpl" table="CREDIT_FAC_CONF" dynamic-insert="false" dynamic-update="false">
<id name="id" type="java.lang.Long" unsaved-value="null">
<column name="ID" sql-type="NUMBER(19)"/>
<generator class="native">
</generator>
</id>
<set name="primaryFacilitySequence" order-by="PRIM_CONF_FK" lazy="false" fetch="select" inverse="false" cascade="all-delete-orphan">
<key foreign-key="PRIM_CONF_FKC">
<column name="PRIM_CONF_FK" sql-type="NUMBER(19)"/>
</key>
<one-to-many class="com.wcg.wsf.businesspartners.CreditFacilityImpl"/>
</set>
...
</class>
</hibernate-mapping>
Stack trace:Code:
Caused by: java.lang.ClassCastException: com.wcg.wsf.businesspartners.CreditFacilityImpl$$EnhancerByCGLIB$$aba7cbb3
at com.wcg.wsf.businesspartners.CreditFacilityImpl$$EnhancerByCGLIB$$aba7cbb3.baseLimit(<generated>)
at com.wcg.wsf.businesspartners.CreditFacilityConfigurationImpl.modifyPrimaryFacilityConfig(CreditFacilityConfigurationImpl.java:358)