0

I am trying to get @Transactional working in Spring Boot when updating for Multiple tables. This code is not rolling back on error. How can I fix this? I ran test case where Table 1 and 2 works, and Table3 fails. It should rollback on all 1, 2, 3. Are there any other syntax or annotations missing in the code?

Wondering if its this issue: Does using spring JdbcTemplate create a new connection to the sql server?

Attempt 1:

@Transactional
public void createData() {
    updateTable1();
    updateTable2();
    updateTable3();
}

private updateTable1() {
    namedParameterJdbcTemplate.batchUpdate(TABLE1_INSERT_STRING, parameters1);
}

private updateTable2() {
    namedParameterJdbcTemplate.batchUpdate(TABLE2_INSERT_STRING, parameters2);
}

private updateTable3() {
    namedParameterJdbcTemplate.batchUpdate(TABLE3_INSERT_STRING, parameters3);
}

Application has @EnableTransactionManagement:

@EnableTransactionManagement
public class Application extends SpringBootServletInitializer {
    public static void main(String[] args) {
      

Attempt 1 Logging:

===================================
2021-10-11 19:17:58 DEBUG 
TraceId:  
CustomerId: 
CLASS: org.springframework.jdbc.datasource.DataSourceUtils
Fetching JDBC Connection from DataSource

===================================
2021-10-11 19:17:59 DEBUG 
TraceId: 151f53360d2b4668 
CustomerId: 
CLASS: org.springframework.jdbc.support.JdbcTransactionManager
Creating new transaction with name [com.abccompany.services.service.charge.capture.server.repository.sql.EncounterRepository.createEncounter]: PROPAGATION_REQUIRED,ISOLATION_DEFAULT
   
===================================
2021-10-11 19:17:59 TRACE 
TraceId: 151f53360d2b4668 
CustomerId: 
CLASS: org.springframework.transaction.interceptor.TransactionInterceptor
Getting transaction for [com.abccompany.services.service.charge.capture.server.repository.sql.EncounterRepository.createEncounter]

===================================
2021-10-11 19:17:59 DEBUG 
TraceId: 151f53360d2b4668 
CustomerId: 
CLASS: org.springframework.jdbc.core.JdbcTemplate
Executing SQL update and returning generated keys

===================================
2021-10-11 19:17:59 DEBUG 
TraceId: 151f53360d2b4668 
CustomerId: 
CLASS: org.springframework.jdbc.core.JdbcTemplate
Executing prepared SQL statement [INSERT INTO dbo.Encounter(PracticeID, PatientID, DoctorID, AppointmentID, LocationID, Notes, EncounterStatusID, AdminNotes, AmountPaid, CreatedUserID, PlaceOfServiceCode, PostingDate, PatientCaseID, DateOfService, DateOfServiceTo, SupervisingProviderID, EncounterGuid, ClaimTypeID, EncounterMode, DoNotSendElectronic, DoNotSendElectronicSecondary, overrideClosingDate, ReferringPhysicianID) VALUES ( ?, ? , ?,?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ISNULL(?,newid()), ?, ?, ?, ?, ?, ?)]

===================================
2021-10-11 19:17:59 DEBUG 
TraceId: 151f53360d2b4668 
CustomerId: 
CLASS: org.springframework.jdbc.datasource.DataSourceUtils
Fetching JDBC Connection from DataSource

===================================
2021-10-11 19:18:00 DEBUG 
TraceId: 151f53360d2b4668 
CustomerId: 
CLASS: org.springframework.jdbc.support.SQLErrorCodesFactory
Looking up default SQLErrorCodes for DataSource [com.kareo.library.servercore.data.pm.PMDataSourceFactory$PMDataSource@2a02c45a]

===================================
2021-10-11 19:18:00 DEBUG 
TraceId: 151f53360d2b4668 
CustomerId: 
CLASS: org.springframework.jdbc.support.SQLErrorCodesFactory
SQL error codes for 'Microsoft SQL Server' found

===================================
2021-10-11 19:18:00 DEBUG 
TraceId: 151f53360d2b4668 
CustomerId: 
CLASS: org.springframework.jdbc.support.SQLErrorCodesFactory
Caching SQL error codes for DataSource [com.kareo.library.servercore.data.pm.PMDataSourceFactory$PMDataSource@2a02c45a]: database product name is 'Microsoft SQL Server'

===================================
2021-10-11 19:18:00 DEBUG 
TraceId: 151f53360d2b4668 
CustomerId: 
CLASS: org.springframework.jdbc.support.SQLErrorCodeSQLExceptionTranslator
Unable to translate SQLException with Error code '8152', will now try the fallback translator

===================================
2021-10-11 19:18:00 DEBUG 
TraceId: 151f53360d2b4668 
CustomerId: 
CLASS: org.springframework.jdbc.support.SQLStateSQLExceptionTranslator
Extracted SQL state class '22' from value '22001'

===================================
2021-10-11 19:18:00 TRACE 
TraceId: 151f53360d2b4668 
CustomerId: 
CLASS: org.springframework.transaction.interceptor.TransactionInterceptor
Completing transaction for [com.kareo.services.service.charge.capture.server.repository.sql.EncounterRepository.createEncounter] after exception: org.springframework.dao.DataIntegrityViolationException: PreparedStatementCallback; SQL [INSERT INTO dbo.EncounterProcedure(EncounterID, ProcedureCodeDictionaryID, ServiceChargeAmount, AnesthesiaTime, ServiceUnitCount, ProcedureModifier1, ProcedureModifier2, ProcedureModifier3, ProcedureModifier4, ServiceEndDate, ProcedureDateOfService, PracticeID) VALUES (?, ? , ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)]; Data truncation; nested exception is java.sql.BatchUpdateException: Data truncation

===================================
2021-10-11 19:18:00 TRACE 
TraceId: 151f53360d2b4668 
CustomerId: 
CLASS: org.springframework.transaction.interceptor.RuleBasedTransactionAttribute
Applying rules to determine whether transaction should rollback on org.springframework.dao.DataIntegrityViolationException: PreparedStatementCallback; SQL [INSERT INTO dbo.EncounterProcedure(EncounterID, ProcedureCodeDictionaryID, ServiceChargeAmount, AnesthesiaTime, ServiceUnitCount, ProcedureModifier1, ProcedureModifier2, ProcedureModifier3, ProcedureModifier4, ServiceEndDate, ProcedureDateOfService, PracticeID) VALUES (?, ? , ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)]; Data truncation; nested exception is java.sql.BatchUpdateException: Data truncation

===================================
2021-10-11 19:18:00 TRACE 
TraceId: 151f53360d2b4668 
CustomerId: 
CLASS: org.springframework.transaction.interceptor.RuleBasedTransactionAttribute
Winning rollback rule is: null

===================================
2021-10-11 19:18:00 TRACE 
TraceId: 151f53360d2b4668 
CustomerId: 
CLASS: org.springframework.transaction.interceptor.RuleBasedTransactionAttribute
No relevant rollback rule found: applying default rules

===================================
2021-10-11 19:18:00 DEBUG 
TraceId: 151f53360d2b4668 
CustomerId: 
CLASS: org.springframework.jdbc.support.JdbcTransactionManager
Initiating transaction rollback

enter image description here

Attempt 2:

private TransactionTemplate transactionTemplate;

public TestRepository(PlatformTransactionManager transactionManager) {
     this.transactionTemplate = new TransactionTemplate(transactionManager);
}

public void createData() {
    transactionTemplate.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRES_NEW);
    transactionTemplate.execute(status -> {
        updateTable1();
        updateTable2();
        updateTable3();
    } catch (Exception exception) {
         exception.printStackTrace();
         status.setRollbackOnly();
         throw new RuntimeException(exception);
    }

Attempt 2 Logging:

===================================
2021-10-11 17:36:54 TRACE 
TraceId: bba0ffd336161184 
CustomerId: 
CLASS: org.springframework.transaction.interceptor.TransactionInterceptor
Completing transaction for [com.abccompany.services.service.charge.capture.server.facade.EncounterFacade.createEncounter] after exception: java.lang.RuntimeException: org.springframework.dao.DataIntegrityViolationException: PreparedStatementCallback; SQL [INSERT INTO dbo.EncounterProcedure(EncounterID, ProcedureCodeDictionaryID, ServiceChargeAmount, AnesthesiaTime, ServiceUnitCount, ProcedureModifier1, ProcedureModifier2, ProcedureModifier3, ProcedureModifier4, ServiceEndDate, ProcedureDateOfService, PracticeID) VALUES (?, ? , ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)]; Data truncation; nested exception is java.sql.BatchUpdateException: Data truncation

===================================
2021-10-11 17:36:54 TRACE 
TraceId: bba0ffd336161184 
CustomerId: 
CLASS: org.springframework.transaction.interceptor.RuleBasedTransactionAttribute
Applying rules to determine whether transaction should rollback on java.lang.RuntimeException: org.springframework.dao.DataIntegrityViolationException: PreparedStatementCallback; SQL [INSERT INTO dbo.EncounterProcedure(EncounterID, ProcedureCodeDictionaryID, ServiceChargeAmount, AnesthesiaTime, ServiceUnitCount, ProcedureModifier1, ProcedureModifier2, ProcedureModifier3, ProcedureModifier4, ServiceEndDate, ProcedureDateOfService, PracticeID) VALUES (?, ? , ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)]; Data truncation; nested exception is java.sql.BatchUpdateException: Data truncation

===================================
2021-10-11 17:36:54 TRACE 
TraceId: bba0ffd336161184 
CustomerId: 
CLASS: org.springframework.transaction.interceptor.RuleBasedTransactionAttribute
Winning rollback rule is: null

===================================
2021-10-11 17:36:54 TRACE 
TraceId: bba0ffd336161184 
CustomerId: 
CLASS: org.springframework.transaction.interceptor.RuleBasedTransactionAttribute
No relevant rollback rule found: applying default rules

Full Log

===================================
2021-10-14 17:57:54 TRACE 
TraceId: 7dc1bb4dd4fca9ed 
CustomerId: 
CLASS: org.springframework.transaction.interceptor.TransactionInterceptor
Getting transaction for [com.abccompany.services.service.charge.capture.server.facade.EncounterFacade.createEncounter]

===================================
2021-10-14 17:57:54 TRACE 
TraceId: 7dc1bb4dd4fca9ed 
CustomerId: 
CLASS: org.springframework.transaction.interceptor.TransactionInterceptor
Getting transaction for [com.abccompany.services.service.charge.capture.server.repository.sql.EncounterRepository.createEncounter]

===================================
2021-10-14 17:57:55 TRACE 
TraceId: 7dc1bb4dd4fca9ed 
CustomerId: 
CLASS: org.springframework.transaction.interceptor.TransactionInterceptor
Completing transaction for [com.abccompany.services.service.charge.capture.server.repository.sql.EncounterRepository.createEncounter] after exception: org.springframework.dao.DataIntegrityViolationException: PreparedStatementCallback; SQL [INSERT INTO dbo.EncounterProcedure(EncounterID, ProcedureCodeDictionaryID, ServiceChargeAmount, AnesthesiaTime, ServiceUnitCount, ProcedureModifier1, ProcedureModifier2, ProcedureModifier3, ProcedureModifier4, ServiceEndDate, ProcedureDateOfService, PracticeID, EncounterDiagnosisID1, EncounterDiagnosisID2, EncounterDiagnosisID3, EncounterDiagnosisID4, EncounterDiagnosisID5, EncounterDiagnosisID6, EncounterDiagnosisID7, EncounterDiagnosisID8) VALUES (?, ? , ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?,?, ?, ?, ?, ?)]; Data truncation; nested exception is java.sql.BatchUpdateException: Data truncation

===================================
2021-10-14 17:57:55 TRACE 
TraceId: 7dc1bb4dd4fca9ed 
CustomerId: 
CLASS: org.springframework.transaction.interceptor.RuleBasedTransactionAttribute
Applying rules to determine whether transaction should rollback on org.springframework.dao.DataIntegrityViolationException: PreparedStatementCallback; SQL [INSERT INTO dbo.EncounterProcedure(EncounterID, ProcedureCodeDictionaryID, ServiceChargeAmount, AnesthesiaTime, ServiceUnitCount, ProcedureModifier1, ProcedureModifier2, ProcedureModifier3, ProcedureModifier4, ServiceEndDate, ProcedureDateOfService, PracticeID, EncounterDiagnosisID1, EncounterDiagnosisID2, EncounterDiagnosisID3, EncounterDiagnosisID4, EncounterDiagnosisID5, EncounterDiagnosisID6, EncounterDiagnosisID7, EncounterDiagnosisID8) VALUES (?, ? , ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?,?, ?, ?, ?, ?)]; Data truncation; nested exception is java.sql.BatchUpdateException: Data truncation

===================================
2021-10-14 17:57:55 TRACE 
TraceId: 7dc1bb4dd4fca9ed 
CustomerId: 
CLASS: org.springframework.transaction.interceptor.RuleBasedTransactionAttribute
Winning rollback rule is: null

===================================
2021-10-14 17:57:55 TRACE 
TraceId: 7dc1bb4dd4fca9ed 
CustomerId: 
CLASS: org.springframework.transaction.interceptor.RuleBasedTransactionAttribute
No relevant rollback rule found: applying default rules

===================================
2021-10-14 17:57:55 TRACE 
TraceId: 7dc1bb4dd4fca9ed 
CustomerId: 
CLASS: org.springframework.transaction.interceptor.TransactionInterceptor
Completing transaction for [com.abccompany.services.service.charge.capture.server.facade.EncounterFacade.createEncounter] after exception: org.springframework.dao.DataIntegrityViolationException: PreparedStatementCallback; SQL [INSERT INTO dbo.EncounterProcedure(EncounterID, ProcedureCodeDictionaryID, ServiceChargeAmount, AnesthesiaTime, ServiceUnitCount, ProcedureModifier1, ProcedureModifier2, ProcedureModifier3, ProcedureModifier4, ServiceEndDate, ProcedureDateOfService, PracticeID, EncounterDiagnosisID1, EncounterDiagnosisID2, EncounterDiagnosisID3, EncounterDiagnosisID4, EncounterDiagnosisID5, EncounterDiagnosisID6, EncounterDiagnosisID7, EncounterDiagnosisID8) VALUES (?, ? , ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?,?, ?, ?, ?, ?)]; Data truncation; nested exception is java.sql.BatchUpdateException: Data truncation

===================================
2021-10-14 17:57:55 TRACE 
TraceId: 7dc1bb4dd4fca9ed 
CustomerId: 
CLASS: org.springframework.transaction.interceptor.RuleBasedTransactionAttribute
Applying rules to determine whether transaction should rollback on org.springframework.dao.DataIntegrityViolationException: PreparedStatementCallback; SQL [INSERT INTO dbo.EncounterProcedure(EncounterID, ProcedureCodeDictionaryID, ServiceChargeAmount, AnesthesiaTime, ServiceUnitCount, ProcedureModifier1, ProcedureModifier2, ProcedureModifier3, ProcedureModifier4, ServiceEndDate, ProcedureDateOfService, PracticeID, EncounterDiagnosisID1, EncounterDiagnosisID2, EncounterDiagnosisID3, EncounterDiagnosisID4, EncounterDiagnosisID5, EncounterDiagnosisID6, EncounterDiagnosisID7, EncounterDiagnosisID8) VALUES (?, ? , ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?,?, ?, ?, ?, ?)]; Data truncation; nested exception is java.sql.BatchUpdateException: Data truncation

===================================
2021-10-14 17:57:55 TRACE 
TraceId: 7dc1bb4dd4fca9ed 
CustomerId: 
CLASS: org.springframework.transaction.interceptor.RuleBasedTransactionAttribute
Winning rollback rule is: null

===================================
2021-10-14 17:57:55 TRACE 
TraceId: 7dc1bb4dd4fca9ed 
CustomerId: 
CLASS: org.springframework.transaction.interceptor.RuleBasedTransactionAttribute
No relevant rollback rule found: applying default rules

===================================
2021-10-14 17:57:55 ERROR 
TraceId: 7dc1bb4dd4fca9ed 
CustomerId: 
CLASS: errorLogger
Message: PreparedStatementCallback; SQL [INSERT INTO dbo.EncounterProcedure(EncounterID, ProcedureCodeDictionaryID, ServiceChargeAmount, AnesthesiaTime, ServiceUnitCount, ProcedureModifier1, ProcedureModifier2, ProcedureModifier3, ProcedureModifier4, ServiceEndDate, ProcedureDateOfService, PracticeID, EncounterDiagnosisID1, EncounterDiagnosisID2, EncounterDiagnosisID3, EncounterDiagnosisID4, EncounterDiagnosisID5, EncounterDiagnosisID6, EncounterDiagnosisID7, EncounterDiagnosisID8) VALUES (?, ? , ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?,?, ?, ?, ?, ?)]; Data truncation; nested exception is java.sql.BatchUpdateException: Data truncation
ErrorUUID: f538fc46-984b-4e55-bb10-f592466970b9
ErrorCode: 500
ErrorType: DataIntegrityViolationException
ServicesStack: charge-capture-service
ServiceName: charge-capture-service
Host: KMAC-15SJGH5
StackTrace: org.springframework.dao.DataIntegrityViolationException: PreparedStatementCallback; SQL [INSERT INTO dbo.EncounterProcedure(EncounterID, ProcedureCodeDictionaryID, ServiceChargeAmount, AnesthesiaTime, ServiceUnitCount, ProcedureModifier1, ProcedureModifier2, ProcedureModifier3, ProcedureModifier4, ServiceEndDate, ProcedureDateOfService, PracticeID, EncounterDiagnosisID1, EncounterDiagnosisID2, EncounterDiagnosisID3, EncounterDiagnosisID4, EncounterDiagnosisID5, EncounterDiagnosisID6, EncounterDiagnosisID7, EncounterDiagnosisID8) VALUES (?, ? , ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?,?, ?, ?, ?, ?)]; Data truncation; nested exception is java.sql.BatchUpdateException: Data truncation
    at org.springframework.jdbc.support.SQLStateSQLExceptionTranslator.doTranslate(SQLStateSQLExceptionTranslator.java:104)
    at org.springframework.jdbc.support.AbstractFallbackSQLExceptionTranslator.translate(AbstractFallbackSQLExceptionTranslator.java:70)
    at org.springframework.jdbc.support.AbstractFallbackSQLExceptionTranslator.translate(AbstractFallbackSQLExceptionTranslator.java:79)
    at org.springframework.jdbc.support.AbstractFallbackSQLExceptionTranslator.translate(AbstractFallbackSQLExceptionTranslator.java:79)
    at org.springframework.jdbc.core.JdbcTemplate.translateException(JdbcTemplate.java:1541)
    at org.springframework.jdbc.core.JdbcTemplate.execute(JdbcTemplate.java:667)
    at org.springframework.jdbc.core.JdbcTemplate.execute(JdbcTemplate.java:691)
    at org.springframework.jdbc.core.JdbcTemplate.batchUpdate(JdbcTemplate.java:1034)
    at org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate.batchUpdate(NamedParameterJdbcTemplate.java:373)
    at com.abccompany.library.servercore.data.StatisticsGatheringNamedParameterJdbcTemplate.access$801(StatisticsGatheringNamedParameterJdbcTemplate.java:25)
    at com.abccompany.library.servercore.data.StatisticsGatheringNamedParameterJdbcTemplate$9.call(StatisticsGatheringNamedParameterJdbcTemplate.java:173)
    at com.abccompany.library.servercore.data.StatisticsGatheringNamedParameterJdbcTemplate$9.call(StatisticsGatheringNamedParameterJdbcTemplate.java:170)
    at com.abccompany.library.servercore.data.StatisticsGatheringNamedParameterJdbcTemplate.recordExecutionTime(StatisticsGatheringNamedParameterJdbcTemplate.java:188)
    at com.abccompany.library.servercore.data.StatisticsGatheringNamedParameterJdbcTemplate.batchUpdate(StatisticsGatheringNamedParameterJdbcTemplate.java:170)
    at com.abccompany.services.service.charge.capture.server.repository.sql.EncounterRepository.createEncounter(EncounterRepository.java:325)
    at com.abccompany.services.service.charge.capture.server.repository.sql.EncounterRepository$$FastClassBySpringCGLIB$$dfd7e0f6.invoke(<generated>)
    at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:218)
    at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.invokeJoinpoint(CglibAopProxy.java:779)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:163)
    at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:750)
    at org.springframework.dao.support.PersistenceExceptionTranslationInterceptor.invoke(PersistenceExceptionTranslationInterceptor.java:137)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)
    at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:750)
    at org.springframework.transaction.interceptor.TransactionInterceptor$1.proceedWithInvocation(TransactionInterceptor.java:123)
    at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:388)
    at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:119)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)
    at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:750)
    at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:692)
    at com.abccompany.services.service.charge.capture.server.repository.sql.EncounterRepository$$EnhancerBySpringCGLIB$$59b88173.createEncounter(<generated>)
    at com.abccompany.services.service.charge.capture.server.facade.EncounterFacade.createEncounter(EncounterFacade.java:65)
    at com.abccompany.services.service.charge.capture.server.facade.EncounterFacade$$FastClassBySpringCGLIB$$23023cb6.invoke(<generated>)
    at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:218)
    at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.invokeJoinpoint(CglibAopProxy.java:779)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:163)
    at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:750)
    at org.springframework.transaction.interceptor.TransactionInterceptor$1.proceedWithInvocation(TransactionInterceptor.java:123)
    at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:388)
    at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:119)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)
    at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:750)
    at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:692)
    at com.abccompany.services.service.charge.capture.server.facade.EncounterFacade$$EnhancerBySpringCGLIB$$6baeecb3.createEncounter(<generated>)
    at com.abccompany.services.service.charge.capture.server.controller.EncounterController.createEncounter(EncounterController.java:35)
    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:497)
    at org.springframework.web.method.support.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:197)
    at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:141)
    at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:106)
    at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:894)
    at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:808)
    at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:87)
    at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1060)
    at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:962)
    at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1006)
    at org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:909)
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:652)
    at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:883)
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:733)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:227)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162)
    at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:53)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:189)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162)
    at com.abccompany.library.springboot.starter.abccompanytrace.abccompanyTracerFilter.doFilterInternal(abccompanyTracerFilter.java:45)
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:189)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162)
    at org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:100)
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:189)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162)
    at org.springframework.web.filter.FormContentFilter.doFilterInternal(FormContentFilter.java:93)
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:189)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162)
    at org.springframework.boot.actuate.metrics.web.servlet.WebMvcMetricsFilter.doFilterInternal(WebMvcMetricsFilter.java:93)
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:189)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162)
    at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:201)
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:189)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162)
    at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:202)
    at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:97)
    at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:542)
    at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:143)
    at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:92)
    at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:78)
    at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:346)
    at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:374)
    at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:65)
    at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:887)
    at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1684)
    at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
    at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
    at java.lang.Thread.run(Thread.java:745)
