Home > Enterprise >  HibernateOptimisticLockingFailureException when saving an entity in Spring MVC with JPA
HibernateOptimisticLockingFailureException when saving an entity in Spring MVC with JPA

Time:06-18

In my Java project (Spring MVC) I have the following entities:

public class TariffeBaseComposizioni implements Serializable {

 private static final long serialVersionUID = 1L;

 @Id
 @Basic(optional = false)
 @NotNull
 @Column(name = "ID")
 private Long id;
 ...
 @JoinColumn(name = "TIPO_COMPOSIZIONE", referencedColumnName = "SIGLA")
 @ManyToOne
 private TipiComposizioni tipoComposizione;
 ...
}

public class TipiComposizioni implements Serializable {

private static final long serialVersionUID = 1L;

 @Id
 @Basic(optional = false)
 @NotNull
 @Column(name = "ID")
 private Long id;

 @Size(max = 20)
 @Column(name = "SIGLA")     
 private String sigla;

 @Size(max = 255)
 @Column(name = "NOME")
 private String nome;
 ...
}

I'm trying to save my TariffeBaseComposizioni instance with the save():

getCurrentSession().save(t);

where 't' is of type 'TariffeBaseComposizioni'.

When save is called, the HibernateOptimisticLockingFailureException is raised.

Here the stacktrace of the error, where the queries executed are visible:

