My code was working perfectly fine for a long period, but after a few refactors I noticed I suddenly couldn't save a Group
object anymore.
I was getting the dreaded Row was updated or deleted by another transaction (or unsaved-value mapping was incorrect)
error. After Googling of course I found this StackOverflow question, but it didn't help me at all as I wasn't doing anything concurrently.
After going through my refactors, the only difference I found is this change.
Before it was:
final Collection<Sample> allByBarcode = sampleService.byBarcode(groupRequest.getSamples(), currentUser);
if (!allByBarcode.isEmpty()) {
Group group = new Group();
group.setName(groupRequest.getName());
group.setSamples(allByBarcode);
group.setType(groupRequest.getType());
group.setOwner(currentUser);
group = repository.save(group);
return Optional.ofNullable(group);
}
after refactoring (don't remember exactly why) it became:
final Collection<Sample> allByBarcode = sampleService.byBarcode(groupRequest.getSamples(), currentUser);
if (!allByBarcode.isEmpty()) {
Group group = new Group();
group.setName(groupRequest.getName());
group.setSamples(new HashSet<>(allByBarcode));
group.setType(groupRequest.getType());
group.setOwner(currentUser);
group = repository.save(group);
return Optional.ofNullable(group);
}
After changing it back to the original, It suddenly started working again every time, with no errors whatsoever.
Could anyone please explain what's the reason to this error as this is literally the only difference in the code that made it work again?
Update 1:
I tried another variant:
Group group = new Group();
group.setName(groupRequest.getName());
group.setSamples(new ArrayList<>(allByBarcode));
group.setType(groupRequest.getType());
group.setOwner(currentUser);
group = repository.save(group);
Note ArrayList
instead of HashSet
– for some reason this code works.
I've also tried LinkedList
which also works.
Any ideas?
Update 2:
The stack trace is as follows: I have cut it to remove lots of Spring and Tomcat-related trace.
Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is org.springframework.orm.ObjectOptimisticLockingFailureException: Object of class [uk.ac.sanger.mig.aker.domain.Group] with identifier [93]: optimistic locking failed; nested exception is org.hibernate.StaleObjectStateException: Row was updated or deleted by another transaction (or unsaved-value mapping was incorrect) : [uk.ac.sanger.mig.aker.domain.Group#93]] with root cause
org.hibernate.StaleObjectStateException: Row was updated or deleted by another transaction (or unsaved-value mapping was incorrect) : [uk.ac.sanger.mig.aker.domain.Group#93]
at org.hibernate.persister.entity.AbstractEntityPersister.check(AbstractEntityPersister.java:2541)
at org.hibernate.persister.entity.AbstractEntityPersister.update(AbstractEntityPersister.java:3285)
at org.hibernate.persister.entity.AbstractEntityPersister.updateOrInsert(AbstractEntityPersister.java:3183)
at org.hibernate.persister.entity.AbstractEntityPersister.update(AbstractEntityPersister.java:3525)
at org.hibernate.action.internal.EntityUpdateAction.execute(EntityUpdateAction.java:159)
at org.hibernate.engine.spi.ActionQueue.executeActions(ActionQueue.java:465)
at org.hibernate.engine.spi.ActionQueue.executeActions(ActionQueue.java:351)
at org.hibernate.event.internal.AbstractFlushingEventListener.performExecutions(AbstractFlushingEventListener.java:350)
at org.hibernate.event.internal.DefaultFlushEventListener.onFlush(DefaultFlushEventListener.java:56)
at org.hibernate.internal.SessionImpl.flush(SessionImpl.java:1222)
at org.hibernate.internal.SessionImpl.managedFlush(SessionImpl.java:425)
at org.hibernate.engine.transaction.internal.jdbc.JdbcTransaction.beforeTransactionCommit(JdbcTransaction.java:101)
at org.hibernate.engine.transaction.spi.AbstractTransactionImpl.commit(AbstractTransactionImpl.java:177)
at org.hibernate.jpa.internal.TransactionImpl.commit(TransactionImpl.java:77)
at org.springframework.orm.jpa.JpaTransactionManager.doCommit(JpaTransactionManager.java:517)
at org.springframework.transaction.support.AbstractPlatformTransactionManager.processCommit(AbstractPlatformTransactionManager.java:757)
at org.springframework.transaction.support.AbstractPlatformTransactionManager.commit(AbstractPlatformTransactionManager.java:726)
at org.springframework.transaction.interceptor.TransactionAspectSupport.commitTransactionAfterReturning(TransactionAspectSupport.java:521)
at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:291)
at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:96)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:207)
at com.sun.proxy.$Proxy116.createGroup(Unknown Source)
at uk.ac.sanger.mig.aker.controllers.GroupController.store(GroupController.java:107)
In your new code, you are creating a new HashSet
that will contain all the Sample
elements in allByBarcode
collection.
And I think the problem is raised here because those elements are not persisted by the session because they are new objects added to the new HashSet()
instance and they're not really saved by the session, it explains the Row was updated or deleted by another transaction (or unsaved-value mapping was incorrect)
Exception
however the allByBarcode
elements are taken from your service so they are persisted and recognized by the session.
And I don't see what's the need of setting a new HashSet
instance where the elements should be saved(persisted) by the session (which will throw an Exception
because you are trying to save elements with existing identifier) while it's more efficient and more safe to set the already saved elements, so better use:
group.setSamples(allByBarcode);
I think the problem is that you have some duplicates objects in your collection allByBarcode that disappear when you wrap your collection in a HashSet which doesn't allow duplicates. What happens after that is : your repository is trying to save the same object more than once.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With