Caused by: java.sql.BatchUpdateException: Data truncation
    at net.sourceforge.jtds.jdbc.JtdsStatement.executeBatch(JtdsStatement.java:1069)
    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:497)
    at org.apache.tomcat.jdbc.pool.StatementFacade$StatementProxy.invoke(StatementFacade.java:114)
    at com.sun.proxy.$Proxy204.executeBatch(Unknown Source)
    at org.springframework.jdbc.core.JdbcTemplate.lambda$batchUpdate$4(JdbcTemplate.java:1048)
    at org.springframework.jdbc.core.JdbcTemplate.execute(JdbcTemplate.java:651)
    
mattsmith5
  • 540
  • 4
  • 29
  • 67
  • Does it roll back if you have an error with one of the other tables? It really should either roll back or not with an error on any of the 3. If it doesn't work for any of them, make sure that you are not calling this method from another method in the same class. Because of the way the @Transactioal annotation works, you need to call it from another class. Also note that rollback only happens when there's a Runtime exception - checked exceptions won't cause a roll back – GreyBeardedGeek Oct 08 '21 at 21:58
  • Understood. But does it roll back if you have an error while updating table 2? – GreyBeardedGeek Oct 08 '21 at 22:01
  • You must use a `TransactionTemplate` in `createData`: https://docs.spring.io/spring-framework/docs/current/reference/html/data-access.html#tx-prog-template. This is because you are calling the `updateTable` methods yourself and `createData` won't wrap them in a db txn despite `@Transactional`. See the note block of this section: https://docs.spring.io/spring-framework/docs/current/reference/html/data-access.html#transaction-declarative-annotations-method-visibility. I believe if `createData` was called else where by another spring component, it would work. – drumonii Oct 10 '21 at 01:18
  • Please refer to accepted answer in https://stackoverflow.com/questions/12641666/springs-jdbctemplate-and-transactions – Shailendra Oct 11 '21 at 06:17