Hibernate: insert into TARIFFE_BASE_COMPOSIZIONI (ID_FASCIA, PREZZO, PUNTI, TIPO_COMPOSIZIONE, ID) values (?, ?, ?, ?, ?)
Hibernate: update TARIFFE_BASE_COMPOSIZIONI set ID_FASCIA=?, PREZZO=?, PUNTI=?, TIPO_COMPOSIZIONE=? where ID=?
2022-06-10 12:55:02 INFO  AbstractBatchImpl:195 - HHH000010: On release of batch it still contained JDBC statements
2022-06-10 12:55:02 ERROR GlobalExceptionHandler:15 - Batch update returned unexpected row count from update [0]; actual row count: 0; expected: 1; nested exception is org.hibernate.StaleStateException: Batch update returned unexpected row count from update [0]; actual row count: 0; expected: 1
org.springframework.orm.hibernate4.HibernateOptimisticLockingFailureException: Batch update returned unexpected row count from update [0]; actual row count: 0; expected: 1; nested exception is org.hibernate.StaleStateException: Batch update returned unexpected row count from update [0]; actual row count: 0; expected: 1
at   org.springframework.orm.hibernate4.SessionFactoryUtils.convertHibernateAccessException(SessionFactoryUtils.java:181)
at org.springframework.orm.hibernate4.HibernateTransactionManager.convertHibernateAccessException(HibernateTransactionManager.java:680)
at org.springframework.orm.hibernate4.HibernateTransactionManager.doCommit(HibernateTransactionManager.java:562)
at org.springframework.transaction.support.AbstractPlatformTransactionManager.processCommit(AbstractPlatformTransactionManager.java:755)
at org.springframework.transaction.support.AbstractPlatformTransactionManager.commit(AbstractPlatformTransactionManager.java:724)
at org.springframework.transaction.interceptor.TransactionAspectSupport.commitTransactionAfterReturning(TransactionAspectSupport.java:475)
at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:270)
at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:94)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172)
at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:204)
at com.sun.proxy.$Proxy163.save(Unknown Source)
at it.openingcode.ep.web.controller.TariffeBaseComposizioniController.doUpdate(TariffeBaseComposizioniController.java:210)
at it.openingcode.ep.web.controller.TariffeBaseComposizioniController.doUpdate(TariffeBaseComposizioniController.java:36)
at it.openingcode.ep.web.AbstractCrudController.update(AbstractCrudController.java:163)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.springframework.web.method.support.InvocableHandlerMethod.invoke(InvocableHandlerMethod.java:219)
at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:132)
at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:104)
at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandleMethod(RequestMappingHandlerAdapter.java:745)
at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:686)
at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:80)
at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:925)
at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:856)
at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:936)
at org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:838)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:650)
at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:812)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:731)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:303)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:208)
at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:52)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:241)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:208)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:330)
at org.springframework.security.web.access.intercept.FilterSecurityInterceptor.invoke(FilterSecurityInterceptor.java:118)
at org.springframework.security.web.access.intercept.FilterSecurityInterceptor.doFilter(FilterSecurityInterceptor.java:84)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
at org.springframework.security.web.access.ExceptionTranslationFilter.doFilter(ExceptionTranslationFilter.java:113)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
at org.springframework.security.web.session.SessionManagementFilter.doFilter(SessionManagementFilter.java:103)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
at org.springframework.security.web.authentication.AnonymousAuthenticationFilter.doFilter(AnonymousAuthenticationFilter.java:113)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
at org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter.doFilter(SecurityContextHolderAwareRequestFilter.java:154)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
at org.springframework.security.web.savedrequest.RequestCacheAwareFilter.doFilter(RequestCacheAwareFilter.java:45)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
at org.springframework.security.web.authentication.www.BasicAuthenticationFilter.doFilter(BasicAuthenticationFilter.java:150)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
at org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter.doFilter(AbstractAuthenticationProcessingFilter.java:199)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
at org.springframework.security.web.authentication.logout.LogoutFilter.doFilter(LogoutFilter.java:110)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
at org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter.doFilterInternal(WebAsyncManagerIntegrationFilter.java:50)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
at org.springframework.security.web.context.SecurityContextPersistenceFilter.doFilter(SecurityContextPersistenceFilter.java:87)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
at org.springframework.security.web.FilterChainProxy.doFilterInternal(FilterChainProxy.java:192)
at org.springframework.security.web.FilterChainProxy.doFilter(FilterChainProxy.java:160)
at org.springframework.web.filter.DelegatingFilterProxy.invokeDelegate(DelegatingFilterProxy.java:343)
at org.springframework.web.filter.DelegatingFilterProxy.doFilter(DelegatingFilterProxy.java:260)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:241)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:208)
at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:219)
at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:110)
at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:494)
at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:169)
at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:104)
at org.apache.catalina.valves.AccessLogValve.invoke(AccessLogValve.java:1025)
at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:116)
at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:445)
at org.apache.coyote.http11.AbstractHttp11Processor.process(AbstractHttp11Processor.java:1137)
at org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:637)
at org.apache.tomcat.util.net.AprEndpoint$SocketProcessor.doRun(AprEndpoint.java:2575)
at org.apache.tomcat.util.net.AprEndpoint$SocketProcessor.run(AprEndpoint.java:2564)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
at java.lang.Thread.run(Thread.java:748)
Caused by: org.hibernate.StaleStateException: Batch update returned unexpected row count from update [0]; actual row count: 0; expected: 1
at org.hibernate.jdbc.Expectations$BasicExpectation.checkBatched(Expectations.java:81)
at org.hibernate.jdbc.Expectations$BasicExpectation.verifyOutcome(Expectations.java:73)
at org.hibernate.engine.jdbc.batch.internal.NonBatchingBatch.addToBatch(NonBatchingBatch.java:59)
at org.hibernate.persister.entity.AbstractEntityPersister.update(AbstractEntityPersister.java:3224)
at org.hibernate.persister.entity.AbstractEntityPersister.updateOrInsert(AbstractEntityPersister.java:3126)
at org.hibernate.persister.entity.AbstractEntityPersister.update(AbstractEntityPersister.java:3456)
at org.hibernate.action.internal.EntityUpdateAction.execute(EntityUpdateAction.java:140)
at org.hibernate.engine.spi.ActionQueue.execute(ActionQueue.java:364)
at org.hibernate.engine.spi.ActionQueue.executeActions(ActionQueue.java:356)
at org.hibernate.engine.spi.ActionQueue.executeActions(ActionQueue.java:278)
at org.hibernate.event.internal.AbstractFlushingEventListener.performExecutions(AbstractFlushingEventListener.java:328)
at org.hibernate.event.internal.DefaultFlushEventListener.onFlush(DefaultFlushEventListener.java:52)
at org.hibernate.internal.SessionImpl.flush(SessionImpl.java:1234)
at org.hibernate.internal.SessionImpl.managedFlush(SessionImpl.java:404)
at org.hibernate.engine.transaction.internal.jdbc.JdbcTransaction.beforeTransactionCommit(JdbcTransaction.java:101)
at org.hibernate.engine.transaction.spi.AbstractTransactionImpl.commit(AbstractTransactionImpl.java:175)
at org.springframework.orm.hibernate4.HibernateTransactionManager.doCommit(HibernateTransactionManager.java:554)
... 80 more

I've understood that the problem is that in my object there is a TipiComposizioni entity with a null id (it has only SIGLA value inside). In Oracle table, TIPI_COMPOSIZIONI has a natural key with the field SIGLA.

I've solved the issue loading the whole TipiComposizioni entity from database, with the ID value, but I would like to understand why Hibernate raises that exception and if there is another way to solve it. I thought about some editing in JPA annotations in model class, but nothing works.

