Hibernate 3.2 rc2
Spring 2.0 rc2
Oracle 9i
Java 5
I need another pair of eyes looking at this because I can't see the problem. In a nutshell my problem has to do with my many-to-one mapping not loading when a value is present, even if I attempt to force a load by setting lazy to false. There are no errors produced when running the code just a null value where there should be a proxy or the related object. I have tried this code using 3.1.2, 3.1.3, and the current 3.2 rc2 with the same results.
I have a parent/child relationship were the child is mapped using single table per hierarchy pattern. The parent is ChangeRequest and the abstract child is ChangeAction. ChangeAction has two subclasses PayChangeAction and JobChangeAction.
Here are the relevant parts of the ChangeRequest mapping:
Code:
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping schema="PAF">
<class
name="paf.change.domain.ChangeRequest"
table="CHANGE_REQUESTS">
<id
name="id"
column="CHANGE_REQUEST_ID"
type="long" />
<!-- snip -->
<set
name="actions"
cascade="all">
<key
column="CHANGE_REQUEST_ID"
not-null="true"/>
<one-to-many class="paf.change.domain.ChangeAction"/>
</set>
<!-- snip -->
</class>
</hibernate-mapping>
Here are the relevant parts of the ChangeAction mapping:
Code:
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping schema="PAF">
<class
name="paf.change.domain.ChangeAction"
table="CHANGE_ACTIONS"
abstract="true">
<id
name="id"
column="CHANGE_ACTION_ID"
type="long" />
<discriminator
column="TYPE_CD"
type="string"/>
<!-- snip -->
<subclass
name="paf.change.domain.JobChangeAction"
discriminator-value="JOB">
<property
name="effectiveOn"
column="EFFECTIVE_DT"
type="zion.core.dao.hibernate.usertype.DateUserType" />
<many-to-one
name="jobChangeReason"
class="hr.employee.domain.ActionReason"
unique="true">
<column name="JOB_CHANGE_ACTION_ID" sql-type="VARCHAR"/>
<column name="JOB_CHANGE_REASON_ID" sql-type="VARCHAR"/>
</many-to-one>
<many-to-one
name="job"
class="hr.employee.domain.Job"
unique="true">
<column name="JOB_ID" sql-type="VARCHAR"/>
<column name="JOB_SET_ID" sql-type="VARCHAR"/>
</many-to-one>
</subclass>
<subclass
name="paf.change.domain.PayChangeAction"
discriminator-value="PAY">
<property
name="effectiveOn"
column="EFFECTIVE_DT"
type="zion.core.dao.hibernate.usertype.DateUserType" />
<!-- snip -->
<property
name="payChangeAmount1"
column="PAY_CHANGE_AMT1"
type="zion.core.dao.hibernate.usertype.MoneyUserType" />
<many-to-one
name="payChangeReason1"
class="hr.employee.domain.ActionReason"
unique="true">
<column name="PAY_CHANGE_ACTION_ID1" sql-type="VARCHAR"/>
<column name="PAY_CHANGE_REASON_ID1" sql-type="VARCHAR"/>
</many-to-one>
<!-- snip -->
</subclass>
</class>
</hibernate-mapping>
I use the following code in my ChangeRequestDAO implementation to load a ChangeRequest:
Code:
public ChangeRequest read(Long id) {
return (ChangeRequest) getHibernateTemplate().get(ChangeRequest.class, id);
}
I also created a ChangeActionDAO implementation to load ChangeActions (I don't actually need this DAO since hibernate will take care of the associations for me but I have it for testing purposes and whatever lies down the road):
Code:
public ChangeAction read(Long id) {
return (ChangeAction) getHibernateTemplate().get(ChangeAction.class, id);
}
Here are the relevant parts of the log (debug) when reading a PayChangeAction:
Code:
08:29:19,248 DEBUG SessionImpl:222 - opened session at timestamp: 4726812504633344
08:29:19,264 DEBUG JDBCTransaction:54 - begin
08:29:19,264 DEBUG ConnectionManager:415 - opening JDBC connection
08:29:19,279 DEBUG JDBCTransaction:59 - current autocommit status: true
08:29:19,279 DEBUG JDBCTransaction:62 - disabling autocommit
08:29:19,826 DEBUG Loader:1777 - loading entity: [paf.change.domain.ChangeAction#100001]
08:29:19,842 DEBUG AbstractBatcher:358 - about to open PreparedStatement (open PreparedStatements: 0, globally: 0)
08:29:19,842 DEBUG SQL:393 -
select
changeacti0_.CHANGE_ACTION_ID as CHANGE1_2_0_,
changeacti0_.UPDATE_TS as UPDATE3_2_0_,
changeacti0_.UPDATE_NAME as UPDATE4_2_0_,
changeacti0_.VERSION as VERSION2_0_,
changeacti0_.EFFECTIVE_DT as EFFECTIVE6_2_0_,
changeacti0_.JOB_CHANGE_ACTION_ID as JOB7_2_0_,
changeacti0_.JOB_CHANGE_REASON_ID as JOB8_2_0_,
changeacti0_.JOB_ID as JOB9_2_0_,
changeacti0_.JOB_SET_ID as JOB10_2_0_,
changeacti0_.NEW_ANNUAL_PAY as NEW11_2_0_,
changeacti0_.NEW_HOURLY_PAY as NEW12_2_0_,
changeacti0_.NEXT_PAY_CHANGE_DT as NEXT13_2_0_,
changeacti0_.PAY_CHANGE_AMT1 as PAY14_2_0_,
changeacti0_.PAY_CHANGE_AMT2 as PAY15_2_0_,
changeacti0_.PAY_CHANGE_AMT3 as PAY16_2_0_,
changeacti0_.PAY_CHANGE_ACTION_ID1 as PAY17_2_0_,
changeacti0_.PAY_CHANGE_REASON_ID1 as PAY18_2_0_,
changeacti0_.PAY_CHANGE_ACTION_ID2 as PAY19_2_0_,
changeacti0_.PAY_CHANGE_REASON_ID2 as PAY20_2_0_,
changeacti0_.PAY_CHANGE_ACTION_ID3 as PAY21_2_0_,
changeacti0_.PAY_CHANGE_REASON_ID3 as PAY22_2_0_,
changeacti0_.TYPE_CD as TYPE2_2_0_
from
PAF.CHANGE_ACTIONS changeacti0_
where
changeacti0_.CHANGE_ACTION_ID=?
Hibernate:
select
changeacti0_.CHANGE_ACTION_ID as CHANGE1_2_0_,
changeacti0_.UPDATE_TS as UPDATE3_2_0_,
changeacti0_.UPDATE_NAME as UPDATE4_2_0_,
changeacti0_.VERSION as VERSION2_0_,
changeacti0_.EFFECTIVE_DT as EFFECTIVE6_2_0_,
changeacti0_.JOB_CHANGE_ACTION_ID as JOB7_2_0_,
changeacti0_.JOB_CHANGE_REASON_ID as JOB8_2_0_,
changeacti0_.JOB_ID as JOB9_2_0_,
changeacti0_.JOB_SET_ID as JOB10_2_0_,
changeacti0_.NEW_ANNUAL_PAY as NEW11_2_0_,
changeacti0_.NEW_HOURLY_PAY as NEW12_2_0_,
changeacti0_.NEXT_PAY_CHANGE_DT as NEXT13_2_0_,
changeacti0_.PAY_CHANGE_AMT1 as PAY14_2_0_,
changeacti0_.PAY_CHANGE_AMT2 as PAY15_2_0_,
changeacti0_.PAY_CHANGE_AMT3 as PAY16_2_0_,
changeacti0_.PAY_CHANGE_ACTION_ID1 as PAY17_2_0_,
changeacti0_.PAY_CHANGE_REASON_ID1 as PAY18_2_0_,
changeacti0_.PAY_CHANGE_ACTION_ID2 as PAY19_2_0_,
changeacti0_.PAY_CHANGE_REASON_ID2 as PAY20_2_0_,
changeacti0_.PAY_CHANGE_ACTION_ID3 as PAY21_2_0_,
changeacti0_.PAY_CHANGE_REASON_ID3 as PAY22_2_0_,
changeacti0_.TYPE_CD as TYPE2_2_0_
from
PAF.CHANGE_ACTIONS changeacti0_
where
changeacti0_.CHANGE_ACTION_ID=?
08:29:19,857 DEBUG AbstractBatcher:374 - about to open ResultSet (open ResultSets: 0, globally: 0)
08:29:19,857 DEBUG Loader:1164 - result row: EntityKey[paf.change.domain.ChangeAction#100001]
08:29:19,888 DEBUG AbstractBatcher:381 - about to close ResultSet (open ResultSets: 1, globally: 1)
08:29:19,904 DEBUG AbstractBatcher:366 - about to close PreparedStatement (open PreparedStatements: 1, globally: 1)
08:29:19,904 DEBUG TwoPhaseLoad:107 - resolving associations for [paf.change.domain.PayChangeAction#100001]
08:29:19,951 DEBUG TwoPhaseLoad:206 - done materializing entity [paf.change.domain.PayChangeAction#100001]
08:29:19,951 DEBUG StatefulPersistenceContext:748 - initializing non-lazy collections
08:29:19,951 DEBUG Loader:1808 - done entity load
08:29:19,951 DEBUG SessionImpl:825 - initializing proxy: [hr.employee.domain.ActionReason#component[actionId,reasonId]{actionId=PAY, reasonId=MER}]
08:29:19,967 DEBUG Loader:1777 - loading entity: [hr.employee.domain.ActionReason#component[actionId,reasonId]{actionId=PAY, reasonId=MER}]
08:29:19,967 DEBUG AbstractBatcher:358 - about to open PreparedStatement (open PreparedStatements: 0, globally: 0)
08:29:19,967 DEBUG SQL:393 -
select
actionreas0_.ACTION as ACTION16_0_,
actionreas0_.REASON as REASON16_0_,
actionreas0_.DESCR as DESCR16_0_,
actionreas0_.AVAILABLE as AVAILABLE16_0_,
actionreas0_.UPDATE_TS as UPDATE5_16_0_
from
PSFT_DATAMART.PSFT_REASON actionreas0_
where
actionreas0_.ACTION=?
and actionreas0_.REASON=?
Hibernate:
select
actionreas0_.ACTION as ACTION16_0_,
actionreas0_.REASON as REASON16_0_,
actionreas0_.DESCR as DESCR16_0_,
actionreas0_.AVAILABLE as AVAILABLE16_0_,
actionreas0_.UPDATE_TS as UPDATE5_16_0_
from
PSFT_DATAMART.PSFT_REASON actionreas0_
where
actionreas0_.ACTION=?
and actionreas0_.REASON=?
08:29:19,967 DEBUG AbstractBatcher:374 - about to open ResultSet (open ResultSets: 0, globally: 0)
08:29:19,967 DEBUG Loader:1164 - result row: EntityKey[hr.employee.domain.ActionReason#component[actionId,reasonId]{actionId=PAY, reasonId=MER}]
08:29:19,967 DEBUG AbstractBatcher:381 - about to close ResultSet (open ResultSets: 1, globally: 1)
08:29:19,967 DEBUG AbstractBatcher:366 - about to close PreparedStatement (open PreparedStatements: 1, globally: 1)
08:29:19,967 DEBUG TwoPhaseLoad:107 - resolving associations for [hr.employee.domain.ActionReason#component[actionId,reasonId]{actionId=PAY, reasonId=MER}]
08:29:19,967 DEBUG TwoPhaseLoad:206 - done materializing entity [hr.employee.domain.ActionReason#component[actionId,reasonId]{actionId=PAY, reasonId=MER}]
08:29:19,967 DEBUG StatefulPersistenceContext:748 - initializing non-lazy collections
08:29:19,967 DEBUG Loader:1808 - done entity load
08:29:19,967 DEBUG JDBCTransaction:152 - rollback
08:29:19,998 DEBUG JDBCTransaction:193 - re-enabling autocommit
08:29:19,998 DEBUG JDBCTransaction:163 - rolled back JDBC Connection
08:29:19,998 DEBUG ConnectionManager:296 - transaction completed on session with on_close connection release mode; be sure to close the session to release JDBC resources!
08:29:19,998 DEBUG ConnectionManager:435 - releasing JDBC connection [ (open PreparedStatements: 0, globally: 0) (open ResultSets: 0, globally: 0)]
08:29:19,998 DEBUG ConnectionManager:296 - transaction completed on session with on_close connection release mode; be sure to close the session to release JDBC resources!
Here are the relevant parts of the log (debug) when reading a JobChangeAction:
Code:
08:29:19,998 DEBUG SessionImpl:222 - opened session at timestamp: 4726812508151808
08:29:19,998 DEBUG JDBCTransaction:54 - begin
08:29:19,998 DEBUG ConnectionManager:415 - opening JDBC connection
08:29:20,013 DEBUG JDBCTransaction:59 - current autocommit status: true
08:29:20,013 DEBUG JDBCTransaction:62 - disabling autocommit
08:29:20,326 DEBUG Loader:1777 - loading entity: [paf.change.domain.ChangeAction#100002]
08:29:20,326 DEBUG AbstractBatcher:358 - about to open PreparedStatement (open PreparedStatements: 0, globally: 0)
08:29:20,326 DEBUG SQL:393 -
select
changeacti0_.CHANGE_ACTION_ID as CHANGE1_2_0_,
changeacti0_.UPDATE_TS as UPDATE3_2_0_,
changeacti0_.UPDATE_NAME as UPDATE4_2_0_,
changeacti0_.VERSION as VERSION2_0_,
changeacti0_.EFFECTIVE_DT as EFFECTIVE6_2_0_,
changeacti0_.JOB_CHANGE_ACTION_ID as JOB7_2_0_,
changeacti0_.JOB_CHANGE_REASON_ID as JOB8_2_0_,
changeacti0_.JOB_ID as JOB9_2_0_,
changeacti0_.JOB_SET_ID as JOB10_2_0_,
changeacti0_.NEW_ANNUAL_PAY as NEW11_2_0_,
changeacti0_.NEW_HOURLY_PAY as NEW12_2_0_,
changeacti0_.NEXT_PAY_CHANGE_DT as NEXT13_2_0_,
changeacti0_.PAY_CHANGE_AMT1 as PAY14_2_0_,
changeacti0_.PAY_CHANGE_AMT2 as PAY15_2_0_,
changeacti0_.PAY_CHANGE_AMT3 as PAY16_2_0_,
changeacti0_.PAY_CHANGE_ACTION_ID1 as PAY17_2_0_,
changeacti0_.PAY_CHANGE_REASON_ID1 as PAY18_2_0_,
changeacti0_.PAY_CHANGE_ACTION_ID2 as PAY19_2_0_,
changeacti0_.PAY_CHANGE_REASON_ID2 as PAY20_2_0_,
changeacti0_.PAY_CHANGE_ACTION_ID3 as PAY21_2_0_,
changeacti0_.PAY_CHANGE_REASON_ID3 as PAY22_2_0_,
changeacti0_.TYPE_CD as TYPE2_2_0_
from
PAF.CHANGE_ACTIONS changeacti0_
where
changeacti0_.CHANGE_ACTION_ID=?
Hibernate:
select
changeacti0_.CHANGE_ACTION_ID as CHANGE1_2_0_,
changeacti0_.UPDATE_TS as UPDATE3_2_0_,
changeacti0_.UPDATE_NAME as UPDATE4_2_0_,
changeacti0_.VERSION as VERSION2_0_,
changeacti0_.EFFECTIVE_DT as EFFECTIVE6_2_0_,
changeacti0_.JOB_CHANGE_ACTION_ID as JOB7_2_0_,
changeacti0_.JOB_CHANGE_REASON_ID as JOB8_2_0_,
changeacti0_.JOB_ID as JOB9_2_0_,
changeacti0_.JOB_SET_ID as JOB10_2_0_,
changeacti0_.NEW_ANNUAL_PAY as NEW11_2_0_,
changeacti0_.NEW_HOURLY_PAY as NEW12_2_0_,
changeacti0_.NEXT_PAY_CHANGE_DT as NEXT13_2_0_,
changeacti0_.PAY_CHANGE_AMT1 as PAY14_2_0_,
changeacti0_.PAY_CHANGE_AMT2 as PAY15_2_0_,
changeacti0_.PAY_CHANGE_AMT3 as PAY16_2_0_,
changeacti0_.PAY_CHANGE_ACTION_ID1 as PAY17_2_0_,
changeacti0_.PAY_CHANGE_REASON_ID1 as PAY18_2_0_,
changeacti0_.PAY_CHANGE_ACTION_ID2 as PAY19_2_0_,
changeacti0_.PAY_CHANGE_REASON_ID2 as PAY20_2_0_,
changeacti0_.PAY_CHANGE_ACTION_ID3 as PAY21_2_0_,
changeacti0_.PAY_CHANGE_REASON_ID3 as PAY22_2_0_,
changeacti0_.TYPE_CD as TYPE2_2_0_
from
PAF.CHANGE_ACTIONS changeacti0_
where
changeacti0_.CHANGE_ACTION_ID=?
08:29:20,326 DEBUG AbstractBatcher:374 - about to open ResultSet (open ResultSets: 0, globally: 0)
08:29:20,326 DEBUG Loader:1164 - result row: EntityKey[paf.change.domain.ChangeAction#100002]
08:29:20,326 DEBUG AbstractBatcher:381 - about to close ResultSet (open ResultSets: 1, globally: 1)
08:29:20,326 DEBUG AbstractBatcher:366 - about to close PreparedStatement (open PreparedStatements: 1, globally: 1)
08:29:20,341 DEBUG TwoPhaseLoad:107 - resolving associations for [paf.change.domain.JobChangeAction#100002]
08:29:20,341 DEBUG TwoPhaseLoad:206 - done materializing entity [paf.change.domain.JobChangeAction#100002]
08:29:20,341 DEBUG StatefulPersistenceContext:748 - initializing non-lazy collections
08:29:20,341 DEBUG Loader:1808 - done entity load
08:29:20,341 DEBUG JDBCTransaction:152 - rollback
08:29:20,357 DEBUG JDBCTransaction:193 - re-enabling autocommit
08:29:20,357 DEBUG JDBCTransaction:163 - rolled back JDBC Connection
08:29:20,357 DEBUG ConnectionManager:296 - transaction completed on session with on_close connection release mode; be sure to close the session to release JDBC resources!
08:29:20,357 DEBUG ConnectionManager:435 - releasing JDBC connection [ (open PreparedStatements: 0, globally: 0) (open ResultSets: 0, globally: 0)]
08:29:20,357 DEBUG ConnectionManager:296 - transaction completed on session with on_close connection release mode; be sure to close the session to release JDBC resources!
When I run my unit test I use DBUnit to initialize the database to a known state and then attempt to read a ChangeAction by ID. If I read a PayChangeAction I find that the many-to-one assocation with ActionReason (payChangeReason1) loads as expected, and that expectation is a proxy.
However when I attempt to read a JobChangeAction I find that the many-to-one assocation with ActionReason (jobChangeReason) is always null and the many-to-one assocation with Job (job) is always null despite the fact that there are values in the tables and that the ChangeAction record correctly relates to those records. The fact that the Job association does not work is one thing but the fact that the ActionReason association works in one subclass and not the other is rather strange.
I have searched the forums and found some people that have had a similar problem as far back as version 2.x but there never seemed to be solution posted, or at least a solution that worked for me. Even tenwit posted the same problem but he was able to get around it by setting fetch to "join" rather than to "select". I would give this a try but it is not a feasible solution because my subclasses have more than one many-to-one associations. JobChangeAction has two, PayChangeAction actually has three, and there are several other ChangeAction subclasses on the way, one of which has seven assocations.
Has anybody encountered this problem before? Is there something obvious that I am missing? What additional information could I provide that would be helpful in solving this problem?
I am hoping that my long history of finding solutions to my problems right after I post the question will work yet again.
*crosses fingers*