I'm running into a problem at the moment in my code, whereby when Hibernate loads the object graph, it is initialising a map with key objects which are not fully hydrated, and contain only their ID. Unfortunately, my hashcode for the key objects is based off a unique business key (the key's name property), and therefore when I try to obtain anything out of the map at a later date, I don't get anything.
I've managed to simplify the problem down to the following situation:
A Customer has a primary contact, of type User (many-to-one). A User has a map of Customers (many-to-many), with the corresponding value of the map being a UserCustomerDetails object which I can attach various properties (in real life Sets of objects, but in my test case now a simple String property).
The problem occurs when I load the Customer by primary key - it seems to start hydrating the entry (initialises its ID), and then cascades down to the User primary contact. Hibernate then fully hydrates the User, but in doing so uses the session-cache Customer object in the map (and therefore uses a zero-value hashcode). The hydration of the Customer is then completed.
Any help is much appreciated - I can't work out whether my mapping is just completely wrong, or I'm trying to do something with Hibernate in a dodgy way.
My mapping files are as follows:
Customer
Code:
<hibernate-mapping>
<class name="com.qxlva.qure.datamodel.bo.Customer" table="CUSTOMER" dynamic-update="false" dynamic-insert="false">
<id name="id" column="CUSTOMER_ID" type="int" unsaved-value="0">
<generator class="sequence">
<param name="sequence">CUSTOMER_SEQ</param>
</generator>
</id>
<property name="archived" type="boolean" update="true" insert="true" access="property" column="ARCHIVED"/>
<property name="name" type="string" update="true" insert="true" access="property" column="CUSTOMER_NAME" length="20"/>
<many-to-one name="primaryContact" class="com.qxlva.qure.datamodel.bo.User" cascade="none" outer-join="auto" update="true" insert="true" access="property" column="USER_ID"/>
</class>
</hibernate-mapping>
UserCode:
<hibernate-mapping>
<class name="com.qxlva.qure.datamodel.bo.User" table="QURE_USER" dynamic-update="false" dynamic-insert="false">
<id name="id" column="USER_ID" type="int" unsaved-value="0">
<generator class="sequence">
<param name="sequence">QURE_USER_SEQ</param>
</generator>
</id>
<map name="customerDetails" lazy="false" sort="unsorted" inverse="false" cascade="all-delete-orphan">
<key column="USER_ID"/>
<index-many-to-many class="com.qxlva.qure.datamodel.bo.Customer" column="CUSTOMER_ID"/>
<one-to-many class="com.qxlva.qure.datamodel.bo.UserCustomerDetails"/>
</map>
<property name="password" type="string" update="true" insert="true" access="property" column="PASSWORD" length="20" not-null="true"/>
<property name="userName" type="string" update="true" insert="true" access="property" column="USERNAME" length="30" not-null="true"/>
</class>
</hibernate-mapping>
UserCustomerDetailsCode:
<hibernate-mapping>
<class name="com.qxlva.qure.datamodel.bo.UserCustomerDetails" table="USER_CUSTOMER_DETAILS" dynamic-update="false" dynamic-insert="false">
<id name="id" column="USER_CUSTOMER_DETAILS_ID" type="int" unsaved-value="0">
<generator class="sequence">
<param name="sequence">USER_CUST_DETAILS_SEQ</param>
</generator>
</id>
<property name="name" type="string" update="true" insert="true" access="property" column="CUSTOMER_POSITION" length="20"/>
</class>
</hibernate-mapping>
The Java code for carrying out the load by primary key is:
Code:
Session currentSession = sessionFactory.openSession();
persistedObject = currentSession.load(Customer.class, primaryKey);
currentSession.close();
My Customer hashcode is as follows:
Code:
return (name != null ? name.hashCode() : 0);
I'm currently working on Hibernate 2.1.4 and Oracle 9.2.0.4.
The debug Hibernate log is as follows:
Code:
16:26:09,521 DEBUG SessionImpl:1982 - loading [com.qxlva.qure.datamodel.bo.Customer#1]
16:26:09,521 DEBUG SessionImpl:2079 - attempting to resolve [com.qxlva.qure.datamodel.bo.Customer#1]
16:26:09,531 DEBUG SessionImpl:2112 - object not resolved in any cache [com.qxlva.qure.datamodel.bo.Customer#1]
16:26:09,531 DEBUG EntityPersister:416 - Materializing entity: [com.qxlva.qure.datamodel.bo.Customer#1]
16:26:09,531 DEBUG BatcherImpl:196 - about to open: 0 open PreparedStatements, 0 open ResultSets
16:26:09,531 DEBUG SQL:237 - select customer0_.CUSTOMER_ID as CUSTOMER1_1_, customer0_.ARCHIVED as ARCHIVED1_, customer0_.CUSTOMER_NAME as CUSTOMER3_1_, customer0_.USER_ID as USER_ID1_, user1_.USER_ID as USER_ID0_, user1_.PASSWORD as PASSWORD0_, user1_.USERNAME as USERNAME0_ from CUSTOMER customer0_, QURE_USER user1_ where customer0_.CUSTOMER_ID=? and customer0_.USER_ID=user1_.USER_ID(+)
16:26:09,531 DEBUG BatcherImpl:241 - preparing statement
16:26:09,561 DEBUG IntegerType:46 - binding '1' to parameter: 1
16:26:09,631 DEBUG Loader:197 - processing result set
16:26:09,641 DEBUG IntegerType:68 - returning '1' as column: USER_ID0_
16:26:09,641 DEBUG Loader:405 - result row: 1, 1
16:26:09,641 DEBUG Loader:536 - Initializing object from ResultSet: 1
16:26:09,641 DEBUG Loader:605 - Hydrating entity: com.qxlva.qure.datamodel.bo.User#1
16:26:09,641 DEBUG StringType:68 - returning 'password1' as column: PASSWORD0_
16:26:09,651 DEBUG StringType:68 - returning 'simonk' as column: USERNAME0_
16:26:09,661 DEBUG Loader:536 - Initializing object from ResultSet: 1
16:26:09,661 DEBUG Loader:605 - Hydrating entity: com.qxlva.qure.datamodel.bo.Customer#1
16:26:09,661 DEBUG BooleanType:68 - returning 'false' as column: ARCHIVED1_
16:26:09,661 DEBUG StringType:68 - returning 'Ni SSA' as column: CUSTOMER3_1_
16:26:09,661 DEBUG IntegerType:68 - returning '1' as column: USER_ID1_
16:26:09,671 DEBUG Loader:226 - done processing result set (1 rows)
16:26:09,671 DEBUG BatcherImpl:203 - done closing: 0 open PreparedStatements, 0 open ResultSets
16:26:09,671 DEBUG BatcherImpl:261 - closing statement
16:26:09,671 DEBUG Loader:239 - total objects hydrated: 2
16:26:09,671 DEBUG SessionImpl:2198 - resolving associations for [com.qxlva.qure.datamodel.bo.User#1]
16:26:09,681 DEBUG SessionImpl:3929 - creating collection wrapper:[com.qxlva.qure.datamodel.bo.User.customerDetails#1]
16:26:09,681 DEBUG SessionImpl:3256 - initializing collection [com.qxlva.qure.datamodel.bo.User.customerDetails#1]
16:26:09,681 DEBUG SessionImpl:3257 - checking second-level cache
16:26:09,681 DEBUG SessionImpl:3263 - collection not cached
16:26:09,691 DEBUG BatcherImpl:196 - about to open: 0 open PreparedStatements, 0 open ResultSets
16:26:09,691 DEBUG SQL:237 - select customerde0_.USER_CUSTOMER_DETAILS_ID as USER_CUS1___, customerde0_.USER_ID as USER_ID__, customerde0_.CUSTOMER_ID as CUSTOMER4___, customerde0_.USER_CUSTOMER_DETAILS_ID as USER_CUS1_0_, customerde0_.CUSTOMER_POSITION as CUSTOMER2_0_ from USER_CUSTOMER_DETAILS customerde0_ where customerde0_.USER_ID=?
16:26:09,691 DEBUG BatcherImpl:241 - preparing statement
16:26:09,691 DEBUG IntegerType:46 - binding '1' to parameter: 1
16:26:09,701 DEBUG Loader:327 - result set contains (possibly empty) collection: [com.qxlva.qure.datamodel.bo.User.customerDetails#1]
16:26:09,701 DEBUG SessionImpl:3014 - uninitialized collection: initializing
16:26:09,711 DEBUG Loader:197 - processing result set
16:26:09,711 DEBUG IntegerType:68 - returning '1' as column: USER_CUS1_0_
16:26:09,711 DEBUG Loader:405 - result row: 1
16:26:09,711 DEBUG Loader:536 - Initializing object from ResultSet: 1
16:26:09,711 DEBUG Loader:605 - Hydrating entity: com.qxlva.qure.datamodel.bo.UserCustomerDetails#1
16:26:09,711 DEBUG StringType:64 - returning null as column: CUSTOMER2_0_
16:26:09,711 DEBUG IntegerType:68 - returning '1' as column: USER_ID__
16:26:09,721 DEBUG Loader:292 - found row of collection: [com.qxlva.qure.datamodel.bo.User.customerDetails#1]
16:26:09,721 DEBUG SessionImpl:3037 - reading row
16:26:09,721 DEBUG IntegerType:68 - returning '1' as column: USER_CUS1___
16:26:09,721 DEBUG SessionImpl:1982 - loading [com.qxlva.qure.datamodel.bo.UserCustomerDetails#1]
16:26:09,721 DEBUG SessionImpl:2079 - attempting to resolve [com.qxlva.qure.datamodel.bo.UserCustomerDetails#1]
16:26:09,721 DEBUG SessionImpl:2095 - resolved object in session cache [com.qxlva.qure.datamodel.bo.UserCustomerDetails#1]
16:26:09,731 DEBUG IntegerType:68 - returning '1' as column: CUSTOMER4___
16:26:09,731 DEBUG SessionImpl:1982 - loading [com.qxlva.qure.datamodel.bo.Customer#1]
16:26:09,731 DEBUG SessionImpl:2079 - attempting to resolve [com.qxlva.qure.datamodel.bo.Customer#1]
16:26:09,731 DEBUG SessionImpl:2095 - resolved object in session cache [com.qxlva.qure.datamodel.bo.Customer#1]
16:26:09,731 DEBUG Loader:226 - done processing result set (1 rows)
16:26:09,731 DEBUG BatcherImpl:203 - done closing: 0 open PreparedStatements, 0 open ResultSets
16:26:09,731 DEBUG BatcherImpl:261 - closing statement
16:26:09,741 DEBUG Loader:239 - total objects hydrated: 1
16:26:09,741 DEBUG SessionImpl:2198 - resolving associations for [com.qxlva.qure.datamodel.bo.UserCustomerDetails#1]
16:26:09,741 DEBUG SessionImpl:2222 - done materializing entity [com.qxlva.qure.datamodel.bo.UserCustomerDetails#1]
16:26:09,741 DEBUG SessionImpl:3073 - 1 collections were found in result set
16:26:09,741 DEBUG SessionImpl:3091 - collection fully initialized: [com.qxlva.qure.datamodel.bo.User.customerDetails#1]
16:26:09,741 DEBUG SessionImpl:3094 - 1 collections initialized
16:26:09,741 DEBUG SessionImpl:3265 - collection initialized
16:26:09,751 DEBUG SessionImpl:2222 - done materializing entity [com.qxlva.qure.datamodel.bo.User#1]
16:26:09,751 DEBUG SessionImpl:2198 - resolving associations for [com.qxlva.qure.datamodel.bo.Customer#1]
16:26:09,751 DEBUG SessionImpl:1982 - loading [com.qxlva.qure.datamodel.bo.User#1]
16:26:09,751 DEBUG SessionImpl:2079 - attempting to resolve [com.qxlva.qure.datamodel.bo.User#1]
16:26:09,751 DEBUG SessionImpl:2095 - resolved object in session cache [com.qxlva.qure.datamodel.bo.User#1]
16:26:09,751 DEBUG SessionImpl:2222 - done materializing entity [com.qxlva.qure.datamodel.bo.Customer#1]
16:26:09,751 DEBUG SessionImpl:3112 - initializing non-lazy collections
16:26:09,751 DEBUG JDBCTransaction:59 - commit
16:26:09,751 DEBUG SessionImpl:2242 - flushing session
16:26:09,761 DEBUG Cascades:497 - processing cascades for: com.qxlva.qure.datamodel.bo.User
16:26:09,761 DEBUG Cascades:524 - cascading to collection: com.qxlva.qure.datamodel.bo.User.customerDetails
16:26:09,761 DEBUG Cascades:113 - cascading to saveOrUpdate()
16:26:09,761 DEBUG SessionImpl:1371 - saveOrUpdate() persistent instance
16:26:09,761 DEBUG Cascades:506 - done processing cascades for: com.qxlva.qure.datamodel.bo.User
16:26:09,761 DEBUG SessionImpl:2435 - Flushing entities and processing referenced collections
16:26:09,771 DEBUG SessionImpl:2880 - Collection found: [com.qxlva.qure.datamodel.bo.User.customerDetails#1], was: [com.qxlva.qure.datamodel.bo.User.customerDetails#1]
16:26:09,771 DEBUG SessionImpl:2776 - Processing unreferenced collections
16:26:09,771 DEBUG SessionImpl:2790 - Scheduling collection removes/(re)creates/updates
16:26:09,771 DEBUG SessionImpl:2266 - Flushed: 0 insertions, 0 updates, 0 deletions to 3 objects
16:26:09,771 DEBUG SessionImpl:2271 - Flushed: 0 (re)creations, 0 updates, 0 removals to 1 collections
16:26:09,781 DEBUG Printer:75 - listing entities:
16:26:09,781 DEBUG Printer:82 - com.qxlva.qure.datamodel.bo.UserCustomerDetails{name=null, id=1}
16:26:09,781 DEBUG Printer:82 - com.qxlva.qure.datamodel.bo.Customer{primaryContact=User#1, archived=false, name=Ni SSA, id=1}
16:26:09,781 DEBUG Printer:82 - com.qxlva.qure.datamodel.bo.User{password=password1, userName=simonk, customerDetails=[UserCustomerDetails#1], id=1}
16:26:09,781 DEBUG SessionImpl:2355 - executing flush
16:26:09,781 DEBUG SessionImpl:2820 - post flush
16:26:09,781 DEBUG SessionImpl:585 - transaction completion