如何在春季批处理中解决InvalidIsolationLevelException?

时间:2019-06-11 11:27:57

标签: spring transactions spring-batch

我正在使用Spring批处理,执行批处理作业时会收到InvalidIsolationLevelException。当我使用MapJobRepositoryFactoryBean时,该工作正常进行,但是我需要在数据库上生成BATCH_ *表。

该应用程序已经安装了事务管理器。

我的春季版本是4.3.18.RELEASE,并且使用Oracle SQL作为数据库。

这是我的批处理配置:

@Configuration
@ComponentScan({"com..."})
public class BatchConfig {
    private static final Logger LOGGER = LoggerFactory.getLogger(BatchConfig.class);

    private static final String REPORTS_STEP_NAME = "reportStep";
    private static final String REPORTS_JOB_NAME = "reportsJob";
    private static final int REPORTS_STEP_CHUNK_SIZE = 20;
    private static final String MAX_END_DATE = "2999-12-31 00:00:00";
    private static final String MAX_END_DATE_FORMAT = "yyyy-MM-dd HH:mm:ss";
    private static final String END_DATE_PARAM = "endDate";
    private static final String REPORT_STATUS_PARAM = "reportSend";
    private static final YesNo NO = new YesNo("N");

    @Autowired
    private ApplicationConfig applicationConfig;

    @Autowired
    private ReportProcessor reportProcessor;

    @Autowired
    private ReportItemWriter reportItemWriter;

    @Bean
    public JobBuilderFactory jobBuilderFactory() throws Exception {
        JobBuilderFactory jobBuilderFactory = new JobBuilderFactory(jobRepository());
        return jobBuilderFactory;
    }

    @Bean
    public StepBuilderFactory stepBuilderFactory() throws Exception {
        StepBuilderFactory stepBuilderFactory = new StepBuilderFactory(jobRepository(), applicationConfig.transactionManager());
        return stepBuilderFactory;
    }

    @Bean
    public PlatformTransactionManager transactionManager() {
        JpaTransactionManager jpaTransactionManager = new JpaTransactionManager();
        jpaTransactionManager.setDataSource(dataSource());
        jpaTransactionManager.setEntityManagerFactory(applicationConfig.entityManagerFactory().getObject());
        return jpaTransactionManager;
    }

    @Bean
    public JobRegistry jobRegistry() {
        return new MapJobRegistry();
    }

    @Bean
    public JobExplorer jobExplorer() throws Exception {
        JobExplorerFactoryBean jobExplorerFactoryBean = new JobExplorerFactoryBean();
        jobExplorerFactoryBean.setDataSource(dataSource());
        jobExplorerFactoryBean.afterPropertiesSet();
        return jobExplorerFactoryBean.getObject();
    }

    @Bean
    public JobOperator jobOperator() throws Exception {
        SimpleJobOperator jobOperator = new SimpleJobOperator();
        jobOperator.setJobExplorer(jobExplorer());
        jobOperator.setJobRegistry(jobRegistry());
        jobOperator.setJobLauncher(jobLauncher());
        jobOperator.setJobRepository(jobRepository());
        return jobOperator;
    }

    @Bean
    public DataSource dataSource() {
        DriverManagerDataSource dataSource = new DriverManagerDataSource();
        dataSource.setDriverClassName("...");
        dataSource.setUrl("...");
        dataSource.setUsername("...");
        dataSource.setPassword("...");
        return dataSource;
    }

    @Bean
    public JobRepository jobRepository() throws Exception {
        JobRepositoryFactoryBean jobRepositoryFactoryBean = new JobRepositoryFactoryBean();
        jobRepositoryFactoryBean.setDataSource(dataSource());
        jobRepositoryFactoryBean.setTransactionManager(transactionManager());
        jobRepositoryFactoryBean.setIsolationLevelForCreate("ISOLATION_SERIALIZABLE");
        return jobRepositoryFactoryBean.getObject();
    }

    @Bean
    public JobLauncher jobLauncher() throws Exception {
        SimpleJobLauncher simpleJobLauncher = new SimpleJobLauncher();
        simpleJobLauncher.setJobRepository(jobRepository());
        return simpleJobLauncher;
    }

    public JpaPagingItemReader<FishingVessel> readFishingVesselRecords() throws ParseException {
        LOGGER.info("Inside JpaPagingItemReader");
        JpaPagingItemReader<FishingVessel> fishingVesselsReader = new JpaPagingItemReader<>();
        fishingVesselsReader.setEntityManagerFactory(applicationConfig.entityManagerFactory().getObject());
        fishingVesselsReader.setQueryString(FishingVessel.FISHING_VESSEL_REPORTS);
        fishingVesselsReader.setTransacted(false);
        fishingVesselsReader.setSaveState(false);
        Map<String, Object> params = createQueryParams();
        fishingVesselsReader.setParameterValues(params);
        fishingVesselsReader.setPageSize(100);
        return fishingVesselsReader;
    }