2 Answers2

2

It should work based on what codes you posted.

Several things that you can check for further troubleshooting :

  1. Make sure you are using @Transactional from the org.springframework package but not the one from javax.transaction package

  2. Turn on and configure following loggers for more detailed logging to see if there are any insights :

    • org.springframework.transaction.interceptor to TRACE level
    • org.springframework.transaction.support to DEBUG level

Pay attention if TransactionInterceptor#completeTransactionAfterThrowing() will be invoked after the exception is thrown from the transactional method . It should be invoked to handle the rollback normally.

Also pay attention to see if there are some logs saying committing transaction before the transactional method returns. If yes , then something go wrong.

Show me what are logged if you are still struggling with it.

Ken Chan
  • 84,777
  • 26
  • 143
  • 172
  • one last question, I Just noticed a lot of SQL Server Triggers on this table, I want to insert into, it has commits and rollbacks, would this affect my attempts to rollback? – mattsmith5 Oct 12 '21 at 06:41
  • the trigger and its related SQL should be atomic which means if the SQL rollback , the things done by trigger will also rollback based on understanding. If you want to be sure , you can create 3 dummy tables without any trigger to check. By the way , do you mean that if you exclude update table3 , then the rollback stuff work perfectly ? – Ken Chan Oct 12 '21 at 08:41
  • added full log, see updated question – mattsmith5 Oct 14 '21 at 17:08
  • full log is in the bottom of the updated question – mattsmith5 Oct 14 '21 at 17:37
