I have written an object called Patient and a simple PatienDAO object to handle creation and updating and querying the Patient objects. Recently setup a GUI so that the objects can be edited from multiple locations in the application, and encountered StaleObjectStateException, and wrote the following tests to try to understand this problem better.
First test runs successfully. The object patient was instantiated in the setup, so the DAO adds the patient to the database via createPatient, a query is then done, and then I alter the first name to Sammy, update the object, alter the last name to Revised, and update the object:
Code:
public void testMultipleDAOUpdates2(){
assertTrue(patientDAO.createPatient(patient));
List<Patient> patients = patientDAO.getAllPatients();
patient.setFirstName("Sammy");
assertTrue(patientDAO.updatePatientValues(patient));
patient.setLastName("Revised");
assertTrue(patientDAO.updatePatientValues(patient));
}
The following code also runs fine, adding a for loop and updating patient:
Code:
public void testPatientUpdates4(){
patientDAO.createPatient(patient);
List<Patient> patients = patientDAO.getAllPatients();
for (Patient p : patients) {
p.setGender("Female");
p.setWeight(25.95);
assertTrue(patientDAO.updatePatientValues(p));
}
patients = patientDAO.getAllPatients();
}
However, the following code yields a StaleObjectStateException at the indicated line. The only difference between this test and the previous one is that I have combined the updates from the two tests:
Code:
public void testPatientUpdates3(){
patientDAO.createPatient(patient);
List<Patient> patients = patientDAO.getAllPatients();
for (Patient p : patients) {
p.setGender("Female");
p.setWeight(25.95);
assertTrue(patientDAO.updatePatientValues(p));
}
patient.setFirstName("Sammy");
patientDAO.updatePatientValues(patient); // EXCEPTION HERE!
patient.setLastName("Revised");
patientDAO.updatePatientValues(patient);
}
The failure trace is:
Code:
org.hibernate.StaleObjectStateException: Row was updated or deleted by another transaction (or unsaved-value mapping was incorrect): [edu.utah.cdmcc.entities.Patient#1]
at org.hibernate.persister.entity.AbstractEntityPersister.check(AbstractEntityPersister.java:1680)
at org.hibernate.persister.entity.AbstractEntityPersister.update(AbstractEntityPersister.java:2271)
at org.hibernate.persister.entity.AbstractEntityPersister.updateOrInsert(AbstractEntityPersister.java:2180)
at org.hibernate.persister.entity.AbstractEntityPersister.update(AbstractEntityPersister.java:2456)
at org.hibernate.action.EntityUpdateAction.execute(EntityUpdateAction.java:91)
at org.hibernate.engine.ActionQueue.execute(ActionQueue.java:248)
at org.hibernate.engine.ActionQueue.executeActions(ActionQueue.java:232)
at org.hibernate.engine.ActionQueue.executeActions(ActionQueue.java:140)
at org.hibernate.event.def.AbstractFlushingEventListener.performExecutions(AbstractFlushingEventListener.java:297)
at org.hibernate.event.def.DefaultFlushEventListener.onFlush(DefaultFlushEventListener.java:27)
at org.hibernate.impl.SessionImpl.flush(SessionImpl.java:988)
at org.hibernate.impl.SessionImpl.managedFlush(SessionImpl.java:337)
at org.hibernate.transaction.JDBCTransaction.commit(JDBCTransaction.java:106)
at edu.utah.cdmcc.entities.PatientDAO.updatePatientValues(PatientDAO.java:198)
at database.tests.TestPatientAdditionValidationDeletion.testPatientUpdates3(TestPatientAdditionValidationDeletion.java:264)
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:585)
at junit.framework.TestCase.runTest(TestCase.java:154)
at junit.framework.TestCase.runBare(TestCase.java:127)
at junit.framework.TestResult$1.protect(TestResult.java:106)
at junit.framework.TestResult.runProtected(TestResult.java:124)
at junit.framework.TestResult.run(TestResult.java:109)
at junit.framework.TestCase.run(TestCase.java:118)
at junit.framework.TestSuite.runTest(TestSuite.java:208)
at junit.framework.TestSuite.run(TestSuite.java:203)
at junit.framework.TestSuite.runTest(TestSuite.java:208)
at junit.framework.TestSuite.run(TestSuite.java:203)
at junit.extensions.TestDecorator.basicRun(TestDecorator.java:22)
at junit.extensions.TestSetup$1.protect(TestSetup.java:19)
at junit.framework.TestResult.runProtected(TestResult.java:124)
at junit.extensions.TestSetup.run(TestSetup.java:23)
at org.eclipse.jdt.internal.junit.runner.junit3.JUnit3TestReference.run(JUnit3TestReference.java:128)
at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:460)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:673)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:386)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:196)
The "supporting code" for these tests is in my DAO, consisting of createPatient, updatePatientValues, and getAllPatients; these are shown below for readers' reference:
Code:
public boolean createPatient(Patient newPatient) {
if(trialDBNumberAlreadyExists(newPatient.getTrialDbCode())) return false;
personValidator = new ClassValidator<Patient>(Patient.class);
validationMessages = personValidator.getInvalidValues(newPatient);
if (validationMessages.length == 0) {
session = HibernateUtil.getSessionFactory().getCurrentSession();
session.beginTransaction();
session.save(newPatient);
session.getTransaction().commit();
return true;
} else
return false;
}
public boolean updatePatientValues(Patient patient) {
personValidator = new ClassValidator<Patient>(Patient.class);
validationMessages = personValidator.getInvalidValues(patient);
if (validationMessages.length == 0) {
session = HibernateUtil.getSessionFactory().getCurrentSession();
session.beginTransaction();
session.saveOrUpdate(patient);
session.getTransaction().commit();
return true;
} else
return false;
}
@SuppressWarnings("unchecked")
public List<Patient> getAllPatients(){
session = HibernateUtil.getSessionFactory().getCurrentSession();
session.beginTransaction();
List<Patient> results = session.getNamedQuery(Patient.GETALLPATIENTS).list();
session.getTransaction().commit();
return results;
}
Using 3.1.0beta5 tools with Eclipse 3.2 final.
I have been looking at this code too long to see what is probably obvious! I appreciate any help in understanding this problem. Thanks.
- Mike Dean