    private Map<String, Object> createQueryParams() throws ParseException {
        Map<String, Object> params = new HashMap<>();
        DateFormatter dateFormatter = new DateFormatter(MAX_END_DATE_FORMAT);
        Date date = dateFormatter.parse(MAX_END_DATE, Locale.ENGLISH);
        params.put(END_DATE_PARAM, date);
        //params.put(REPORT_STATUS_PARAM, NO);
        return params;
    }

    @Bean
    public Step reportsStep() throws Exception {
        DefaultTransactionAttribute attribute = new DefaultTransactionAttribute();
        attribute.setPropagationBehavior(Propagation.REQUIRED.value());
        attribute.setIsolationLevel(Isolation.SERIALIZABLE.value());
        attribute.setTimeout(30);

        return stepBuilderFactory()
                .get(REPORTS_STEP_NAME)
                .<FishingVessel, FishingVessel>chunk(REPORTS_STEP_CHUNK_SIZE)
                .reader(readFishingVesselRecords())
                .processor(reportProcessor)
                .writer(reportItemWriter)
                .transactionAttribute(attribute)
                .build();
    }

    @Bean
    public Job reportsJob() throws Exception {
        return jobBuilderFactory()
                .get(REPORTS_JOB_NAME)
                .start(reportsStep())
                .build();
    }
}

这是应用程序中已经存在的应用程序配置:

@Configuration
@ComponentScan({
    "com....components",
    "com....services",
    "com....converters",
    "com....controllers",
    "com....fluxFolder",
    "com....processors",
    "com....flux.service",
    "com....flux.gateway"
})
@EnableJpaRepositories("com....repositories")
@EnableTransactionManagement
@EnableJpaAuditing
@EnableWebMvc
@Import({SecurityConfig.class})
@PropertySource("classpath:application.properties")
public class ApplicationConfig {
    @Bean
    public LocalContainerEntityManagerFactoryBean entityManagerFactory() {

        LocalContainerEntityManagerFactoryBean factory = new LocalContainerEntityManagerFactoryBean();
        factory.setPersistenceXmlLocation("classpath:/META-INF/persistence.xml");
        return factory;
    }

    @Bean
    @Primary
    public PlatformTransactionManager transactionManager() {
        JpaTransactionManager transactionManager = new JpaTransactionManager();
        transactionManager.setEntityManagerFactory(entityManagerFactory().getObject());
        return transactionManager;
    }

    @Bean
    public static PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer() {
        return new PropertySourcesPlaceholderConfigurer();
    }

    @Bean
    public ViewResolver viewResolver() {
        UrlBasedViewResolver viewResolver = new UrlBasedViewResolver();
        viewResolver.setViewClass(JsfView.class);
        viewResolver.setSuffix(".xhtml");
        return viewResolver;
    }
}

这是我的控制器,用于初始化批处理作业:

@RestController
public class BatchController {
    private static final String JOB_ID = "JobID";

    private JobExecution jobExecution;

    @Autowired
    private JobOperator jobOperator;

    @Autowired
    private JobLauncher jobLauncher;

    @Autowired
    private Job reportsJob;

    @Autowired
    private JobExplorer jobExplorer;

    @GetMapping("/batch/enable")
    public String batchTest() throws JobParametersInvalidException, JobExecutionAlreadyRunningException, JobRestartException, JobInstanceAlreadyCompleteException {
        JobParameters jobParameters = new JobParametersBuilder()
                .addString(JOB_ID, String.valueOf(System.currentTimeMillis()))
                .toJobParameters();
        JobInstance job;
        jobExecution = new JobExecution(jobLauncher.run(reportsJob, jobParameters));
        return "batch job started";
    }

    @GetMapping("/batch/disable")
    public String disable() {
        String jobName = jobExecution.getJobInstance().getJobName();
        Set<JobExecution> jobExecutions = jobExplorer.findRunningJobExecutions(jobName);
        for (JobExecution je : jobExecutions) {
            try {
                jobOperator.stop(je.getId());
            } catch (NoSuchJobExecutionException e) {

            } catch (JobExecutionNotRunningException e) {

            }
        }
        return jobName + " disabled.";
    }
}

这是我得到的堆栈跟踪:

  