2

I tried testing transaction behavior with jdbctemplate on a simple command line program with Spring Boot and in-memory database h2 - and it seems to be working as expected. Here is my code

ManagingTransactionsApplication



@SpringBootApplication
public class ManagingTransactionsApplication {

    public static void main(String[] args) {
        SpringApplication.run(ManagingTransactionsApplication.class, args);
    }

}


TransactionTestService


@Component
public class TransactionTestService {

    private final static Logger logger = LoggerFactory.getLogger(TransactionTestService.class);

    private final NamedParameterJdbcTemplate jdbcTemplate;

    public TransactionTestService(NamedParameterJdbcTemplate jdbcTemplate) {
        this.jdbcTemplate = jdbcTemplate;
    }

    @Transactional
    public void bookInTransaction(String... persons) {
        insertInTable1(persons[0]);
        insertInTable2(persons[1]);
        insertInTable3(persons[2]);
    }

    public void insertInTable1(String person) {
        logger.info("Booking " + person + " in a seat...");
        Map<String, String> map = new HashMap<String, String>();
        map.put("FIRST_NAME", person);
        jdbcTemplate.update("insert into TABLE1(FIRST_NAME) values (:FIRST_NAME)", map);
    }

    public void insertInTable2(String person) {
        logger.info("Booking " + person + " in a seat...");
        Map<String, String> map = new HashMap<String, String>();
        map.put("FIRST_NAME", person);
        jdbcTemplate.update("insert into TABLE2(FIRST_NAME) values (:FIRST_NAME)", map);
    }

