tags: springbatch
上一篇文章《决战数据库-spring batch(4)数据库到数据库》使用Spring Batch
内置的读、写组件,对数据库间的数据进行同步。相对来说,这个数据读取和数据写入是基于jdbc
进行读写的(数据对象的映射需要我们自己处理,如UserRowMapper
),我们现在开发一般都使用上层一点的ORM
框架,如Hibernate
,MyBatis
,BeetlSQL
。对于Hibernate
,Spring Batch
有默认的HibernateCursorItemReader
和HibernateItemWriter
,也可以实现自行使用MyBatis
和BeetlSQL
。个人感觉,从易用性和学习曲线上看,BeetlSQL
会更容易上手,社区也挺活跃,因此,本文介绍一下使用Spring Batch
结合BeetlSQL
进行数据库到数据库的数据同步。
按官方文档,BeetlSQL
是一个全功能DAO
工具, 同时具有Hibernate
优点 & Mybatis
优点功能,适用于承认以SQL
为中心,同时又需求工具能自动能生成大量常用的SQL
的应用。详细可参考官方文档。从最近一段时间的使用过程中,感觉从开发效率、维护性、易用性,都比较优秀。
本示例依然是基于上一篇文章的示例功能,从源数据库中读取test_user
表的数据,然后经过处理,再写入到目标数据库的test_user
表中。只是不是使用Spring Batch
内置的JdbcCursorItemReader
和JdbcBatchItemWriter
,改为使用BeetlSQL
进行读写。完整示例可参考代码
BeetlSQL
提供了Spring Boot
的starter
来实现自动配置,在pom.xml
添加以下依赖即可:
com.ibeetl
beetl-framework-starter
1.1.77.RELEASE
添加后,会添加两个依赖,分别是beetl-2.9.9
,和beetlsql-2.11.2
,如下图:
由于我们是使用多数据源进行读写,关于多数据源的配置,上一篇文章已经进行了描述,此处不再说明。BeetlSQL
对多数据源有较好的支持,只需要简单的配置即可。具体可参考官方文档。下面对此做简单说明。在application.properties
文件中添加以下配置:
#beetlsql配置
#默认/sql,可不设置
#beetlsql.sqlPath=/sql
#dao文件的后缀
beetlsql.daoSuffix=Repository
#自动加载和查找的dao文件所在包
beetlsql.basePackage=me.mason.springbatch
#默认org.beetl.sql.core.db.MySqlStyle,可不设置
#beetlsql.dbStyle=org.beetl.sql.core.db.MySqlStyle
#多数据源dao文件所在位置,以包区分读写数据源
beetlsql.ds.datasource.basePackage=me.mason.springbatch.dao.local
beetlsql.ds.originDatasource.basePackage=me.mason.springbatch.dao.origin
beetlsql.ds.targetDatasource.basePackage=me.mason.springbatch.dao.target
beetlsql.mutiple.datasource=datasource,originDatasource,targetDatasource
说明:
beetlsql.daoSuffix
表示Dao
文件的后缀,BeetlSql
会根据此后缀加载Dao
。beetlsql.mutiple.datasource
数据源名与数据源的配置一致。添加上述配置后,由于使用包名来区分读写数据源的Dao
,因此需要创建对应的包,分别在工程中me.mason.springbatch
下创建dao.local
,dao.origin
,dao.target
三个包,分别存放需要读取的三个数据源对应的Dao
。在本示例中,仅使用源数据库和目标数据库,因此,仅需要在dao.origin
中添加OriginUserRepository
进行源数据读操作,在dao.target
中添加TargetUserRepository
进行写操作即可。(注意,由于配置中指定是使用后缀Repository
,因此此处的类名需要使用它作为后缀)。如下:
OriginUserRepository.java
@Repository
public interface OriginUserRepository extends BaseMapper {
List getOriginUser(Map params);
}
TargetUserRepository.java
@Repository
public interface TargetUserRepository extends BaseMapper {
}
说明:
@Repository
标注是数据读写daoBaseMapper
,以使用BeetlSQL
内置的增删改查能力OriginUserRepository
,getOriginUser
是自定义的数据读取操作,此操作具体实现是使用写在sql/user.md
中的sql
语句(sql语句在后面将说明)。根据BeetlSql
的功能,开发者可以自定义sql
语句进行数据库操作,而sql
语句是以markdown文件的形式保存,支持beetl
的语法,支持参数化语句,逻辑判断等操作,这就有点像Mybatis
中的xml语句,但显示和修改会更友好。具体sql文件更详细的使用功能,读者可到官文档查阅。
本示例中,上面OriginUserRepository
自定义了getOriginUser
函数,以此函数名即可编写sql
进行读数据(当然,由于此sql
比较简单,完全可以不写到markdown
文件也可以实现,此处仅做示例展示此功能而已)。如下所示:
getOriginUser
===
* 查询user数据
select * from test_user
接着就是写数据需要用到的sql
,insertUser
是这条语句的名称,在插入数据时会使用到:
insertUser
===
* 插入数据
insert into test_user(id,name,phone,title,email,gender,date_of_birth
,sys_create_time,sys_create_user,sys_update_time,sys_update_user)
values (#id#,#name#,#phone#,#title#,#email#,#gender#,#dateOfBirth#
,#sysCreateTime#,#sysCreateUser#,#sysUpdateTime#,#sysUpdateUser#)
由上面的sql可见,这跟我们平时写sql
没有区别,其中以##
包括的是参数,即实体User
的字段。
经过上面的配置和添加的dao
类,我们已经有了读和写数据的能力。使用OriginUserRepository
,可以编写Spring Batch
的ItemReader
。读取数据后在内存中,在read()
时返回。如下所示:
@Slf4j
public class UserItemReader implements ItemReader {
protected List items;
protected Map params;
@Autowired
private OriginUserRepository originUserRepository;
@Override
public User read() throws Exception, UnexpectedInputException, ParseException, NonTransientResourceException {
if(Objects.isNull(items)){
//使用beetlsql的md执行sql
items = originUserRepository.getOriginUser(params);
if(items.size() > 0){
return items.remove(0);
}
}else{
if (!items.isEmpty()) {
return items.remove(0);
}
}
return null;
}
public Map getParams() {
return params;
}
public void setParams(Map params) {
this.params = params;
}
}
说明:
originUserRepository.getOriginUser
执行的是user.md
的getOriginUser
查询语句。List
,在read()
时返回其中的数据。全部返回后会返回null,以表示结束。BeetlSql
支持使用Map
来传参,本示例暂时没有使用。读取到数据后,使用TargetUserRepository
,执行上面编写的insertUser
来插入数据。如下所示
public class UserItemWriter implements ItemWriter {
@Autowired
private TargetUserRepository targetUserRepository;
@Override
public void write(List extends User> items) throws Exception {
targetUserRepository.getSQLManager().updateBatch("user.insertUser",items);
}
}
说明:
SQLManager
的updateBatch
批量写数据user.insertUser
中,user
是markdown
文件的名称,也是实体名称,insertUser
是写的sql
语句。新建BeetlsqlBatchConfig.java
,作为Spring Batch
的任务配置
使用上面已写的Reader
和Writer
,使用Bean
注解加入,如下:
@Bean
public ItemReader beetlsqlItemReader() {
UserItemReader userItemReader = new UserItemReader();
//设置参数,当前示例可不设置参数
Map params = CollUtil.newHashMap();
userItemReader.setParams(params);
return userItemReader;
}
@Bean
public ItemWriter beetlsqlWriter() {
return new UserItemWriter();
}
使用Step
和Job
,实现完整的任务配置,如下:
@Bean
public Job beetlsqlJob(Step beetlsqlStep,JobExecutionListener beetlsqlListener){
String funcName = Thread.currentThread().getStackTrace()[1].getMethodName();
return jobBuilderFactory.get(funcName)
.listener(beetlsqlListener)
.flow(beetlsqlStep)
.end().build();
}
@Bean
public Step beetlsqlStep(ItemReader beetlsqlItemReader ,ItemProcessor beetlsqlProcessor
,ItemWriter beetlsqlWriter){
String funcName = Thread.currentThread().getStackTrace()[1].getMethodName();
return stepBuilderFactory.get(funcName)
.chunk(10)
.reader(beetlsqlItemReader)
.processor(beetlsqlProcessor)
.writer(beetlsqlWriter)
.build();
}
参考上一文章的Db2DbJobTest
,编写BeetlsqlJobTest
文件。测试前先把目标数据库中的test_user
库清空,然后启动Job
进行测试,结果跟Db2DbJobTest
是一致的。日志输出如下:
在上面使用BeetlSql
过程中,可见有几个好处:
RowMapper
对数据进行映射,更简单。sql
语句写在markdown
文件中,修改更灵活。sql
语句执行输出在日志中,更清晰。本文使用Spring Batch
对数据库到数据库的示例做了一个改动,使用BeetlSQL
进行多数据源的读写操作,以实现更简单、更灵活、更清晰的数据库读写。希望对想要用Spring Batch
同时又想了解BeetlSql
的读者有帮助。