前言

  说得多不如show code。上一章简单介绍了一下Spring Batch。本章将从头到尾搭建一套基于Spring Batch(2.1.9)、Spring(3.0.5)、mybatis(3.4.5)、mysql、gradle的批处理简单应用来处理文件中大量交易数据的写入。

  那么这里简单定义以下交易文件的格式,一个txnId交易Id,一个amt交易金额。一天比如有100w交易数据过来要落表。文件大概长这样,只是简单定义以下,实际开发肯定不会那么少。

  因工作需求没有使用最新版本的Spring Batch,所以本章是基于XML config的例子。最新版本支持用Java Config配置Spring Batch Job、Job Scope等。有兴趣的同学可以自行研究一下。本人技术有限,本章讲的如有错误希望请指正。

2.1 项目依赖

  首先我们要引入Spring Batch的依赖,这里的版本是2.1.9

springbatch = ["org.springframework.batch:spring-batch-core:2.1.9.RELEASE",
"org.springframework.batch:spring-batch-infrastructure:2.1.9.RELEASE"]

  批量处理的过程中,我们都需要数据持久化。这里我用的数据库是mysql,ORM框架是mybatis。所以还要添加mysql-connect和mybatis的依赖

mybatis = "org.mybatis:mybatis:3.4.5"
mysqlconnect = "mysql:mysql-connector-java:5.1.25"
dbcp = "commons-dbcp:commons-dbcp:1.4"

  事务和数据库的配置就不用说了,必须的。

<!-- transaction config -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean> <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
<property name="driverClassName" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost/m_test_db"/>
<property name="username" value="root"/>
<property name="password" value="root"/>
</bean>

2.2 配置Job

  上一章节说过,其实文件批处理的场景,抽象的处理三大步骤分为,读,处理,写。那么我们就依照这张图来开始

  上图如果看不懂的请看上一章节来理解。那么我们先建立一个Spring Batch任务的xml文件,然后定义Job

<!--data-source,transaction-manager 数据源以及事务管理器-->
<batch:job-repository id="jobRepository"
data-source="dataSource" transaction-manager="transactionManager"
isolation-level-for-create="SERIALIZABLE"
table-prefix="DPL_" max-varchar-length="1000"/>
<batch:job id="investmentMatchFileJob"
job-repository="jobRepository">
<batch:step id="investmentMatchFileToDb">
<batch:tasklet>
<batch:chunk reader="txnListFileReader" writer="txnListResultWriter"
commit-interval="300"/>
</batch:tasklet>
</batch:step>
</batch:job>

  结合上面的图看,是不是找到点感觉了?一个Job可以有多个Step组合,每一个Step由开发者自己编写,可一把一个大Step分成多个小Step,完全看开发者意愿。每一个Step对应一个ItemReader、ItemProcessor和ItemWriter。所有的批处理框架都可以抽象成最简单的过程,读取数据,处理数据,写数据。所以Spring Batch提供了3个接口,ItemReader、ItemProcessor和ItemWriter。JobRepository则是记录Job、Step和发起Job的执行信息等。

  xml配置Job必须依赖的有三项,名称,JobRepository和Step列表。还有一个没介绍就是commit-interval属性,这就是控制读了多少行进行一次写。总不可能读一行写一行对吧?这里配置多少,那么Writer的入参list的size就是多少。

2.2.1 JobRepository

  JobRepository是记录Job、Step和发起Job的执行信息,SpringBatch一共会让你导入9张表,具体哪9张表请导入依赖然后查看schema-mysql.sql文件。

  这里要说明的一点是table-prefix属性,默认是以BATCH_开头的,你可以改变前缀,当然你的sql脚本的表名前缀也要改动。注意,这里只能改前缀,不可以改表的全名。表的列可以增加,比如说你的公司建表必须要有id,created_at,xxxx等字段的话,可以增加列,没有问题。但是原有列的名称不可以修改。脚本会在3张以SEQ结尾的表插入0,必须要先插入。

2.3 Step

2.3.1 Reader

  上面配置的reader是以下这个bean,value="file:#{jobParameters['txnListFile']}"。这里用到SPEL表达式,传入文件路径参数。FlatFileItemReader只能处理一个文件,实际使用中不可能只处理一个文件,所以你也可以导入下面那个叫MultiResourceItemReader类,通过给MultiResourceItemReader设置Resource数组可以实现一个Job读取一个目录下多个文件。但是这里注意,JobRepository不会记录每个文件的处理情况。