    public void insertInTable3(String person) {
        logger.info("Booking " + person + " in a seat...");
        Map<String, String> map = new HashMap<String, String>();
        map.put("FIRST_NAME", person);
        jdbcTemplate.update("insert into TABLE3(FIRST_NAME) values (:FIRST_NAME)", map);
    }

    public List<String> findAllBookings() {
        return jdbcTemplate.query("select FIRST_NAME from BOOKINGS", (rs, rowNum) -> rs.getString("FIRST_NAME"));
    }

}

AppRunner

@Component
class AppRunner implements CommandLineRunner {

    private final static Logger logger = LoggerFactory.getLogger(AppRunner.class);
    private final TransactionTestService transTestService;

    public AppRunner(BookingService bookingService, TransactionTestService transTestService) {
        this.transTestService = transTestService;
    }

    @Override
    public void run(String... args) throws Exception {

        try {
            transTestService.bookInTransaction("Alice", "Bob", "Shailendra");
        } catch (RuntimeException e) {
            logger.info("--- The following exception is expect because 'Shailendra' is too " + "big for the DB ---v");
            logger.error(e.getMessage());
        }

        for (String person : transTestService.findAllBookings()) {
            logger.info("So far, " + person + " is booked.");
        }

    }

}


schema.sql

drop table TABLE1 if exists;
create table TABLE1(ID serial, FIRST_NAME varchar(5) NOT NULL);

