I have turned on trace level debugging, and have some additional information. Nordborg, I think you are correct in your assumptions regarding the order of cascades.
Quote:
117828 [AWT-EventQueue-0] DEBUG org.hibernate.transaction.JDBCTransaction - begin
117828 [AWT-EventQueue-0] DEBUG org.hibernate.jdbc.ConnectionManager - opening JDBC connection
117828 [AWT-EventQueue-0] TRACE org.hibernate.connection.DriverManagerConnectionProvider - total checked-out connections: 0
117828 [AWT-EventQueue-0] TRACE org.hibernate.connection.DriverManagerConnectionProvider - using pooled JDBC connection, pool size: 0
117828 [AWT-EventQueue-0] DEBUG org.hibernate.transaction.JDBCTransaction - current autocommit status: true
117828 [AWT-EventQueue-0] DEBUG org.hibernate.transaction.JDBCTransaction - disabling autocommit
117844 [AWT-EventQueue-0] TRACE org.hibernate.jdbc.JDBCContext - after transaction begin
117844 [AWT-EventQueue-0] TRACE org.hibernate.event.def.DefaultSaveOrUpdateEventListener - ignoring persistent instance
117844 [AWT-EventQueue-0] TRACE org.hibernate.event.def.DefaultSaveOrUpdateEventListener - object already associated with session: [net.seamist.organization.Organization#284]
117844 [AWT-EventQueue-0] TRACE org.hibernate.event.def.AbstractFlushingEventListener - flushing session
117844 [AWT-EventQueue-0] DEBUG org.hibernate.event.def.AbstractFlushingEventListener - processing flush-time cascades
117844 [AWT-EventQueue-0] TRACE org.hibernate.engine.Cascade - processing cascade ACTION_SAVE_UPDATE for: net.seamist.organization.Contact
117844 [AWT-EventQueue-0] TRACE org.hibernate.engine.Cascade - done processing cascade ACTION_SAVE_UPDATE for: net.seamist.organization.Contact
117844 [AWT-EventQueue-0] TRACE org.hibernate.engine.Cascade - processing cascade ACTION_SAVE_UPDATE for: net.seamist.organization.Organization
117844 [AWT-EventQueue-0] TRACE org.hibernate.engine.Cascade - cascade ACTION_SAVE_UPDATE for collection: net.seamist.organization.Organization.contacts
117844 [AWT-EventQueue-0] TRACE org.hibernate.engine.CascadingAction - cascading to saveOrUpdate: net.seamist.organization.Contact
117844 [AWT-EventQueue-0] TRACE org.hibernate.event.def.AbstractSaveEventListener - persistent instance of: net.seamist.organization.Contact
117844 [AWT-EventQueue-0] TRACE org.hibernate.event.def.DefaultSaveOrUpdateEventListener - ignoring persistent instance
117844 [AWT-EventQueue-0] TRACE org.hibernate.event.def.DefaultSaveOrUpdateEventListener - object already associated with session: [net.seamist.organization.Contact#1]
117844 [AWT-EventQueue-0] TRACE org.hibernate.engine.CascadingAction - cascading to saveOrUpdate: net.seamist.organization.Contact
117844 [AWT-EventQueue-0] TRACE org.hibernate.event.def.AbstractSaveEventListener - transient instance of: net.seamist.organization.Contact
117844 [AWT-EventQueue-0] TRACE org.hibernate.event.def.DefaultSaveOrUpdateEventListener - saving transient instance
117844 [AWT-EventQueue-0] TRACE org.hibernate.event.def.AbstractSaveEventListener - saving [net.seamist.organization.Contact#<null>]
117844 [AWT-EventQueue-0] TRACE org.hibernate.event.def.AbstractSaveEventListener - executing insertions
117844 [AWT-EventQueue-0] TRACE org.hibernate.engine.Cascade - processing cascade ACTION_SAVE_UPDATE for: net.seamist.organization.Contact
117844 [AWT-EventQueue-0] TRACE org.hibernate.engine.Cascade - done processing cascade ACTION_SAVE_UPDATE for: net.seamist.organization.Contact
117844 [AWT-EventQueue-0] TRACE org.hibernate.engine.VersionValue - version unsaved-value strategy UNDEFINED
117844 [AWT-EventQueue-0] TRACE org.hibernate.engine.IdentifierValue - id unsaved-value: null
117860 [AWT-EventQueue-0] ERROR net.seamist.ui.OrganizationFrameController - Organization save failed
org.hibernate.PropertyValueException: not-null property references a null or transient value: net.seamist.organization.Contact.address
at org.hibernate.engine.Nullability.checkNullability(Nullability.java:95)
at org.hibernate.event.def.AbstractSaveEventListener.performSaveOrReplicate(AbstractSaveEventListener.java:313)
at org.hibernate.event.def.AbstractSaveEventListener.performSave(AbstractSaveEventListener.java:204)
at org.hibernate.event.def.AbstractSaveEventListener.saveWithGeneratedId(AbstractSaveEventListener.java:130)
at org.hibernate.event.def.DefaultSaveOrUpdateEventListener.saveWithGeneratedOrRequestedId(DefaultSaveOrUpdateEventListener.java:210)
at org.hibernate.event.def.DefaultSaveOrUpdateEventListener.entityIsTransient(DefaultSaveOrUpdateEventListener.java:195)
at org.hibernate.event.def.DefaultSaveOrUpdateEventListener.performSaveOrUpdate(DefaultSaveOrUpdateEventListener.java:117)
at org.hibernate.event.def.DefaultSaveOrUpdateEventListener.onSaveOrUpdate(DefaultSaveOrUpdateEventListener.java:93)
at org.hibernate.impl.SessionImpl.fireSaveOrUpdate(SessionImpl.java:534)
at org.hibernate.impl.SessionImpl.saveOrUpdate(SessionImpl.java:526)
at org.hibernate.engine.CascadingAction$5.cascade(CascadingAction.java:241)
at org.hibernate.engine.Cascade.cascadeToOne(Cascade.java:291)
at org.hibernate.engine.Cascade.cascadeAssociation(Cascade.java:239)
at org.hibernate.engine.Cascade.cascadeProperty(Cascade.java:192)
at org.hibernate.engine.Cascade.cascadeCollectionElements(Cascade.java:319)
at org.hibernate.engine.Cascade.cascadeCollection(Cascade.java:265)
at org.hibernate.engine.Cascade.cascadeAssociation(Cascade.java:242)
at org.hibernate.engine.Cascade.cascadeProperty(Cascade.java:192)
at org.hibernate.engine.Cascade.cascade(Cascade.java:153)
at org.hibernate.event.def.AbstractFlushingEventListener.cascadeOnFlush(AbstractFlushingEventListener.java:154)
at org.hibernate.event.def.AbstractFlushingEventListener.prepareEntityFlushes(AbstractFlushingEventListener.java:145)
at org.hibernate.event.def.AbstractFlushingEventListener.flushEverythingToExecutions(AbstractFlushingEventListener.java:88)
at org.hibernate.event.def.DefaultFlushEventListener.onFlush(DefaultFlushEventListener.java:49)
at org.hibernate.impl.SessionImpl.flush(SessionImpl.java:1027)
at net.seamist.ui.OrganizationFrameController.saveOrganization(OrganizationFrameController.java:228)
at net.seamist.ui.OrganizationFrame.saveOrganization(OrganizationFrame.java:1044)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:597)
at org.jdesktop.application.ApplicationAction.noProxyActionPerformed(ApplicationAction.java:662)
at org.jdesktop.application.ApplicationAction.actionPerformed(ApplicationAction.java:698)
at javax.swing.AbstractButton.fireActionPerformed(AbstractButton.java:1995)
at javax.swing.AbstractButton$Handler.actionPerformed(AbstractButton.java:2318)
at javax.swing.DefaultButtonModel.fireActionPerformed(DefaultButtonModel.java:387)
at javax.swing.DefaultButtonModel.setPressed(DefaultButtonModel.java:242)
at javax.swing.plaf.basic.BasicButtonListener.mouseReleased(BasicButtonListener.java:236)
at java.awt.AWTEventMulticaster.mouseReleased(AWTEventMulticaster.java:272)
at java.awt.Component.processMouseEvent(Component.java:6134)
at javax.swing.JComponent.processMouseEvent(JComponent.java:3265)
at java.awt.Component.processEvent(Component.java:5899)
at java.awt.Container.processEvent(Container.java:2023)
at java.awt.Component.dispatchEventImpl(Component.java:4501)
at java.awt.Container.dispatchEventImpl(Container.java:2081)
at java.awt.Component.dispatchEvent(Component.java:4331)
at java.awt.LightweightDispatcher.retargetMouseEvent(Container.java:4301)
at java.awt.LightweightDispatcher.processMouseEvent(Container.java:3965)
at java.awt.LightweightDispatcher.dispatchEvent(Container.java:3895)
at java.awt.Container.dispatchEventImpl(Container.java:2067)
at java.awt.Window.dispatchEventImpl(Window.java:2458)
at java.awt.Component.dispatchEvent(Component.java:4331)
at java.awt.EventQueue.dispatchEvent(EventQueue.java:599)
at java.awt.EventDispatchThread.pumpOneEventForFilters(EventDispatchThread.java:269)
at java.awt.EventDispatchThread.pumpEventsForFilter(EventDispatchThread.java:184)
at java.awt.EventDispatchThread.pumpEventsForHierarchy(EventDispatchThread.java:174)
at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:169)
at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:161)
at java.awt.EventDispatchThread.run(EventDispatchThread.java:122)
I have already used the Netbeans debugger to verify that the contacts do in fact have addresses. Before session.save() is called with the organization, organization.locations contains the new address. organization.contacts contains the new contact. And contact.address contains the new address as well.
Here is the code that populates the contact's fields. At this point there is already a transient address that was added to the EntityComboBoxModel.
Code:
contact.getName().setSalutation((String) view.getJComboBox1().getSelectedItem());
contact.getName().setFirst(view.getJTextField1().getText());
contact.getName().setAlias(view.getJTextField2().getText());
contact.getName().setLast(view.getJTextField3().getText());
contact.setTitle(view.getJTextField10().getText());
contact.setRemarks(view.getJTextArea1().getText());
contact.setAttendingNextEvent(view.getJCheckBox3().isSelected());
EntityComboBoxModel<OrganizationAddress> aModel
= (EntityComboBoxModel<OrganizationAddress>) view.getJComboBox2().getModel();
contact.setAddress(aModel.getSelectedEntityInstance());
contact.setEmail(view.getJTextField9().getText());
contact.setEwg(view.getJCheckBox1().isSelected());
contact.setJpag(view.getJCheckBox2().isSelected());
contact.setWorkPhone((String) view.getJFormattedTextField1().getValue());
contact.setWorkPhoneDSN((String) view.getJFormattedTextField2().getValue());
contact.setFax((String) view.getJFormattedTextField3().getValue());
contact.setFaxDSN((String) view.getJFormattedTextField4().getValue());
contact.setMobilePhone((String) view.getJFormattedTextField5().getValue());
That transient contact is then passed to an already persistent organization. By this time, the address has already been added to organization.locations. Here is the code that is called when a address is added and when a contact is added.
Code:
public class OrganizationAddressAddedListener
implements DefaultEventListener<OrganizationAddress> {
@Override
public void handleEvent( DefaultEvent<OrganizationAddress> event ) {
organization.addLocation(event.getPayload());
view.getJTable3().setModel(prepareLocationTableModel());
}
}
public class ContactAddedEventListener implements DefaultEventListener<Contact> {
@Override
public void handleEvent( DefaultEvent<Contact> event ) {
organization.addContact(event.getPayload());
view.getJTable2().setModel(prepareContactTableModel());
}
}
Finally, when a user saves the organization the following code is called. This is the only code that actually contains calls to session methods.
Code:
EntityComboBoxModel<Citizenship> citiModel
= (EntityComboBoxModel<Citizenship>) view.getJComboBox2().getModel();
organization.setCitizenship(citiModel.getSelectedEntityInstance());
organization.setExtendedName(view.getJTextField2().getText());
organization.setName(view.getJTextField1().getText());
organization.setRedFlag(view.getJCheckBox2().isSelected());
organization.setRemarks(view.getJTextArea1().getText());
EntityComboBoxModel<Source> sModel
= (EntityComboBoxModel<Source>) view.getJComboBox3().getModel();
organization.setSource(sModel.getSelectedEntityInstance());
EntityComboBoxModel<OrganizationType> otModel
= (EntityComboBoxModel<OrganizationType>) view.getJComboBox1().getModel();
organization.setType(otModel.getSelectedEntityInstance());
organization.setUrl(view.getJTextField3().getText());
organization.setVisaApplicant(view.getJRadioButton1().isSelected());
organization.setVisaDenied(view.getJRadioButton4().isSelected());
organization.setVisaEnrollee(view.getJRadioButton2().isSelected());
organization.setVisaLinerOperator(view.getJCheckBox1().isSelected());
organization.setVisaParticipant(view.getJRadioButton3().isSelected());
organization.setVisaTerminated(view.getJRadioButton5().isSelected());
organization.setVisaUnknown(view.getJRadioButton7().isSelected());
organization.setVisaWithdrewApplication(view.getJRadioButton6().isSelected());
Commitment c1 = new Commitment();
c1.setActualMetricTons((Integer) view.getJFormattedTextField3().getValue());
c1.setActualSquareFeet((Integer) view.getJFormattedTextField2().getValue());
c1.setActualTeus((Integer) view.getJFormattedTextField1().getValue());
c1.setMetricTons((Integer) view.getJFormattedTextField6().getValue());
c1.setSquareFeet((Integer) view.getJFormattedTextField5().getValue());
c1.setTeus((Integer) view.getJFormattedTextField4().getValue());
c1.setRemarks(view.getJTextArea2().getText());
organization.setStageOneCommitment(c1);
c1 = new Commitment();
c1.setActualMetricTons((Integer) view.getJFormattedTextField9().getValue());
c1.setActualSquareFeet((Integer) view.getJFormattedTextField8().getValue());
c1.setActualTeus((Integer) view.getJFormattedTextField7().getValue());
c1.setMetricTons((Integer) view.getJFormattedTextField12().getValue());
c1.setSquareFeet((Integer) view.getJFormattedTextField11().getValue());
c1.setTeus((Integer) view.getJFormattedTextField10().getValue());
c1.setRemarks(view.getJTextArea3().getText());
organization.setStageTwoCommitment(c1);
c1 = new Commitment();
c1.setActualMetricTons((Integer) view.getJFormattedTextField15().getValue());
c1.setActualSquareFeet((Integer) view.getJFormattedTextField14().getValue());
c1.setActualTeus((Integer) view.getJFormattedTextField13().getValue());
c1.setMetricTons((Integer) view.getJFormattedTextField18().getValue());
c1.setSquareFeet((Integer) view.getJFormattedTextField17().getValue());
c1.setTeus((Integer) view.getJFormattedTextField16().getValue());
c1.setRemarks(view.getJTextArea4().getText());
organization.setStageThreeCommitment(c1);
try {
session.beginTransaction();
session.save(organization);
session.flush();
session.getTransaction().commit();
log.debug("Organization saved successfully");
/* Fire off the proper event on a successful save. */
if (newOrganization) {
fireEventGlobal(new OrganizationAddedEvent(organization));
} else {
fireEventGlobal(new OrganizationChangedEvent(organization));
}
} catch (RuntimeException e) {
try {
log.error("Organization save failed", e);
session.getTransaction().rollback();
} catch (RuntimeException ex) {
log.error("Transaction rollback failed", ex);
}
} finally {
session.close();
}