<bean id="txnListFileReader"
class="org.springframework.batch.item.file.FlatFileItemReader"
scope="step">
<!--输入文件-->
<property name="resource" value="file:#{jobParameters['txnListFile']}"/>
<!--将每行映射为一个对象-->
<property name="lineMapper">
<bean class="org.springframework.batch.item.file.mapping.DefaultLineMapper">
<!--从划分的字段中构建一个对象-->
<property name="fieldSetMapper" ref="InvestMatchItemMapper"/>
<!--根据某种分隔符来分-->
<property name="lineTokenizer" ref="TxnListItemMapperFileLineTokenizer"/>
</bean>
</property>
<!--跳过开头的的一些行-->
<property name="linesToSkip" value="1"/>
<property name="encoding" value="UTF-8"/>
</bean>
<bean id="InvestMatchItemMapper" class="me.grimmjx.sync.TxnListItemMapper"/>
<bean id="TxnListItemMapperFileLineTokenizer"
class="org.springframework.batch.item.file.transform.DelimitedLineTokenizer">
<property name="delimiter" value="|"/>
<property name="names">
<list>
<value>txnId</value>
<value>amt</value>
</list>
</property>
</bean> <!--以下的内容是对一个目录下多个文件进行批处理的样例-->
<bean id="txnListFileReader"
class="org.springframework.batch.item.file.MultiResourceItemReader"
scope="step">
<property name="resources" value="file:#{jobParameters['txnListFile']}/*.txt"/>
<property name="delegate">
<bean class="org.springframework.batch.item.file.FlatFileItemReader"
scope="step">
<property name="lineMapper">
<bean
class="org.springframework.batch.item.file.mapping.DefaultLineMapper">
<property name="fieldSetMapper" ref="InvestMatchItemMapper"/>
<property name="lineTokenizer"
ref="TxnListItemMapperFileLineTokenizer"/>
</bean>
</property>
<property name="linesToSkip" value="1"/>
<property name="encoding" value="UTF-8"/>
</bean>
</property>
</bean>

  以下图来理解比较方便

  从xml配置来看,delimiter控制如何分割,names就是文件每一列的名字。在这么多配置里,我们只需要写一个Java类。这里就是从一行数据,转换成一个对象。

/**
* @author GrimMjx
* 交易记录匹配器类。
*/
public class TxnListItemMapper implements FieldSetMapper<TxnList>{ @Override
public TxnList mapFieldSet(FieldSet fieldSet) throws BindException {
TxnList txnList = new TxnList();
txnList.setTxnId(fieldSet.readString("txnId"));
txnList.setAmt(fieldSet.readBigDecimal("amt")); return txnList;
}
}

2.3.2 Writer

  writer的bean为

<bean id="txnListResultWriter" class="me.grimmjx.sync.TxnListResultWriter" scope="step"/>

  writer执行的是写入操作,我们要实现ItemWriter<T>接口,以下为这个类的Java代码。这里的操作很简单,将构建好的对象集合直接写入库。注意了,外面没有幂等的话,最好这里先判断库里有没有,不要无脑写入。

/**
* @author GrimMjx
* 交易数据写入类。
*/
public class TxnListResultWriter implements ItemWriter<TxnList> {
@Autowired
private TxnListMapper txnListMapper; @Override
public void write(List<? extends TxnList> items) throws Exception {
List<TxnList> txnLists = Lists.newArrayList();
for (TxnList item : items) {
txnLists.add(item);
}
txnListMapper.insertBatch(txnLists);
}
}

2.4 启动Job

  这里先定义一个bean,与之前的Job相关联。

<bean id="DefaultFileProcessor" class="me.grimmjx.processor.DefaultFileProcessor">
<property name="job" ref="investmentMatchFileJob"/>
<property name="jobLauncher" ref="jobLauncher"/>
</bean>

  以下为这个processor的Java代码

/**
* 默认文件处理器类。
*
* @author GrimMjx
*/
public class DefaultFileProcessor {
/**
* 批次job
*/
protected Job job; /**
* 任务启动器
*/
protected JobLauncher jobLauncher; public void process() {
String baseDir = "/Users/miaojiaxing/test/2019.01.31.txt"; JobParametersBuilder builder = new JobParametersBuilder();
builder.addString("txnListFile", baseDir);
// 携带参数
// builder.addString("packageCode", "12345");
builder.addString("dateTime", System.currentTimeMillis() + "");
JobParameters jobParas = builder.toJobParameters(); try {
jobLauncher.run(job, jobParas);
} catch (Exception e) {
throw new RuntimeException("Run springBatchJob meet error", e);
}
} public void setJob(Job job) {
this.job = job;
} public void setJobLauncher(JobLauncher jobLauncher) {
this.jobLauncher = jobLauncher;
} }

  最后我们试试

/**
* @author GrimMjx
* <p>
* 测试类。
*/
public class MainTest { public static void main(String[] args) {
ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
DefaultFileProcessor bean = ctx.getBean("DefaultFileProcessor", DefaultFileProcessor.class);
bean.process();
DefaultFileProcessor rereadProcessor = ctx.getBean("rereadProcessor", DefaultFileProcessor.class);
rereadProcessor.process();
}
}

  没有问题。

  下一章节将结合校验清洗、异常弹性处理、并行配置附上代码。