drop table TABLE2 if exists;
create table TABLE2(ID serial, FIRST_NAME varchar(5) NOT NULL);

drop table TABLE3 if exists;
create table TABLE3(ID serial, FIRST_NAME varchar(5) NOT NULL);

Basically I will do 3 inserts - with the last insert the value will exceeds max length of 5 and so the insert will fail and the overall transaction should also fail and on querying data it should result in none records saved. I get below on console and no data in database is found

2021-10-11 15:00:21.726  INFO 23860 --- [           main] c.e.m.TransactionTestService             : Booking Alice in a seat...
2021-10-11 15:00:21.746  INFO 23860 --- [           main] c.e.m.TransactionTestService             : Booking Bob in a seat...
2021-10-11 15:00:21.746  INFO 23860 --- [           main] c.e.m.TransactionTestService             : Booking Shailendra in a seat...
2021-10-11 15:00:21.909  INFO 23860 --- [           main] c.e.managingtransactions.AppRunner       : --- The following exception is expect because 'Shailendra' is too big for the DB ---v
2021-10-11 15:00:21.909 ERROR 23860 --- [           main] c.e.managingtransactions.AppRunner       : PreparedStatementCallback; SQL [insert into TABLE1(FIRST_NAME) values (?)]; Value too long for column "FIRST_NAME VARCHAR(5)": "'Shailendra' (10)"; SQL statement:
insert into TABLE1(FIRST_NAME) values (?) [22001-200]; nested exception is org.h2.jdbc.JdbcSQLDataException: Value too long for column "FIRST_NAME VARCHAR(5)": "'Shailendra' (10)"; SQL statement:
insert into TABLE1(FIRST_NAME) values (?) [22001-200]

