Troubleshooting Hibernate
NonUniqueObjectException
What to do if you see an exception like the following
org.hibernate.NonUniqueObjectException: a different object with the same identifier value was already associated with the session: [org.openmrs.Role#System Developer]
at org.hibernate.engine.StatefulPersistenceContext.checkUniqueness(StatefulPersistenceContext.java:590)
at org.hibernate.event.def.DefaultSaveOrUpdateEventListener.performUpdate(DefaultSaveOrUpdateEventListener.java:284)
at org.hibernate.event.def.DefaultSaveOrUpdateEventListener.entityIsDetached(DefaultSaveOrUpdateEventListener.java:223)
at org.hibernate.event.def.DefaultSaveOrUpdateEventListener.performSaveOrUpdate(DefaultSaveOrUpdateEventListener.java:89)
at org.hibernate.event.def.DefaultSaveOrUpdateEventListener.onSaveOrUpdate(DefaultSaveOrUpdateEventListener.java:70)
at org.hibernate.impl.SessionImpl.fireSaveOrUpdate(SessionImpl.java:507)
at org.hibernate.impl.SessionImpl.saveOrUpdate(SessionImpl.java:499)
at org.hibernate.engine.CascadingAction$5.cascade(CascadingAction.java:218)
at org.hibernate.engine.Cascade.cascadeToOne(Cascade.java:268)
...
This usually means that you have loaded an object into your hibernate session that is a duplicate of an object you also have around as a detached instance, and you then try to reattach the attached instance.
Here is an example of code that Does Not Work
// Opening a form, i.e. controller.formBackingObject()
User user = Context.getUserService().getUser(12);
// Next when the form is submitted, i.e. controller.onSubmit()
// at this point user is detached
for (String roleUserShouldHave : request.getParameterValues("role")) {
Role role = Context.getUserService().getRole(roleUserShouldHave); // This is the problem
user.addRole(role);
}
Context.getUserService().updateUser(user); // This throws org.hibernate.NonUniqueObjectException
The issue here is that in the problem line we are fetching an object from the database that we already have a detached reference to via the user object. This exception would not be thrown if updateUser was doing a session.merge(user), since that just copies the state of the user object into the database. But doing session.saveOrUpdate(user) tries to reattach the detached user object, which fails if one of its dependant detachd objects is already in the session as an attached object.
Here is one solution, meant to be illustrative rather than elegant:
for (String roleUserShouldHave : request.getParameterValues("role")) {
Role role = null;
for (Role test : user.getRoles())
if (test.equals(roleUserShouldHave)
role = test; // it is safe to use the detached object
if (role == null)
Role role = Context.getUserService().getRole(roleUserShouldHave); // only do this if there is no detached object
user.addRole(role);
}
Could not synchronize database state with session, and other batch commit issues
When working with transactions that contain several changes, it is sometimes difficult to determine which sql stmt in the batch is the cause of problem. In these situations hibernate is often less then helpful: setting hibernate.show_sql=true in the runtime properties may help. In these situations, the helpful trick is to set the hibernate batch size to 0 in runtime properties via: hibernate.jdbc.batch_size=0
For example, without the setting, the error may look something like this:
ERROR - JDBCExceptionReporter.logExceptions(78) |2008-10-10 09:41:37,765| Field 'guid' doesn't have a default value
ERROR - AbstractFlushingEventListener.performExecutions(301) |2008-10-10 09:41:37,765| Could not synchronize database st
ate with session
at org.hibernate.exception.SQLStateConverter.handledNonSpecificException(SQLStateConverter.java:103)
at org.hibernate.exception.SQLStateConverter.convert(SQLStateConverter.java:91)
at org.hibernate.exception.JDBCExceptionHelper.convert(JDBCExceptionHelper.java:43)
...
With hibernate.jdbc.batch_size=0, the error message recorded is:
ERROR - JDBCExceptionReporter.logExceptions(78) |2008-10-10 09:41:37,765| Field 'guid' doesn't have a default value
ERROR - AbstractFlushingEventListener.performExecutions(301) |2008-10-10 09:41:37,765| Could not synchronize database st
ate with session
<b>org.hibernate.exception.GenericJDBCException: could not insert collection rows: [org.openmrs.User.roles#2408]</b>
at org.hibernate.exception.SQLStateConverter.handledNonSpecificException(SQLStateConverter.java:103)
at org.hibernate.exception.SQLStateConverter.convert(SQLStateConverter.java:91)
at org.hibernate.exception.JDBCExceptionHelper.convert(JDBCExceptionHelper.java:43)
...