陪你解读Spring Batch(二)带你入手Spring Batch的更多相关文章

  1. Spring(二十):Spring AOP(四):基于配置文件的方式来配置 AOP

    基于配置文件的方式来配置 AOP 前边三个章节<Spring(十七):Spring AOP(一):简介>.<Spring(十八):Spring AOP(二):通知(前置.后置.返回. ...

  2. Spring(二十一):Spring JdbcTemplate、NamedParameterJdbcTemplate具名参数

    JdbcTemplate主要提供以下五类方法: execute方法:可以用于执行任何SQL语句,一般用于执行DDL语句: update方法及batchUpdate方法:update方法用于执行新增.修 ...

  3. spring 【二】学习之spring EL

    spring EL-spring 表达式语言,支持在xml和注解的形式,类似于JSP的el表达式的形式. 其主要使用@Value注解的结构形式 其主要功能 [1].注入普通字符串 [2].注入操作系统 ...

  4. Spring(二十三):Spring自动注入的实现方式

    注解注入顾名思义就是通过注解来实现注入,Spring和注入相关的常见注解包含:Autowrired/Resource/Qualifier/Service/Controller/Repository/C ...

  5. spring batch(二):核心部分(1):配置Spring batch

    spring batch(二):核心部分(1):配置Spring batch 博客分类: Spring 经验 java   chapter 3.Batch configuration 1.spring ...

  6. Spring学习总结(五)——Spring整合MyBatis(Maven+MySQL)二

    接着上一篇博客<Spring整合MyBatis(Maven+MySQL)一>继续. Spring的开放性和扩张性在J2EE应用领域得到了充分的证明,与其他优秀框架无缝的集成是Spring最 ...

  7. spring boot(二):启动原理解析

    我们开发任何一个Spring Boot项目,都会用到如下的启动类 @SpringBootApplication public class Application { public static voi ...

  8. Spring系列(二) Bean装配

    创建应用对象之间协作关系的行为称为装配(wiring), 这也是DI的本质. Spring中装配Bean的方式 Spring提供了三种装配Bean的方式. 隐式的Bean发现机制和自动装配 Java ...

  9. [Spring Batch 系列] 第一节 初识 Spring Batch

    距离开始使用 Spring Batch 有一段时间了,一直没有时间整理,现在项目即将完结,整理下这段时间学习和使用经历. 官网地址:http://projects.spring.io/spring-b ...

随机推荐

  1. jasperReport Studio java报表设计(详细)

    一.环境搭建 在spring-mvc.xml加入 <!-- jasperReports--><import resource="classpath*:spring-mvc- ...

  2. vue组件的生命周期

    先来张组件生命周期的示意图: 文档里是这样描述的:你不需要立马弄明白所有的东西,不过以后它会有帮助.传送门. Vue2.0的生命周期钩子一共有10个,同样结合官方文档作出了下表 生命周期钩子 详细 b ...

  3. 1、原生javascript方法小汇

    Js 对象 使用new 关键字来创建对象,举例如下, var a = new String();如构造函数无参数,则不必加括号, JS内部对象数组(Array)对象创建数组var myarray = ...

  4. MySQL SHOW TABLE 输出的每列详细介绍

    Name: 表名 Engine: 表的存储引擎(旧版本中,该值为Type) Row_format: 行的格式.对于MyISAM表,可选的值为Dynamic.Fixed或者Copressed. Dyna ...

  5. Spring中的循环依赖

    循环依赖 在使用Spring时,如果主要采用基于构造器的依赖注入方式,则可能会遇到循环依赖的情况,简而言之就是Bean A的构造器依赖于Bean B,Bean B的构造器又依赖于Bean A.在这种情 ...

  6. Asp.Net Core 2.0 项目实战(6)Redis配置、封装帮助类RedisHelper及使用实例

    本文目录 1. 摘要 2. Redis配置 3. RedisHelper 4.使用实例 5. 总结 1.  摘要 由于內存存取速度远高于磁盘读取的特性,为了程序效率提高性能,通常会把常用的不常变动的数 ...

  7. 深入出不来nodejs源码-编译启动(1)

    整整弄了两天,踩了无数的坑,各种奇怪的error,最后终于编译成功了. 网上的教程基本上都过时了,或者是版本不对,都会报一些奇怪的错误,这里总结一下目前可行的流程. node版本:v10.1.0. 首 ...

  8. jquery遍历table为每一个单元格取值及赋值

    表格代码 <tr> <td> <input type="text" style="border: none; text-align: cen ...

  9. 你不知道的JavaScript--Item18 JScript的Bug与内存管理

    1.JScript的Bug IE的ECMAScript实现JScript严重混淆了命名函数表达式,搞得现很多人都出来反对命名函数表达式,而且即便是现在还一直在用的一版(IE8中使用的5.8版)仍然存在 ...

  10. Latex数学公式中的空格表示方法

    两个quad空格 a \qquad b 两个m的宽度 quad空格 a \quad b 一个m的宽度 大空格 a\ b 1/3m宽度 中等空格 a\;b 2/7m宽度 小空格 a\,b 1/6m宽度 ...