On adding debug/trace for transaction below is more detailed information on transaction managed by Spring and it mentions the fact that it rolled back transaction

2021-10-11 15:22:22.734  INFO 23124 --- [           main] c.e.m.ManagingTransactionsApplication    : Started ManagingTransactionsApplication in 5.369 seconds (JVM running for 6.179)
2021-10-11 15:22:22.734 TRACE 23124 --- [           main] o.s.t.i.TransactionInterceptor           : Getting transaction for [com.example.managingtransactions.TransactionTestService.bookInTransaction]
2021-10-11 15:22:22.765  INFO 23124 --- [           main] c.e.m.TransactionTestService             : Booking Alice in a seat...
2021-10-11 15:22:22.787  INFO 23124 --- [           main] c.e.m.TransactionTestService             : Booking Bob in a seat...
2021-10-11 15:22:22.787  INFO 23124 --- [           main] c.e.m.TransactionTestService             : Booking Shailendra in a seat...
2021-10-11 15:22:22.939 TRACE 23124 --- [           main] o.s.t.i.TransactionInterceptor           : Completing transaction for [com.example.managingtransactions.TransactionTestService.bookInTransaction] after exception: org.springframework.dao.DataIntegrityViolationException: PreparedStatementCallback; SQL [insert into TABLE1(FIRST_NAME) values (?)]; Value too long for column "FIRST_NAME VARCHAR(5)": "'Shailendra' (10)"; SQL statement:
insert into TABLE1(FIRST_NAME) values (?) [22001-200]; nested exception is org.h2.jdbc.JdbcSQLDataException: Value too long for column "FIRST_NAME VARCHAR(5)": "'Shailendra' (10)"; SQL statement:
insert into TABLE1(FIRST_NAME) values (?) [22001-200]
2021-10-11 15:22:22.941 TRACE 23124 --- [           main] **o.s.t.i.RuleBasedTransactionAttribute    : Applying rules to determine whether transaction should rollback on org.springframework.dao.DataIntegrityViolationException: PreparedStatementCallback; SQL [insert into TABLE1(FIRST_NAME) values (?)]; Value too long for column "FIRST_NAME VARCHAR(5)": "'Shailendra' (10)"; SQL statement:**
insert into TABLE1(FIRST_NAME) values (?) [22001-200]; nested exception is org.h2.jdbc.JdbcSQLDataException: Value too long for column "FIRST_NAME VARCHAR(5)": "'Shailendra' (10)"; SQL statement:
insert into TABLE1(FIRST_NAME) values (?) [22001-200]
2021-10-11 15:22:22.941 TRACE 23124 --- [           main] o.s.t.i.RuleBasedTransactionAttribute    : Winning rollback rule is: null
2021-10-11 15:22:22.941 TRACE 23124 --- [           main] **o.s.t.i.RuleBasedTransactionAttribute    : No relevant rollback rule found: applying default rules**
2021-10-11 15:22:22.941  INFO 23124 --- [           main] c.e.managingtransactions.AppRunner       : --- The following exception is expect because 'Shailendra' is too big for the DB ---v
2021-10-11 15:22:22.941 ERROR 23124 --- [           main] c.e.managingtransactions.AppRunner       : PreparedStatementCallback; SQL [insert into TABLE1(FIRST_NAME) values (?)]; Value too long for column "FIRST_NAME VARCHAR(5)": "'Shailendra' (10)"; SQL statement:
insert into TABLE1(FIRST_NAME) values (?) [22001-200]; nested exception is org.h2.jdbc.JdbcSQLDataException: Value too long for column "FIRST_NAME VARCHAR(5)": "'Shailendra' (10)"; SQL statement:
insert into TABLE1(FIRST_NAME) values (?) [22001-200]