14:01:30,945错误[io.undertow.request](默认任务1)UT005023:   对/.../batch/enable的异常处理请求:org.springframework   .web.util.NestedServletException:请求处理失败;嵌套的   例外是   org.springframework.transaction.InvalidIsolationLevelException:   由于以下原因,DefaultJpaDiale ct不支持自定义隔离级别:   标准JPA中的限制。可以实施具体安排   在自定义JpaDialect变体中。           在org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:982)           在org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:861)           在javax.servlet.http.HttpServlet.service(HttpServlet.java:687)           在org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:846)           在javax.servlet.http.HttpServlet.service(HttpServlet.java:790)           在io.undertow.servlet.handlers.ServletHandler.handleRequest(ServletHandler.java:86)           在io.undertow.servlet.handlers.FilterHandler $ FilterChainImpl.doFilter(FilterHandler.java:130)           在org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:197)           在org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)           在io.undertow.servlet.core.ManagedFilter.doFilter(ManagedFilter.java:60)           在io.undertow.servlet.handlers.FilterHandler $ FilterChainImpl.doFilter(FilterHandler.java:132)           在org.springframework.security.web.FilterChainProxy $ VirtualFilterChain.doFilter(FilterChainProxy.java:317)           在org.springframework.security.web.access.intercept.FilterSecurityInterceptor.invoke(FilterSecurityInterceptor.java:127)           在org.springframework.security.web.access.intercept.FilterSecurityInterceptor.doFilter(FilterSecurityInterceptor.java:91)           在org.springframework.security.web.FilterChainProxy $ VirtualFilterChain.doFilter(FilterChainProxy.java:331)           在org.springframework.security.web.access.ExceptionTranslationFilter.doFilter(ExceptionTranslationFilter.java:114)           在org.springframework.security.web.FilterChainProxy $ VirtualFilterChain.doFilter(FilterChainProxy.java:331)           在org.springframework.security.web.session.SessionManagementFilter.doFilter(SessionManagementFilter.java:137)           在org.springframework.security.web.FilterChainProxy $ VirtualFilterChain.doFilter(FilterChainProxy.java:331)           在org.springframework.security.web.authentication.AnonymousAuthenticationFilter.doFilter(AnonymousAuthenticationFilter.java:111)           在org.springframework.security.web.FilterChainProxy $ VirtualFilterChain.doFilter(FilterChainProxy.java:331)           在org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter.doFilter(SecurityContextHolderAwareRequestFilter.java:170)           在org.springframework.security.web.FilterChainProxy $ VirtualFilterChain.doFilter(FilterChainProxy.java:331)           在org.springframework.security.web.savedrequest.RequestCacheAwareFilter.doFilter(RequestCacheAwareFilter.java:63)           在org.springframework.security.web.FilterChainProxy $ VirtualFilterChain.doFilter(FilterChainProxy.java:331)           在org.springframework.security.web.session.ConcurrentSessionFilter.doFilter(ConcurrentSessionFilter.java:155)           在org.springframework.security.web.FilterChainProxy $ VirtualFilterChain.doFilter(FilterChainProxy.java:331)           在org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter.doFilter(AbstractAuthenticationProcessingFilter.java:200)处           在org.springframework.security.web.FilterChainProxy $ VirtualFilterChain.doFilter(FilterChainProxy.java:331)           在org.springframework.security.web.authentication.logout.LogoutFilter.doFilter(LogoutFilter.java:116)           在org.springframework.security.web.FilterChainProxy $ VirtualFilterChain.doFilter(FilterChainProxy.java:331)           在org.springframework.security.web.header.HeaderWriterFilter.doFilterInternal(HeaderWriterFilter.java:66)           在org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)           在org.springframework.security.web.FilterChainProxy $ VirtualFilterChain.doFilter(FilterChainProxy.java:331)           在org.springframework.security.web.context.SecurityContextPersistenceFilter.doFilter(SecurityContextPersistenceFilter.java:105)           在org.springframework.security.web.FilterChainProxy $ VirtualFilterChain.doFilter(FilterChainProxy.java:331)           在org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter.doFilterInternal(WebAsyncManagerIntegrationFilter.java:56)处           在org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)           在org.springframework.security.web.FilterChainProxy $ VirtualFilterChain.doFilter(FilterChainProxy.java:331)           在org.springframework.security.web.FilterChainProxy.doFilterInternal(FilterChainProxy.java:214)           在org.springframework.security.web.FilterChainProxy.doFilter(FilterChainProxy.java:177)           在org.springframework.web.filter.DelegatingFilterProxy.invokeDelegate(DelegatingFilterProxy.java:347)           在org.springframework.web.filter.DelegatingFilterProxy.doFilter(DelegatingFilterProxy.java:263)           在io.undertow.servlet.core.ManagedFilter.doFilter(ManagedFilter.java:60)           在io.undertow.servlet.handlers.FilterHandler $ FilterChainImpl.doFilter(FilterHandler.java:132)           在io.undertow.servlet.handlers.FilterHandler.handleRequest(FilterHandler.java:85)           在io.undertow.servlet.handlers.security.ServletSecurityRoleHandler.handleRequest(ServletSecurityRoleHandler.java:62)           在io.undertow.servlet.handlers.ServletDispatchingHandler.handleRequest(ServletDispatchingHandler.java:36)           在org.wildfly.extension.undertow.security.SecurityContextAssociationHandler.handleRequest(SecurityContextAssociationHandler.java:78)处           在io.undertow.server.handlers.PredicateHandler.handleRequest(PredicateHandler.java:43)           在io.undertow.servlet.handlers.security.SSLInformationAssociationHandler.handleRequest(SSLInformationAssociationHandler.java:131)           在io.undertow.servlet.handlers.security.ServletAuthenticationCallHandler.handleRequest(ServletAuthenticationCallHandler.java:57)           在io.undertow.server.handlers.PredicateHandler.handleRequest(PredicateHandler.java:43)           在io.undertow.security.handlers.AbstractConfidentialityHandler.handleRequest(AbstractConfidentialityHandler.java:46)           在io.undertow.servlet.handlers.security.ServletConfidentialityConstraintHandler.handleRequest(ServletConfidentialityConstraintHandler.java:64)           在io.undertow.security.handlers.AuthenticationMechanismsHandler.handleRequest(AuthenticationMechanismsHandler.java:58)           在io.undertow.servlet.handlers.security.CachedAuthenticatedSessionHandler.handleRequest(CachedAuthenticatedSessionHandler.java:72)           在io.undertow.security.handlers.NotificationReceiverHandler.handleRequest(NotificationReceiverHandler.java:50)           在io.undertow.security.handlers.SecurityInitialHandler.handleRequest(SecurityInitialHandler.java:76)           在io.undertow.server.handlers.PredicateHandler.handleRequest(PredicateHandler.java:43)           在org.wildfly.extension.undertow.security.jacc.JACCContextIdHandler.handleRequest(JACCContextIdHandler.java:61)处           在io.undertow.server.handlers.PredicateHandler.handleRequest(PredicateHandler.java:43)           在io.undertow.server.handlers.PredicateHandler.handleRequest(PredicateHandler.java:43)           在io.undertow.servlet.handlers.ServletInitialHandler.handleFirstRequest(ServletInitialHandler.java:282)           在io.undertow.servlet.handlers.ServletInitialHandler.dispatchRequest(ServletInitialHandler.java:261)           在io.undertow.servlet.handlers.ServletInitialHandler.access $ 000(ServletInitialHandler.java:80)           在io.undertow.servlet.handlers.ServletInitialHandler $ 1.handleRequest(ServletInitialHandler.java:172)           在io.undertow.server.Connectors.executeRootHandler(Connectors.java:199)           在io.undertow.server.HttpServerExchange $ 1.run(HttpServerExchange.java:774)           在java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)           在java.util.concurrent.ThreadPoolExecutor $ Worker.run(ThreadPoolExecutor.java:617)           在java.lang.Thread.run(Thread.java:748)导致原因:org.springframework.transaction.InvalidIsolationLevelException:   由于以下原因,DefaultJpaDialect不支持自定义隔离级别:   JPA标准中的限制。可以实施具体安排   在自定义JpaDialect变体中。           在org.springframework.orm.jpa.DefaultJpaDialect.beginTransaction(DefaultJpaDialect.java:63)           在org.springframework.orm.jpa.JpaTransactionManager.doBegin(JpaTransactionManager.java:380)           在org.springframework.transaction.support.AbstractPlatformTransactionManager.getTransaction(AbstractPlatformTransactionManager.java:377)           在org.springframework.transaction.interceptor.TransactionAspectSupport.createTransactionIfNecessary(TransactionAspectSupport.java:461)           在org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:277)           在org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:96)           在org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)           在org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:213)           在com.sun.proxy。$ Proxy419.getLastJobExecution(未知来源)           在org.springframework.batch.core.launch.support.SimpleJobLauncher.run(SimpleJobLauncher.java:98)           在com .... controllers.BatchController.batchTest(BatchController.java:42)           在sun.reflect.NativeMethodAccessorImpl.invoke0(本机方法)处           在sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)           在sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)           在java.lang.reflect.Method.invoke(Method.java:498)           在org.springframework.web.method.support.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:205)           在org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:133)           在org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:97)           在org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:827)           在org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:738)           在org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:85)           在org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:967)           在org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:901)           在org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:970)           ...还有71

1 个答案:

答案 0 :(得分:0)

您可以将JobRepositoryFactoryBean中的隔离级别更改为默认值:

 @Bean
public JobRepository jobRepository() throws Exception {
    JobRepositoryFactoryBean jobRepositoryFactoryBean = new JobRepositoryFactoryBean();
    jobRepositoryFactoryBean.setDataSource(dataSource());
    jobRepositoryFactoryBean.setTransactionManager(transactionManager());
    jobRepositoryFactoryBean.setIsolationLevelForCreate("ISOLATION_DEFAULT");
    return jobRepositoryFactoryBean.getObject();
}