EDIT:

here the scripts of the two involved Oracle tables:

CREATE TABLE TARIFFE_BASE_COMPOSIZIONI
(
  ID                 NUMBER(11),
  ID_FASCIA          NUMBER(11),
  TIPO_COMPOSIZIONE  VARCHAR2(20 BYTE),
  PREZZO             NUMBER(15,5)               DEFAULT 0,
  PUNTI              NUMBER(11)                 DEFAULT 0
);


CREATE UNIQUE INDEX NK_TARIFFE_BASE_COMPOSIZIONI ON TARIFFE_BASE_COMPOSIZIONI
(ID_FASCIA, TIPO_COMPOSIZIONE);

CREATE UNIQUE INDEX PK_TARIFFE_BASE_COMPOSIZIONI ON TARIFFE_BASE_COMPOSIZIONI
(ID);



ALTER TABLE TARIFFE_BASE_COMPOSIZIONI ADD (
 CONSTRAINT PK_TARIFFE_BASE_COMPOSIZIONI
 PRIMARY KEY
 (ID)
 USING INDEX PK_TARIFFE_BASE_COMPOSIZIONI
 ENABLE VALIDATE);

ALTER TABLE TARIFFE_BASE_COMPOSIZIONI ADD (
 CONSTRAINT FK_TABC_FASC 
 FOREIGN KEY (ID_FASCIA) 
 REFERENCES FASCE (ID)
 ENABLE VALIDATE,
 CONSTRAINT FK_TABC_TICO 
 FOREIGN KEY (TIPO_COMPOSIZIONE) 
 REFERENCES TIPI_COMPOSIZIONI (SIGLA)
 ENABLE VALIDATE);

---------------

CREATE TABLE TIPI_COMPOSIZIONI
(
  ID     NUMBER(11),
  SIGLA  VARCHAR2(20 BYTE)                      NOT NULL,
  NOME   VARCHAR2(255 BYTE)                     NOT NULL
);

CREATE UNIQUE INDEX NK_TIPI_COMPOSIZIONI ON TIPI_COMPOSIZIONI
 (SIGLA);

CREATE UNIQUE INDEX PK_TIPI_COMPOSIZIONI ON TIPI_COMPOSIZIONI
 (ID);

ALTER TABLE TIPI_COMPOSIZIONI ADD (
  CONSTRAINT PK_TIPI_COMPOSIZIONI
  PRIMARY KEY
  (ID)
  USING INDEX PK_TIPI_COMPOSIZIONI
  ENABLE VALIDATE,
  CONSTRAINT NK_TIPI_COMPOSIZIONI
  UNIQUE (SIGLA)
  USING INDEX NK_TIPI_COMPOSIZIONI
  ENABLE VALIDATE);

EDIT 2: I've added the following in Class definitions (I forgot to add it when I wrote the question):

private static final long serialVersionUID = 1L;

does the serialVersionUID affects the save() process?

I've also noticed that Hibernate performs an INSERT INTO query and after that tries to perform an UPDATE query on the same TARIFFE_BASE_COMPOSIZIONI table.

CodePudding user response:

I think there is more code involved causing the problem. This solution that I am writing is just a workaround, but the real problem is a mistery because we don´t have the entire code. Just call getCurrentSession().flush() after calling getCurrentSession().save(t)

getCurrentSession().save(t);
getCurrentSession().flush();

CodePudding user response:

Maybe mapping the attribute sigla as natural identifier will help:

public class TipiComposizioni implements Serializable {

private static final long serialVersionUID = 1L;

 @Id
 // Is this correct? You said the id is null for a row on the db
 @Basic(optional = false)
 @NotNull
 @Column(name = "ID")
 private Long id;

 @NaturalId
 @Size(max = 20)
 @Column(name = "SIGLA")     
 private String sigla;

 @Size(max = 255)
 @Column(name = "NOME")
 private String nome;
 ...
}

When you update TariffeBaseComposizioni:

String sigla = ...
TariffeBaseComposizioni tariffe =  ...


TipiComposizioni tipo = getCurrentSession()
    .bySimpleNaturalId( TipiComposizioni.class )
    .getReference(sigla ); // Assuming you know already it exists in the db
tariffe.setTipoComposizione( tipi );
...

I think the fact that some of the identifiers in TipiComposizioni are null is causing issues when Hibernate create the association, making it believes that the entity has changed while it was updating the association (I haven't tested this theory).

Using getReference shouldn't cause the execution of any additional query to select or update TipiComposizioni. Unless something changes in tipo (Which column the update query in your question was trying to modify?)

  • Related