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?)