springbatch自定义JobRepository

	在默认配置下,当我们在oracle数据库上执行springbatch时会报"ORA-08177: 无法连续访问此事务处理"的错误,在网上查找资料,显示是因为springbatch的JobRepository默认的事务级别为“ISOLATION_SERIALIZABLE”,需要调整为“ISOLATION_READ_COMMITTED”才可以(具体为啥需要调整数据库事务级别还不是很清楚,需要找时间再研究清楚)
	那么下一步我们自定义自己的JobRepository,通过查看官方文档([spring.io](https://docs.spring.io/spring-batch/docs/current/reference/html/job.html#configuringJobRepository))得知,自定义JobRepository需要实现一个BatchConfigurer接口的类。那么只需要实现这个类就行了。经过测试发现是不行的,显然springbatch还没有智能到这种程度,那么我们需要分析下springbatch的初始化过程。
	不说查找的过程了,我直接写一下自己分析之后的看法吧。
	首先springboot会去初始化org.springframework.boot.autoconfigure.batch.BatchConfigurerConfiguration的配置文件:
	@ConditionalOnClass(PlatformTransactionManager.class)
	@ConditionalOnBean(DataSource.class)
	@ConditionalOnMissingBean(BatchConfigurer.class)
	@Configuration(proxyBeanMethods = false)
class BatchConfigurerConfiguration {

	@Configuration(proxyBeanMethods = false)
	@ConditionalOnMissingBean(name = "entityManagerFactory")
	static class JdbcBatchConfiguration {

		@Bean
		BasicBatchConfigurer batchConfigurer(BatchProperties properties, DataSource dataSource,
				@BatchDataSource ObjectProvider<DataSource> batchDataSource,
				ObjectProvider<TransactionManagerCustomizers> transactionManagerCustomizers) {
			return new BasicBatchConfigurer(properties, batchDataSource.getIfAvailable(() -> dataSource),
					transactionManagerCustomizers.getIfAvailable());
		}

	}

以上代码执行成功之后就会初始化一个BasicBatchConfigurer的bean,继续看里面的代码,在BasicBatchConfigurer里有个加了initialize()方法:

	@PostConstruct
	public void initialize() {
		try {
			this.transactionManager = buildTransactionManager();
			this.jobRepository = createJobRepository();
			this.jobLauncher = createJobLauncher();
			this.jobExplorer = createJobExplorer();
		}
		catch (Exception ex) {
			throw new IllegalStateException("Unable to initialize Spring Batch", ex);
		}
	}

注意该方面有一个@PostConstruct注解,表示bean属性注入之后就会调用该方法。我们再看一下createJobRepository()方法:

protected JobRepository createJobRepository() throws Exception {
		JobRepositoryFactoryBean factory = new JobRepositoryFactoryBean();
		PropertyMapper map = PropertyMapper.get();
		map.from(this.dataSource).to(factory::setDataSource);
		map.from(this::determineIsolationLevel).whenNonNull().to(factory::setIsolationLevelForCreate);
		map.from(this.properties::getTablePrefix).whenHasText().to(factory::setTablePrefix);
		map.from(this::getTransactionManager).to(factory::setTransactionManager);
		factory.afterPropertiesSet();
		return factory.getObject();
	}

可以看到,当代码执行到此时,BasicBatchConfigurer这个bean的jobRepository属性已经初始化完成。下面我们再分析下这个jobRepository是如何作为一个bean注入到spring容器的。
这里我们就要看一下@interface EnableBatchProcessing这个注解:

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(BatchConfigurationSelector.class)
public @interface EnableBatchProcessing {

继续看BatchConfigurationSelector的代码:

	@Override
	public String[] selectImports(AnnotationMetadata importingClassMetadata) {
		Class<?> annotationType = EnableBatchProcessing.class;
		AnnotationAttributes attributes = AnnotationAttributes.fromMap(importingClassMetadata.getAnnotationAttributes(
				annotationType.getName(), false));
		Assert.notNull(attributes, String.format("@%s is not present on importing class '%s' as expected",
				annotationType.getSimpleName(), importingClassMetadata.getClassName()));

		String[] imports;
		if (attributes.containsKey("modular") && attributes.getBoolean("modular")) {
			imports = new String[] { ModularBatchConfiguration.class.getName() };
		}
		else {
			imports = new String[] { SimpleBatchConfiguration.class.getName() };
		}

		return imports;
	}

有以上代码可以看出,当我们在代码中加了@EnableBatchProcessing()注解时,spring容器就会自动去加载SimpleBatchConfiguration,在该类中又有一个initialize方法:

protected void initialize() throws Exception {
		if (initialized) {
			return;
		}
		BatchConfigurer configurer = getConfigurer(context.getBeansOfType(BatchConfigurer.class).values());
		jobRepository.set(configurer.getJobRepository());
		jobLauncher.set(configurer.getJobLauncher());
		transactionManager.set(configurer.getTransactionManager());
		jobRegistry.set(new MapJobRegistry());
		jobExplorer.set(configurer.getJobExplorer());
		initialized = true;
	}

再然后,spring容器在执行SimpleBatchConfiguration的jobRepository()时彻底的将该对象封装为一个bean放入到容器中。
由此,我们得出一个简单结论,一旦我们再代码中加入了@EnableBatchProcessing(),springboot就会根据BasicBatchConfigurer来初始化一个jobRepository。我们只有这么几个机会来自定义jobRepository:
(1)BatchConfigurerConfiguration有一个注解@ConditionalOnMissingBean(BatchConfigurer.class),意思就是说只有当BatchConfigurer.class的bean不存在时才会执行这个类的方法(描述不够准确),所以我们可以定义一个BatchConfigurer的bean来阻止系统默认的初始化。
(2)在BasicBatchConfigurer的createJobRepository()方法中是通过系统参数的map来定义JobRepository:

 protected JobRepository createJobRepository() throws Exception {
	JobRepositoryFactoryBean factory = new JobRepositoryFactoryBean();
	PropertyMapper map = PropertyMapper.get();
	map.from(this.dataSource).to(factory::setDataSource);
	map.from(this::determineIsolationLevel).whenNonNull().to(factory::setIsolationLevelForCreate);
	map.from(this.properties::getTablePrefix).whenHasText().to(factory::setTablePrefix);
	map.from(this::getTransactionManager).to(factory::setTransactionManager);
	factory.afterPropertiesSet();
	return factory.getObject();
}

因此,可以尝试改变map中的值来改变JobRepository的初始化(该方面目前我还没研究明白)。

还有一处一开始我也以为可以,后来发现不行,那就是在SimpleBatchConfiguration的initialize()方法中有一行这样的代码:
BatchConfigurer configurer = getConfigurer(context.getBeansOfType(BatchConfigurer.class).values());
一开始我以为只需要定义一个级别高的BatchConfigurer.class的bean也可以实现个性化定义JobRepository的方面,但是后来继续跟代码发现不太行。

最后给一下自己的解决方案:
第一步、复制一个BasicBatchConfigurer重命名为CustomerBatchConfig,然后根据自己的需要进行调整,比如我修改了JobRepository的事务级别:

protected JobRepository createJobRepository() throws Exception {
        JobRepositoryFactoryBean factory = new JobRepositoryFactoryBean();
        PropertyMapper map = PropertyMapper.get();
        map.from(this.dataSource).to(factory::setDataSource);
        //map.from(this::determineIsolationLevel).whenNonNull().to(factory::setIsolationLevelForCreate);
        factory.setIsolationLevelForCreate("ISOLATION_READ_COMMITTED");
        map.from(this.properties::getTablePrefix).whenHasText().to(factory::setTablePrefix);
        map.from(this::getTransactionManager).to(factory::setTransactionManager);
        factory.afterPropertiesSet();
        return factory.getObject();
    }

第二步、定义一个config:
@Configuration
public class CustomerConfigurerConfiguration {

@Bean
BatchConfigurer batchConfigurer(BatchProperties properties, DataSource dataSource,
                                @BatchDataSource ObjectProvider<DataSource> batchDataSource,
                                ObjectProvider<TransactionManagerCustomizers> transactionManagerCustomizers) {
    return new CustomerBatchConfig(properties, batchDataSource.getIfAvailable(() -> dataSource),
            transactionManagerCustomizers.getIfAvailable());
}

}

其实这个代码大家看着应该也很眼熟,其实也是copy过来改的。

总而言之,对我来说自定义一个JobRepository还真是挺难的,改天仔细研究下方案二看看能不能方便一些

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值