Update:

Even if I use batchUpdate method the results remains the same

SqlParameterSource[] params = new SqlParameterSource[] { new MapSqlParameterSource("FIRST_NAME", 1),

    };
    
    jdbcTemplate.batchUpdate("insert into TABLE1(FIRST_NAME) values (:FIRST_NAME)", params);

Corresponding logs

2021-10-12 00:12:42.075 TRACE 22352 --- [           main] o.s.t.i.TransactionInterceptor           : Getting transaction for [com.example.managingtransactions.TransactionTestService.bookInTransaction]
2021-10-12 00:12:42.094  INFO 22352 --- [           main] c.e.m.TransactionTestService             : Booking Alice in a seat...
2021-10-12 00:12:42.134  INFO 22352 --- [           main] c.e.m.TransactionTestService             : Booking Bob in a seat...
2021-10-12 00:12:42.134  INFO 22352 --- [           main] c.e.m.TransactionTestService             : Booking Shailendra in a seat...
2021-10-12 00:12:42.385 TRACE 22352 --- [           main] o.s.t.i.TransactionInterceptor           : Completing transaction for [com.example.managingtransactions.TransactionTestService.bookInTransaction] after exception: org.springframework.dao.DataIntegrityViolationException: PreparedStatementCallback; SQL [insert into TABLE1(FIRST_NAME) values (?)]; Value too long for column "FIRST_NAME VARCHAR(5)": "'Shailendra' (10)"; SQL statement:
insert into TABLE1(FIRST_NAME) values (?) [22001-200]; nested exception is org.h2.jdbc.JdbcSQLDataException: Value too long for column "FIRST_NAME VARCHAR(5)": "'Shailendra' (10)"; SQL statement:
insert into TABLE1(FIRST_NAME) values (?) [22001-200]
2021-10-12 00:12:42.385 TRACE 22352 --- [           main] o.s.t.i.RuleBasedTransactionAttribute    : Applying rules to determine whether transaction should rollback on org.springframework.dao.DataIntegrityViolationException: PreparedStatementCallback; SQL [insert into TABLE1(FIRST_NAME) values (?)]; Value too long for column "FIRST_NAME VARCHAR(5)": "'Shailendra' (10)"; SQL statement:
insert into TABLE1(FIRST_NAME) values (?) [22001-200]; nested exception is org.h2.jdbc.JdbcSQLDataException: Value too long for column "FIRST_NAME VARCHAR(5)": "'Shailendra' (10)"; SQL statement:
insert into TABLE1(FIRST_NAME) values (?) [22001-200]
2021-10-12 00:12:42.385 TRACE 22352 --- [           main] o.s.t.i.RuleBasedTransactionAttribute    : Winning rollback rule is: null
2021-10-12 00:12:42.385 TRACE 22352 --- [           main] o.s.t.i.RuleBasedTransactionAttribute    : No relevant rollback rule found: applying default rules
2021-10-12 00:12:42.385  INFO 22352 --- [           main] c.e.managingtransactions.AppRunner       : --- The following exception is expect because 'Shailendra' is too big for the DB ---v
2021-10-12 00:12:42.385 ERROR 22352 --- [           main] c.e.managingtransactions.AppRunner       : PreparedStatementCallback; SQL [insert into TABLE1(FIRST_NAME) values (?)]; Value too long for column "FIRST_NAME VARCHAR(5)": "'Shailendra' (10)"; SQL statement:
insert into TABLE1(FIRST_NAME) values (?) [22001-200]; nested exception is org.h2.jdbc.JdbcSQLDataException: Value too long for column "FIRST_NAME VARCHAR(5)": "'Shailendra' (10)"; SQL statement:
insert into TABLE1(FIRST_NAME) values (?) [22001-200]

application.yml

logging:
    level:
        org.springframework:
            transaction:
               support: DEBUG
               interceptor: TRACE
Shailendra
  • 8,874
  • 2
  • 28
  • 37
  • I am using batchUpdate, wonder if that makes a difference – mattsmith5 Oct 11 '21 at 16:14
  • which line did it roll back in the log? I see this, Winning rollback rule is: null – mattsmith5 Oct 11 '21 at 17:55
  • Even if I use batchUpdate - the result is same. See the updated answer. Also I am using properties file instead of yaml – Shailendra Oct 11 '21 at 18:45
  • logging.level.org.springframework.transaction.support=DEBUG logging.level.org.springframework.transaction.interceptor=TRACE – Shailendra Oct 11 '21 at 18:46
  • I have added working application.yml as well in case you find that helpful. – Shailendra Oct 11 '21 at 18:51
  • "Winning rollback rule is: null" means that there is no specific rollback exception class mentioned and so it will follow the default rule which means rollback on all kind of runtime exception. This code is handled in Spring transaction management infrastructure and more specifically "RuleBasedTransactionAttribute" and "DefaultTransactionAttribute" classes of Spring. – Shailendra Oct 11 '21 at 18:55