Search code examples
spring-boottransactionsspring-batchisolation-level

Can't serialize access for this transaction when running single job, SERIALIZED isolation level


How to set one isolation level for the whole batch ?

Actually I'm setting isolationLevel like below :

* Update Config class*

@Configuration

@EnableBatchProcessing

public class ApplicationBatchConfig {



                private static final Logger LOGGER = LoggerFactory.getLogger(ApplicationBatchConfig.class);



                @Autowired

                private Environment environment;



                /**

                * Bean - Datasource <Br>
                *
                * @return DataSource
                */
                @Bean(name = "dataSource")

                @ConfigurationProperties(prefix = "spring.datasource")

                public DataSource dataSource() {
                  return DataSourceBuilder.create().build();
                }


                /**

                * @param dataSource 

                * @param transactionManager 

                * @return JobRepository

                * @throws Exception

                */

                @Bean(name = "jobRepository")

                public JobRepository jobRepository(final DataSource dataSource, final PlatformTransactionManager transactionManager) throws Exception {

                               final JobRepositoryFactoryBean rc = new JobRepositoryFactoryBean();

                               rc.setDatabaseType("ORACLE");

                               rc.setDataSource(dataSource);

                               rc.setTransactionManager(transactionManager);

                               rc.setIsolationLevelForCreate("ISOLATION_READ_COMMITTED");
                               return rc.getObject();

                }

                /**

                *

                 * @return PropertySourcesPlaceholderConfigurer

                */

                @Bean

                public static PropertySourcesPlaceholderConfigurer propertyConfigurer() {

                               String location = System.getProperty("ext.properties.dir");

                               if (!StringUtils.hasText(location)) {

                                               LOGGER.warn("utilisation repertoire properties par defaut");

                                               location = "classpath:";

                               }

                               final PropertySourcesPlaceholderConfigurer ppc = new PropertySourcesPlaceholderConfigurer();

                               String propertiesLocation;

                               String propertiesBatchLocation;

                               String propertiesLogLocation;

                               String propertiesAppliLocation;



                               if (location.startsWith("classpath:")) {

                                               propertiesLocation = location.replaceFirst("classpath:", "");

                                               if (StringUtils.hasText(propertiesLocation)) {

                                                               propertiesBatchLocation = propertiesLocation + "/" + IConstantBatch.BATCH_PROPERTIES;

                                                               propertiesLogLocation = propertiesLocation + "/" + IConstantBatch.LOG4J_PROPERTIES;

                                                               propertiesAppliLocation = propertiesLocation + "/" + IConstantBatch.APPLI_PROPERTIES;

                                               } else {

                                                               propertiesBatchLocation = IConstantBatch.BATCH_PROPERTIES;

                                                               propertiesLogLocation = IConstantBatch.LOG4J_PROPERTIES;

                                                               propertiesAppliLocation = IConstantBatch.APPLI_PROPERTIES;

                                               }

                                               ppc.setLocations(new Resource[] { new ClassPathResource(propertiesBatchLocation), new ClassPathResource(propertiesLogLocation),

                                                                              new ClassPathResource(propertiesAppliLocation) });

                               } else {

                                               propertiesLocation = location;

                                               propertiesBatchLocation = propertiesLocation + "/" + IConstantBatch.BATCH_PROPERTIES;

                                               propertiesLogLocation = propertiesLocation + "/" + IConstantBatch.LOG4J_PROPERTIES;

                                               propertiesAppliLocation = propertiesLocation + "/" + IConstantBatch.APPLI_PROPERTIES;

                                               // ajout des fichier de properties

                                               ppc.setLocations(new Resource[] { new FileSystemResource(propertiesBatchLocation), new FileSystemResource(propertiesLogLocation),

                                                                              new FileSystemResource(propertiesAppliLocation) });



                               }

                               ppc.setIgnoreResourceNotFound(true);

                               ppc.setFileEncoding(IConstantBatch.PROPERTIES_ENCODING);

                               ppc.setOrder(1);

                               ppc.setIgnoreUnresolvablePlaceholders(true);

                               LOGGER.info(" repertoire de configuration batch utilisé : {}", propertiesLocation);



                               return ppc;



                }
}

But during the running when spring batch want to use its technicals tables the isolation level is switched from 2 to 8 - see the log below

