I have two joined-subclasses as part of a data model like so:
A
-> B
-> C
Here is my mapping:
Code:
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-2.0.dtd" >
<hibernate-mapping package="howto.dao.model">
<class name="Person" table="Person" polymorphism="explicit">
<id
column="person_id"
length="10"
name="PersonId"
type="integer"
>
<generator class="identity" />
</id>
<property
column="country_id"
length="10"
name="CountryId"
not-null="false"
type="integer"
/>
<property
column="error_message"
length="255"
name="ErrorMessage"
not-null="false"
type="string"
/>
<property
column="postal_code"
length="15"
name="PostalCode"
not-null="false"
type="string"
/>
<property
column="middle_name"
length="30"
name="MiddleName"
not-null="false"
type="string"
/>
<property
column="main_phone"
length="30"
name="MainPhone"
not-null="false"
type="string"
/>
<property
column="old_email"
length="30"
name="OldEmail"
not-null="false"
type="string"
/>
<property
column="current_phone"
length="30"
name="CurrentPhone"
not-null="false"
type="string"
/>
<property
column="mod_by"
length="6"
name="ModBy"
not-null="false"
type="string"
/>
<property
column="name_suffix"
length="10"
name="NameSuffix"
not-null="false"
type="string"
/>
<property
column="city"
length="35"
name="City"
not-null="false"
type="string"
/>
<property
column="first_name"
length="30"
name="FirstName"
not-null="false"
type="string"
/>
<property
column="ssn"
length="20"
name="Ssn"
not-null="false"
type="string"
/>
<property
column="nickname"
length="30"
name="Nickname"
not-null="false"
type="string"
/>
<property
column="contact_complete"
length="3"
name="ContactComplete"
not-null="false"
type="java.lang.Byte"
/>
<property
column="fax_phone"
length="30"
name="FaxPhone"
not-null="false"
type="string"
/>
<property
column="address_1"
length="30"
name="Address1"
not-null="false"
type="string"
/>
<property
column="complete_1099"
length="3"
name="Complete1099"
not-null="false"
type="java.lang.Byte"
/>
<property
column="lower_city"
length="35"
name="LowerCity"
not-null="false"
type="string"
/>
<property
column="date_surveyed"
length="23"
name="DateSurveyed"
not-null="false"
type="timestamp"
/>
<property
column="last_name"
length="40"
name="LastName"
not-null="false"
type="string"
/>
<property
column="address_2"
length="30"
name="Address2"
not-null="false"
type="string"
/>
<property
column="lower_last_name"
length="40"
name="LowerLastName"
not-null="false"
type="string"
/>
<property
column="create_date"
length="23"
name="CreateDate"
not-null="false"
type="timestamp"
/>
<property
column="mod_date"
length="23"
name="ModDate"
not-null="false"
type="timestamp"
/>
<property
column="adp_complete"
length="10"
name="AdpComplete"
not-null="false"
type="integer"
/>
<property
column="textbook_id"
length="10"
name="TextbookId"
not-null="false"
type="integer"
/>
<property
column="lower_first_name"
length="30"
name="LowerFirstName"
not-null="false"
type="string"
/>
<property
column="email"
length="255"
name="Email"
not-null="false"
type="string"
/>
<property
column="state"
length="3"
name="State"
not-null="false"
type="string"
/>
<property
column="created_by"
length="6"
name="CreatedBy"
not-null="false"
type="string"
/>
<property
column="lower_state"
length="3"
name="LowerState"
not-null="false"
type="string"
/>
<property
column="name_salutation"
length="10"
name="NameSalutation"
not-null="false"
type="string"
/>
<joined-subclass name="howto.dao.model.Users" t >
<key column="person_id" />
<property
column="password"
length="16"
name="Password"
not-null="false"
type="binary"
/>
<property
column="sybase_password"
length="30"
name="SybasePassword"
not-null="false"
type="string"
/>
<property
column="user_name"
length="80"
name="UserName"
not-null="false"
type="string"
/>
<property
column="last_login"
length="23"
name="LastLogin"
not-null="false"
type="timestamp"
/>
<property
column="lower_user_name"
length="80"
name="LowerUserName"
not-null="false"
type="string"
/>
<property
column="create_date"
length="23"
name="CreateDate"
not-null="false"
type="timestamp"
/>
<property
column="mod_date"
length="23"
name="ModDate"
not-null="false"
type="timestamp"
/>
<property
column="user_id"
length="6"
name="UserId"
not-null="false"
type="string"
/>
<property
column="password_question_id"
length="10"
name="PasswordQuestionId"
not-null="false"
type="integer"
/>
<property
column="signature"
length="255"
name="Signature"
not-null="false"
type="string"
/>
<property
column="password_answer"
length="80"
name="PasswordAnswer"
not-null="false"
type="string"
/>
<property
column="sybase_login"
length="30"
name="SybaseLogin"
not-null="false"
type="string"
/>
<property
column="mod_by"
length="6"
name="ModBy"
not-null="false"
type="string"
/>
<property
column="last_pwd_change"
length="23"
name="LastPwdChange"
not-null="false"
type="timestamp"
/>
<property
column="created_by"
length="6"
name="CreatedBy"
not-null="false"
type="string"
/>
<many-to-one
class="howto.dao.model.SecurityGroup"
column="group_id"
name="Group"
/>
</joined-subclass>
<joined-subclass name="howto.dao.model.PersonEmpInfo">
<key column="person_id" />
<property
column="is_w2"
length="3"
name="IsW2"
not-null="false"
type="java.lang.Byte"
/>
<property
column="date_hired"
length="23"
name="DateHired"
not-null="false"
type="timestamp"
/>
<property
column="country_id"
length="10"
name="CountryId"
not-null="false"
type="integer"
/>
<property
column="vacation_hrs"
length="19"
name="VacationHrs"
not-null="false"
type="big_decimal"
/>
<property
column="error_message"
length="255"
name="ErrorMessage"
not-null="false"
type="string"
/>
<property
column="partner_end"
length="23"
name="PartnerEnd"
not-null="false"
type="timestamp"
/>
<property
column="own_pc"
length="3"
name="OwnPc"
not-null="false"
type="java.lang.Byte"
/>
<property
column="address_3"
length="30"
name="Address3"
not-null="false"
type="string"
/>
<property
column="interviewed_by_id"
length="10"
name="InterviewedById"
not-null="false"
type="integer"
/>
<property
column="mod_by"
length="6"
name="ModBy"
not-null="false"
type="string"
/>
<property
column="consultant_type_id"
length="10"
name="ConsultantTypeId"
not-null="false"
type="integer"
/>
<property
column="mailing"
length="3"
name="Mailing"
not-null="false"
type="java.lang.Byte"
/>
<property
column="address_1"
length="30"
name="Address1"
not-null="false"
type="string"
/>
<property
column="tax_code"
length="2"
name="TaxCode"
not-null="false"
type="string"
/>
<property
column="home_market_id"
length="10"
name="HomeMarketId"
not-null="false"
type="integer"
/>
<property
column="create_date"
length="23"
name="CreateDate"
not-null="false"
type="timestamp"
/>
<property
column="employee_status_id"
length="10"
name="EmployeeStatusId"
not-null="false"
type="integer"
/>
<property
column="salary"
length="19"
name="Salary"
not-null="false"
type="big_decimal"
/>
<property
column="home_division_id"
length="10"
name="HomeDivisionId"
not-null="false"
type="integer"
/>
<property
column="four_week_hrs"
length="19"
name="FourWeekHrs"
not-null="false"
type="big_decimal"
/>
<property
column="is_partner"
length="3"
name="IsPartner"
not-null="false"
type="java.lang.Byte"
/>
<property
column="internal_job_title"
length="255"
name="InternalJobTitle"
not-null="false"
type="string"
/>
<property
column="state"
length="3"
name="State"
not-null="false"
type="string"
/>
<property
column="eeoc_code_id"
length="10"
name="EeocCodeId"
not-null="false"
type="integer"
/>
<property
column="emp_401k"
length="1"
name="Emp401k"
not-null="false"
type="string"
/>
<property
column="postal_code"
length="15"
name="PostalCode"
not-null="false"
type="string"
/>
<property
column="how_heard_about_id"
length="10"
name="HowHeardAboutId"
not-null="false"
type="integer"
/>
<property
column="last_vacation"
length="23"
name="LastVacation"
not-null="false"
type="timestamp"
/>
<property
column="last_week_eligible"
length="23"
name="LastWeekEligible"
not-null="false"
type="timestamp"
/>
<property
column="ten_week_hrs"
length="19"
name="TenWeekHrs"
not-null="false"
type="big_decimal"
/>
<property
column="current_eligible"
length="23"
name="CurrentEligible"
not-null="false"
type="timestamp"
/>
<property
column="on_leave"
length="10"
name="OnLeave"
not-null="false"
type="integer"
/>
<property
column="is_1099"
length="3"
name="Is1099"
not-null="false"
type="java.lang.Byte"
/>
<property
column="manager_id"
length="10"
name="ManagerId"
not-null="false"
type="integer"
/>
<property
column="city"
length="35"
name="City"
not-null="false"
type="string"
/>
<property
column="partner_start"
length="23"
name="PartnerStart"
not-null="false"
type="timestamp"
/>
<property
column="active_order"
length="10"
name="ActiveOrder"
not-null="false"
type="integer"
/>
<property
column="is_posted"
length="3"
name="IsPosted"
not-null="false"
type="java.lang.Byte"
/>
<property
column="lower_city"
length="35"
name="LowerCity"
not-null="false"
type="string"
/>
<property
column="address_2"
length="30"
name="Address2"
not-null="false"
type="string"
/>
<property
column="mod_date"
length="23"
name="ModDate"
not-null="false"
type="timestamp"
/>
<property
column="has_transportation"
length="1"
name="HasTransportation"
not-null="true"
type="boolean"
/>
<property
column="adp_complete"
length="1"
name="AdpComplete"
not-null="true"
type="boolean"
/>
<property
column="own_mac"
length="3"
name="OwnMac"
not-null="false"
type="java.lang.Byte"
/>
<property
column="fifty_two_week_hrs"
length="19"
name="FiftyTwoWeekHrs"
not-null="false"
type="big_decimal"
/>
<property
column="errors"
length="10"
name="Errors"
not-null="false"
type="integer"
/>
<property
column="termination_reason_id"
length="10"
name="TerminationReasonId"
not-null="false"
type="integer"
/>
<property
column="wpm"
length="10"
name="Wpm"
not-null="false"
type="integer"
/>
<property
column="coordinator_id"
length="10"
name="CoordinatorId"
not-null="false"
type="integer"
/>
<property
column="job_title"
length="80"
name="JobTitle"
not-null="false"
type="string"
/>
<property
column="address_change"
length="3"
name="AddressChange"
not-null="false"
type="java.lang.Byte"
/>
<property
column="home_unit_id"
length="10"
name="HomeUnitId"
not-null="false"
type="integer"
/>
<property
column="created_by"
length="6"
name="CreatedBy"
not-null="false"
type="string"
/>
<property
column="lower_state"
length="3"
name="LowerState"
not-null="false"
type="string"
/>
</joined-subclass>
</class>
</hibernate-mapping>
I'm using a DAO design pattern with Spring, and have a service that describes a way of getting a Users instance by querying on the Users.username field:
Code:
public Person findUsersByUsername(final String username) throws Throwable {
HibernateCallback callback = new HibernateCallback(){
public Object doInHibernate(Session session) throws HibernateException, SQLException{
Query q = session.createQuery("from Users as U where U.UserName =:username");
q.setString("username", username);
return q.uniqueResult();
}
};
return (Person)callback.doInHibernate(getSession());
}
Now, because my query explicitly specifies to load a Users object and not a Person object, the instance Hibernate returns is of a Users type.
My business case specifies however that I need to be able to look up a User by username and then get the data from PersonEmpInfo - another descendant of Person. When I try to use the returned Person object from findUsersByUsername(...) to load the PersonEmpInfo object for the ancestor Person of the Users object, I get a ClassCastException.
The service method:
Code:
public PersonEmpInfo findPersonEmpInfo(final Person person) throws Throwable {
HibernateCallback callback = new HibernateCallback(){
public Object doInHibernate(Session session) throws HibernateException, SQLException{
Object o = session.load(PersonEmpInfo.class, person.getPersonId());
return o;
}
};
return (PersonEmpInfo)callback.doInHibernate(getSession());
}
The stack trace:
Code:
java.lang.ClassCastException
at howto.dao.services.hibernate.PersonServiceImpl.findPersonEmpInfo(PersonServiceImpl.java:63)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
at java.lang.reflect.Method.invoke(Unknown Source)
at org.springframework.aop.framework.AopProxyUtils.invokeJoinpointUsingReflection(AopProxyUtils.java:59)
at org.springframework.aop.framework.ReflectiveMethodInvocation.invokeJoinpoint(ReflectiveMethodInvocation.java:149)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:118)
at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:169)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:138)
at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:148)
at $Proxy10.findPersonEmpInfo(Unknown Source)
at howto.actions.PersonAction.execute(PersonAction.java:50)
at org.apache.struts.action.RequestProcessor.processActionPerform(RequestProcessor.java:484)
at org.apache.struts.action.RequestProcessor.process(RequestProcessor.java:274)
at org.apache.struts.action.ActionServlet.process(ActionServlet.java:1482)
at org.apache.struts.action.ActionServlet.doPost(ActionServlet.java:525)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:760)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:853)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:247)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:193)
at org.springframework.orm.hibernate.support.OpenSessionInViewFilter.doFilterInternal(OpenSessionInViewFilter.java:117)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:73)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:213)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:193)
at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:256)
at org.apache.catalina.core.StandardPipeline$StandardPipelineValveContext.invokeNext(StandardPipeline.java:643)
at org.apache.catalina.core.StandardPipeline.invoke(StandardPipeline.java:480)
at org.apache.catalina.core.ContainerBase.invoke(ContainerBase.java:995)
at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:191)
at org.apache.catalina.core.StandardPipeline$StandardPipelineValveContext.invokeNext(StandardPipeline.java:643)
at org.apache.catalina.core.StandardPipeline.invoke(StandardPipeline.java:480)
at org.apache.catalina.core.ContainerBase.invoke(ContainerBase.java:995)
at org.apache.catalina.core.StandardContext.invoke(StandardContext.java:2422)
at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:180)
at org.apache.catalina.core.StandardPipeline$StandardPipelineValveContext.invokeNext(StandardPipeline.java:643)
at org.apache.catalina.valves.ErrorDispatcherValve.invoke(ErrorDispatcherValve.java:171)
at org.apache.catalina.core.StandardPipeline$StandardPipelineValveContext.invokeNext(StandardPipeline.java:641)
at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:163)
at org.apache.catalina.core.StandardPipeline$StandardPipelineValveContext.invokeNext(StandardPipeline.java:641)
at org.apache.catalina.core.StandardPipeline.invoke(StandardPipeline.java:480)
at org.apache.catalina.core.ContainerBase.invoke(ContainerBase.java:995)
at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:174)
at org.apache.catalina.core.StandardPipeline$StandardPipelineValveContext.invokeNext(StandardPipeline.java:643)
at org.apache.catalina.core.StandardPipeline.invoke(StandardPipeline.java:480)
at org.apache.catalina.core.ContainerBase.invoke(ContainerBase.java:995)
at org.apache.coyote.tomcat4.CoyoteAdapter.service(CoyoteAdapter.java:199)
at org.apache.coyote.http11.Http11Processor.process(Http11Processor.java:828)
at org.apache.coyote.http11.Http11Protocol$Http11ConnectionHandler.processConnection(Http11Protocol.java:700)
at org.apache.tomcat.util.net.TcpWorkerThread.runIt(PoolTcpEndpoint.java:584)
at org.apache.tomcat.util.threads.ThreadPool$ControlRunnable.run(ThreadPool.java:683)
at java.lang.Thread.run(Unknown Source)
And a snippet of log data relevant to the issue and my theory as to what the problem is:
Code:
[DEBUG][http8080-Processor4][net.sf.hibernate.impl.SessionImpl.doLoadByClass](1950) loading [howto.dao.model.PersonEmpInfo#253091]
[DEBUG][http8080-Processor4][net.sf.hibernate.impl.SessionImpl.doLoad](2047) attempting to resolve [howto.dao.model.PersonEmpInfo#253091]
[DEBUG][http8080-Processor4][net.sf.hibernate.impl.SessionImpl.doLoad](2063) resolved object in session cache [howto.dao.model.PersonEmpInfo#253091]
It would appear to me that when I am running back to the database to ask for a Person object that is actually of the type PersonEmpInfo that Hibernate is simply fetching the Person object loaded as a Username in the initial method from the cache. This behavior is not desired.
Is there a known approach to this problem?
Thanks In Advance,
Ryan