I've followed my code through the debugger and tracked down some areas that I'd like feedback on from a Hibernate implementation viewpoint. First of all, let me give specifics on my code and configuration.
I have a one-to-many association with Contact-to-Addresses.
ContactBO.hbm.xml association configuration:
Code:
<bag
name="addresses"
table="Address"
lazy="true"
inverse="false"
cascade="all"
>
<key
column="contactId"
/>
<one-to-many
class="commerce.model.contact.AddressBO"
/>
</bag>
Address.hbm.xml's association back to ContactBO:
Code:
<many-to-one
name="contact"
class="commerce.model.contact.ContactBO"
cascade="none"
outer-join="auto"
update="true"
insert="true"
column="contactId"
not-null="true"
/>
Here is my code in a nutshell:
Code:
/**
* This will do 2 inserts and then immediately call 2 updates
* in the same transaction.
*
* Note: Junit setup() initializes Configuration, SessionFactory, etc.
* and teardown() closes everything up.
*
* Hibernate seems to do the following to cause the unwanted updates:
* 1. Replaces ArrayList with Bag to cause ContactBO to update
* 2. Adds 'contact-addresses' association to the collectionCreation
* list, updating the BillingAddressBO as well.
*
* @throws Exception
*/
public void testContactInsert() throws Exception {
Transaction txn = null;
try {
ContactBO contact = new ContactBO();
// since emails have to be unique, generate
// one per test based on the time
long time = System.currentTimeMillis();
contact.setEmail(time+"@test.com");
contact.setFirstName("JUFirst");
contact.setLastName("JULast");
BillingAddressBO billingAddress = new BillingAddressBO();
billingAddress.setCity("JUSomewhereCity");
billingAddress.setState("CO");
billingAddress.setStreet1("Street1");
billingAddress.setStreet2("Street2");
billingAddress.setZip("80211");
//associate Contact and BillingAddress
contact.setAddresses(new ArrayList(1));
List addresses = contact.getAddresses();
addresses.add(billingAddress);
billingAddress.setContact(contact);
txn = session.beginTransaction();
Long cntctId = (Long)session.save(contact);
txn.commit();
} catch (MappingException me) {
throw me;
} catch (HibernateException he) {
throw he;
} finally {
// if txn not committed, rollback here
checkForRollback(txn);
}
}
session.save(contact) adds two ScheduledInsertions.
txn.commit() adds a Scheduled Update and a collectionCreation entry.
So, it ends up with 2 inserts and 2 updates. Here is the log:
2004-01-13 16:13:30,099 INFO hibernate.impl.SessionFactoryObjectFactory -> no JNDI name configured
Hibernate: insert into Contact (version, email, firstName, lastName, id) values (?, ?, ?, ?, ?)
2004-01-13 17:08:53,447 DEBUG hibernate.type.LongType -> binding '0' to parameter: 1
2004-01-13 17:08:53,447 DEBUG hibernate.type.StringType -> binding
'1074035610189@test.com' to parameter: 2
2004-01-13 17:08:53,447 DEBUG hibernate.type.StringType -> binding 'JUFirst' to parameter: 3
2004-01-13 17:08:53,447 DEBUG hibernate.type.StringType -> binding 'JULast' to parameter: 4
2004-01-13 17:08:53,447 DEBUG hibernate.type.LongType -> binding '25' to parameter: 5
Hibernate: insert into Address (version, city, state, street1, street2, zip,
contactId, typeCode, id) values (?, ?, ?, ?, ?, ?, ?, 1, ?)
2004-01-13 17:08:57,533 DEBUG hibernate.type.LongType -> binding '0' to parameter: 1
2004-01-13 17:08:57,533 DEBUG hibernate.type.StringType -> binding 'JUSomewhereCity' to parameter: 2
2004-01-13 17:08:57,533 DEBUG hibernate.type.StringType -> binding 'CO' to parameter: 3
2004-01-13 17:08:57,533 DEBUG hibernate.type.StringType -> binding 'Street1' to parameter: 4
2004-01-13 17:08:57,533 DEBUG hibernate.type.StringType -> binding 'Street2' to parameter: 5
2004-01-13 17:08:57,533 DEBUG hibernate.type.StringType -> binding '80211' to parameter: 6
2004-01-13 17:08:57,533 DEBUG hibernate.type.LongType ->
binding '25' to parameter: 72004-01-13 17:08:57,543 DEBUG hibernate.type.LongType -> binding '23' to parameter: 8
Hibernate: update Contact set version=? where id=? and version=?
2004-01-13 17:10:02,086 DEBUG hibernate.type.LongType -> binding '1' to parameter: 1
2004-01-13 17:10:02,096 DEBUG hibernate.type.LongType -> binding '25' to parameter: 2
2004-01-13 17:10:02,096 DEBUG hibernate.type.LongType -> binding '0' to parameter: 3
Hibernate: update Address set contactId=? where id=?
2004-01-13 17:12:05,664 DEBUG hibernate.type.LongType -> binding '25' to parameter: 1
2004-01-13 17:12:05,664 DEBUG hibernate.type.LongType -> binding '23' to parameter: 2
Questions/Observations from my debugging session:
1. Have I configured something incorrectly in my hbm.xml's? If so, easy fix! What should I be doing?
2. When the entities are scheduled for insertion, their status is set from SAVING to LOADED. Seems to me like they should be in the process of LOADING until the SQL is commited. Only after txn.commit() is successful, would they become LOADED. Is there a state transition diagram for entities that I could look at for a better understanding?
3. The logs indicate the association between ContactBO and Address is done in the insert statement (e.g., Address FK contactId = 25). So, the extra update statement inside the same txn is unecessary. I think Hibernate has the knowledge to know that duplicate insert and update statements for the association are happening the same txn (therefore they are unecessary). It just needs to apply the knowledge somewhere. This is where I'm at a loss...
pseudo-code during txn.commit() somewhere down the line...
Code:
if (session.isCurrentTransaction=true && associationAlreadyEstablishedOnInsert) {
// skip additional update of collection
someCollection.docreate = false;
}
Or, maybe something looking at the entitys' statuses could be used?
4. txn.commit() eventually calls the ContactBO