这节介绍如何使用spring batch。
Spring batch 默认为内存方式(HSQLDB),但是产品功能上需要进行监控job状态,以及异常情况。所以采用了存储到数据库(Mysql),
那么就需要为这部分建表,那么我们需要配置JobRepository去使用MySQL。建表脚本在 spring-batch-core jar包下的org.springframework.batch.core中。
一、搭建环境
1. 引入jar包,采用Maven的方式引入。具体需要的包可以自己慢慢试。
<dependency>
<groupId>org.springframework.batchgroupId>
<artifactId>spring-batch-coreartifactId>
<version>${spring.batch.version}version>
dependency>
<dependency>
<groupId>org.springframework.batchgroupId>
<artifactId>spring-batch-admin-managerartifactId>
<version>1.3.1.RELEASEversion>
dependency>
<dependency>
<groupId>org.springframework.batchgroupId>
<artifactId>spring-batch-infrastructureartifactId>
<version>${spring.batch.version}version>
dependency>
<dependency>
<groupId>org.springframework.batchgroupId>
<artifactId>spring-batch-integrationartifactId>
<version>${spring.batch.version}version>
dependency>
2. 配置XML,如果集成spring boot则按官方文档就可以。
a. 配置jobRepository
<batch:job-repository id="jobRepository" transaction-manager="main_txManager"
isolation-level-for-create="REPEATABLE_READ" table-prefix="BATCH_" max-varchar-length="1000" />
b. 配置jobLauncher
id="jobLauncher"
class="org.springframework.batch.core.launch.support.SimpleJobLauncher">
<property name="jobRepository" ref="jobRepository"/>
c. 配置job参数传递bean jobParameterBulider
id="jobParameterBulider" class="org.springframework.batch.core.JobParametersBuilder" />
d. 配置job异步多线程
id="taskExecutor" class="org.springframework.core.task.SimpleAsyncTaskExecutor" />
二、 开始简单的job编写
个人喜欢先写配置,再写业务逻辑上的东西。我们以提货提醒为例子,当然这个是去除了很多东西的例子,将就着看。
<batch:job id="pickUpJob" restartable="true">
<batch:step id="pickUpmasterStep" >
<partition step="pickUpSlave" partitioner="pickUpPartitioner" >
<handler grid-size="10" task-executor="taskExecutor" />
partition>
batch:step>
batch:job>
<batch:step id="pickUpSlave" >
<batch:tasklet transaction-manager="main_txManager" >
<batch:chunk reader="pickUpReader" writer="pickUpWriter"
processor="pickUpProcessor" skip-limit="20" commit-interval="100" >
<batch:skippable-exception-classes>
<batch:include class="java.lang.Exception"/>
batch:skippable-exception-classes>
batch:chunk>
batch:tasklet>
<batch:listeners>
<batch:listener ref="pickUpListener" before-step-method="beforeStep" />
batch:listeners>
batch:step>
<bean id="pickUpListener" class="com.cwenao.cc.scheduler.batch.listener.PickUpListener" />
<bean id="pickUpReader" class="org.mybatis.spring.batch.MyBatisPagingItemReader" scope="step" >
<property name="sqlSessionFactory" ref="sqlSessionFactory" />
<property name="queryId" value="com.cwenao.cc.basic.dao.OrderDao.selectForBatchNotPick" />
<property name="pageSize" value="100"/>
<property name="parameterValues">
<map>
<entry key="sTime" value="#{stepExecutionContext[sTime]}" />
<entry key="eTime" value="#{stepExecutionContext[eTime]}" />
map>
property>
bean>
<bean id="pickUpWriter" class="org.mybatis.spring.batch.MyBatisBatchItemWriter" scope="step">
<property name="sqlSessionFactory" ref="sqlSessionFactory"/>
<property name="statementId" value="com.cwenao.cc.basic.dao.NoticeInfoDao.insertSelective"/>
bean>
以上为配置文件,接下来一个个需要实现的类:
job启动
public class BatchRedExpireOpenIdJob {
@Autowired
private JobLauncher jobLauncher;
@Autowired
private Job redExpireOpenIdJob;
@Autowired
JobParametersBuilder jobParameterBulider;
public boolean doExecuteTask(String jobId)
{
if(null == jobParameterBulider)
{
jobParameterBulider = SpringUtil.getBean("jobParameterBulider", JobParametersBuilder.class);
}
if(null == redExpireOpenIdJob)
{
redExpireOpenIdJob = SpringUtil.getBean("redExpireOpenIdJob",Job.class);
}
if(null == jobLauncher)
{
jobLauncher = SpringUtil.getBean("jobLauncher",JobLauncher.class);
}
jobParameterBulider.addDate("date", new Date());
jobParameterBulider.addString("jobId", jobId);
try {
JobExecution execution = jobLauncher.run(redExpireOpenIdJob,jobParameterBulider.toJobParameters());
} catch (JobExecutionAlreadyRunningException e) {
e.printStackTrace();
} catch (JobRestartException e) {
e.printStackTrace();
} catch (JobInstanceAlreadyCompleteException e) {
e.printStackTrace();
} catch (JobParametersInvalidException e) {
e.printStackTrace();
}
return true;
}
}
pickUpPartitioner类
@Scope("step")
@Component("pickUpPartitioner")
public class pickUpPartitioner implements Partitioner {
@Value("#{jobParameters['jobId']}")
private String jobId;
@Resource
TaskSchedulerDao taskSchedulerDao;
@Override
public Map partition(int gridSize) {
Map result = new HashMap();
String preOrderTime= "";
TaskScheduler taskScheduler = taskSchedulerDao.selectByPrimaryKey(jobId);
int days=0;
int hours=0;
if(null !=taskScheduler)
{
String hoursArry[] = taskScheduler.getTaskName().split("#");
if(null != hoursArry && hoursArry.length>1)
{
hours = Integer.parseInt(hoursArry[0]);
}
}
if(hours != 0)
{
days = hours/24;
}
String pre2OrderTime="";
if(days==0){
preOrderTime= DateUtil.dateToStr(DateUtil.getPreDay(), "yyyy-MM-dd");
}
else
preOrderTime= DateUtil.dateToStr(DateUtil.getPreDay(-days), "yyyy-MM-dd");
String sTime = preOrderTime+" 00:00:00";
String eTime = preOrderTime+" 23:59:59";
ExecutionContext value = new ExecutionContext();
value.putString("sTime", sTime);
value.putString("eTime", eTime);
result.put("partition", value);
return result;
}
public String getJobId() {
return jobId;
}
public void setJobId(String jobId) {
this.jobId = jobId;
}
}
读取数据库与写入数据库就不再写,就是一般的sql语句。
处理过程pickUpProcessor
pickUpProcessor类
@Scope("step")
@Component("pickUpProcessor")
public class PickUpProcessor implements ItemProcessor<OrderVo,NoticeInfo> {
private static Map pickPointMap=new HashMap<>();
@Value("#{jobParameters['jobId']}")
private String jobId;
@Override
public NoticeInfo process( OrderVo order) throws Exception {
if(null == order){
return null;
}
Integer amOrPm = DateUtil.getAmPm();
NoticeInfo noticeInfo = new NoticeInfo();
noticeInfo.setId(UUIDUtil.generateUUID());
noticeInfo.setStatus(APIConstant.status_enable);
return noticeInfo;
}
public String getJobId() {
return jobId;
}
public void setJobId(String jobId) {
this.jobId = jobId;
}
}
监听类与一般类无二,主要看业务上的使用。
到目前为止,一个简单的job就算走通了。
附录
《spring batch in action》,这本书是我的主要参考文档,看原文的,翻译什么的不是很好。
如有疑问请加公众号(K171),如果觉得对您有帮助请 github start