* Update Log*

      ================================================================================

    2019-03-25 14:52:43.129 DEBUG 11624 --- [           main] o.s.j.d.DataSourceTransactionManager     : Creating new transaction with name [org.springframework.batch.core.repository.support.SimpleJobRepository.getLastJobExecution]: PROPAGATION_REQUIRES_NEW,ISOLATION_SERIALIZABLE

    2019-03-25 14:52:43.129 DEBUG 11624 --- [           main] o.s.j.d.DataSourceTransactionManager     : Acquired Connection [ProxyConnection[PooledConnection[oracle.jdbc.driver.T4CConnection@eb397d18]]] for JDBC transaction

    2019-03-25 14:52:43.129 DEBUG 11624 --- [           main] o.s.jdbc.datasource.DataSourceUtils      : Changing isolation level of JDBC Connection [ProxyConnection[PooledConnection[oracle.jdbc.driver.T4CConnection@eb397d18]]] to 8

    2019-03-25 14:52:43.129 DEBUG 11624 --- [           main] o.s.j.d.DataSourceTransactionManager     : Switching JDBC Connection [ProxyConnection[PooledConnection[oracle.jdbc.driver.T4CConnection@eb397d18]]] to manual commit

    2019-03-25 14:52:43.129 TRACE 11624 --- [           main] .s.t.s.TransactionSynchronizationManager : Bound value [org.springframework.jdbc.datasource.ConnectionHolder@452fee11] for key [org.apache.tomcat.jdbc.pool.DataSource@375faa9d{ConnectionPool[defaultAutoCommit=null; defaultReadOnly=null; defaultTransactionIsolation=-1; defaultCatalog=null; driverClassName=oracle.jdbc.driver.OracleDriver; maxActive=100; maxIdle=100; minIdle=10; initialSize=10; maxWait=30000; testOnBorrow=false; testOnReturn=false; timeBetweenEvictionRunsMillis=5000; numTestsPerEvictionRun=0; minEvictableIdleTimeMillis=60000; testWhileIdle=false; testOnConnect=false; password=********; url=jdbc:oracle:thin:@********; username=********; validationQuery=null; validationQueryTimeout=-1; validatorClassName=null; validationInterval=3000; accessToUnderlyingConnectionAllowed=true; removeAbandoned=false; removeAbandonedTimeout=60; logAbandoned=false; connectionProperties=null; initSQL=null; jdbcInterceptors=null; jmxEnabled=true; fairQueue=true; useEquals=true; abandonWhenPercentageFull=0; maxAge=0; useLock=false; dataSource=null; dataSourceJNDI=null; suspectTimeout=0; alternateUsernameAllowed=false; commitOnReturn=false; rollbackOnReturn=false; useDisposableConnectionFacade=true; logValidationErrors=false; propagateInterruptState=false; ignoreExceptionOnPreLoad=false; useStatementFacade=true; }] to thread [main]

    2019-03-25 14:52:43.129 TRACE 11624 --- [           main] .s.t.s.TransactionSynchronizationManager : Initializing transaction synchronization

    2019-03-25 14:52:43.129 TRACE 11624 --- [           main] o.s.t.i.TransactionInterceptor           : Getting transaction for [org.springframework.batch.core.repository.support.SimpleJobRepository.getLastJobExecution]

    2019-03-25 14:52:43.129 DEBUG 11624 --- [           main] o.s.jdbc.core.JdbcTemplate               : Executing prepared SQL query

    2019-03-25 14:52:43.129 DEBUG 11624 --- [           main] o.s.jdbc.core.JdbcTemplate               : Executing prepared SQL statement [SELECT JOB_INSTANCE_ID, JOB_NAME from BATCH_JOB_INSTANCE where JOB_NAME = ? and JOB_KEY = ?]

    2019-03-25 14:52:43.129 TRACE 11624 --- [           main] .s.t.s.TransactionSynchronizationManager : Retrieved value [org.springframework.jdbc.datasource.ConnectionHolder@452fee11] for key [org.apache.tomcat.jdbc.pool.DataSource@375faa9d{ConnectionPool[defaultAutoCommit=null; defaultReadOnly=null; defaultTransactionIsolation=-1; defaultCatalog=null; driverClassName=oracle.jdbc.driver.OracleDriver; maxActive=100; maxIdle=100; minIdle=10; initialSize=10; maxWait=30000; testOnBorrow=false; testOnReturn=false; timeBetweenEvictionRunsMillis=5000; numTestsPerEvictionRun=0; 

............

    o.s.jdbc.core.JdbcTemplate               : Executing prepared SQL statement [INSERT into BATCH_JOB_INSTANCE(JOB_INSTANCE_ID, JOB_NAME, JOB_KEY, VERSION) values (?, ?, ?, ?)]

    2019-03-25 14:52:43.191 TRACE 11624 --- [           main] .s.t.s.TransactionSynchronizationManager : Retrieved value [org.springframework.jdbc.datasource.ConnectionHolder@d40e102f] for key [org.apache.tomcat.jdbc.pool.DataSource@375faa9d{ConnectionPool[defaultAutoCommit=null; defaultReadOnly=null; defaultTransactionIsolation=-1; defaultCatalog=null; driverClassName=oracle.jdbc.driver.OracleDriver; maxActive=100; maxIdle=100; minIdle=10; initialSize=10; maxWait=30000; testOnBorrow=false; testOnReturn=false; timeBetweenEvictionRunsMillis=5000; numTestsPerEvictionRun=0; minEvictableIdleTimeMillis=60000; testWhileIdle=false; testOnConnect=false; password=********; url=jdbc:oracle:thin:@*********; username=*******; validationQuery=null; validationQueryTimeout=-1; validatorClassName=null; validationInterval=3000; accessToUnderlyingConnectionAllowed=true; removeAbandoned=false; removeAbandonedTimeout=60; logAbandoned=false; connectionProperties=null; initSQL=null; jdbcInterceptors=null; jmxEnabled=true; fairQueue=true; useEquals=true; abandonWhenPercentageFull=0; maxAge=0; useLock=false; dataSource=null; dataSourceJNDI=null; suspectTimeout=0; alternateUsernameAllowed=false; commitOnReturn=false; rollbackOnReturn=false; useDisposableConnectionFacade=true; logValidationErrors=false; propagateInterruptState=false; ignoreExceptionOnPreLoad=false; useStatementFacade=true; }] bound to thread [main]

    2019-03-25 14:52:43.191 TRACE 11624 --- [           main] .s.t.s.TransactionSynchronizationManager : Retrieved value [org.springframework.jdbc.datasource.ConnectionHolder@d40e102f] for key [org.apache.tomcat.jdbc.pool.DataSource@375faa9d{ConnectionPool[defaultAutoCommit=null; defaultReadOnly=null; defaultTransactionIsolation=-1; defaultCatalog=null; driverClassName=oracle.jdbc.driver.OracleDriver; maxActive=100; maxIdle=100; minIdle=10; initialSize=10; maxWait=30000; testOnBorrow=false; testOnReturn=false; timeBetweenEvictionRunsMillis=5000; numTestsPerEvictionRun=0; minEvictableIdleTimeMillis=60000; testWhileIdle=false; testOnConnect=false; password=********; url=jdbc:oracle:thin:@*********; username=*******; validationQuery=null; validationQueryTimeout=-1; validatorClassName=null; validationInterval=3000; accessToUnderlyingConnectionAllowed=true; removeAbandoned=false; removeAbandonedTimeout=60; logAbandoned=false; connectionProperties=null; initSQL=null; jdbcInterceptors=null; jmxEnabled=true; fairQueue=true; useEquals=true; abandonWhenPercentageFull=0; maxAge=0; useLock=false; dataSource=null; dataSourceJNDI=null; suspectTimeout=0; alternateUsernameAllowed=false; commitOnReturn=false; rollbackOnReturn=false; useDisposableConnectionFacade=true; logValidationErrors=false; propagateInterruptState=false; ignoreExceptionOnPreLoad=false; useStatementFacade=true; }] bound to thread [main]

    2019-03-25 14:52:43.191 TRACE 11624 --- [           main] o.s.jdbc.core.StatementCreatorUtils      : Setting SQL statement parameter value: column index 1, parameter value [701], value class [java.lang.Long], SQL type -5

    2019-03-25 14:52:43.191 TRACE 11624 --- [           main] o.s.jdbc.core.StatementCreatorUtils      : Setting SQL statement parameter value: column index 2, parameter value [HierarchyEntrepriseService], value class [java.lang.String], SQL type 12

    2019-03-25 14:52:43.191 TRACE 11624 --- [           main] o.s.jdbc.core.StatementCreatorUtils      : Setting SQL statement parameter value: column index 3, parameter value [cee0f40db33d9d28023aebe0d48e0839], value class [java.lang.String], SQL type 12

    2019-03-25 14:52:43.191 TRACE 11624 --- [           main] o.s.jdbc.core.StatementCreatorUtils      : Setting SQL statement parameter value: column index 4, parameter value [0], value class [java.lang.Integer], SQL type 4

And that trigger a Can't Serialize exception.


Solution

  • With your current configuration, the bean of type JobRepository that you declared in your Spring application context will not be the one picked up by Spring Batch. It is actually the default one (created by @EnableBatchProcessing) that will be used, which uses the SERIALIZABLE isolation level by default.

    As mentioned in the Configuring a JobRepository section of the reference documentation, you need to implement the BatchConfigurer interface and provide a bean of that type in your configuration, or make your configuration class extend DefaultBatchConfigurer and override createJobRepository.