Spring Batch分析(一)


在前两篇文章中,我们对spring batch这个批处理框架进行了初步的学习和了解。你可以从中了解到spring batch的基本概念、应用场景、如何编写一个spring batch的demo,以及其架构设计和核心组件的简介。

今天我们将深入分析Spring Batch中一个常用的类:JdbcPagingItemReader。通过对其源码的分析,你将对Spring Batch有更深入的理解,从而更好地进行技术选型和场景化方案的落地。

1、JdbcPagingItemReader类的继承层次:

可以看到,该类继承自ItemReader和ItemStream接口。

2、JdbcPagingItemReader的作用是什么?

JdbcPagingItemReader用于通过JDBC以分页方式从数据库中读取记录。它通过PagingQueryProvider构建的SQL来检索数据,并使用setPageSize(int)方法指定分页大小。通过调用read()方法请求其他页面,并返回与当前位置相对应的对象。在重新启动时,它会使用最后一个排序键值来定位要读取的第一页。排序键必须具有唯一的键约束,以确保在两次执行之间不会丢失任何数据。分页性能依赖于数据库的特定功能来限制返回的行数。设置较大的页面大小并使用与页面大小匹配的提交间隔可以提高性能。在两次调用open(ExecutionContext)之间,该实现是线程安全的,但在多线程环境中使用时,需要设置saveState=false(无重启功能)。

3、JdbcPagingItemReader的属性有哪些?

private static final String START_AFTER_VALUE = "start.after";
public static final int VALUE_NOT_SET = -1;
private DataSource dataSource;
private PagingQueryProvider queryProvider;
private Map parameterValues;
private NamedParameterJdbcTemplate namedParameterJdbcTemplate;
private RowMapper rowMapper;
private String firstPageSql;
private String remainingPagesSql;
private Map startAfterValues;
private Map previousStartAfterValues;
private int fetchSize = VALUE_NOT_SET;

关于PagingQueryProvider接口,需要说明的是,Spring Batch根据不同的数据库类型封装了相应的实现类,如MySqlPagingQueryProvider、OraclePagingQueryProvider等,如下图所示:

如果你熟悉阿里巴巴开源的DataX,那么你会发现其设计思想与Spring Batch有一定的相似性,都是通过Reader读取数据源,Writer写入数据源。DataX提供了更细粒度的控制和可插拔性,只需对需要的部分进行组装即可使用,而Spring Batch则提供了常用数据源的封装。

4、JdbcPagingItemReader也实现了InitializingBean接口的afterPropertiesSet方法:

public void afterPropertiesSet() throws Exception {
    super.afterPropertiesSet();
    Assert.notNull(dataSource, "DataSource may not be null");
    JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource);
    if (fetchSize != VALUE_NOT_SET) {
        jdbcTemplate.setFetchSize(fetchSize);
    }
    jdbcTemplate.setMaxRows(getPageSize());
    namedParameterJdbcTemplate = new NamedParameterJdbcTemplate(jdbcTemplate);
    Assert.notNull(queryProvider, "QueryProvider may not be null");
    queryProvider.init(dataSource);
    this.firstPageSql = queryProvider.generateFirstPageQuery(getPageSize());
    this.remainingPagesSql = queryProvider.generateRemainingPagesQuery(getPageSize());
}

从这里可以看出,Spring Batch实际上是使用JdbcTemplate进行SQL查询的,默认的pageSize为10,然后queryProvider调用init方法,将DataSource作为参数传入。

DataSource作为init参数传入后的代码逻辑如下:

public void init(DataSource dataSource) throws Exception {
    Assert.notNull(dataSource, "A DataSource is required");
    Assert.hasLength(selectClause, "selectClause must be specified");
    Assert.hasLength(fromClause, "fromClause must be specified");
    Assert.notEmpty(sortKeys, "sortKey must be specified");
    StringBuilder sql = new StringBuilder(64);
    sql.append("SELECT ").append(selectClause);
    sql.append(" FROM ").append(fromClause);
    if (whereClause != null) {
        sql.append(" WHERE ").append(whereClause);
    }
    if(groupClause != null) {
        sql.append(" GROUP BY ").append(groupClause);
    }
    List namedParameters = new ArrayList();
    parameterCount = JdbcParameterUtils.countParameterPlaceholders(sql.toString(), namedParameters);
    if (namedParameters.size() > 0) {
        if (parameterCount != namedParameters.size()) {
            throw new InvalidDataAccessApiUsageException(
                "You can't use both named parameters and classic \"?\" placeholders: " + sql);
        }
        usingNamedParameters = true;
    }
}

从这段代码可以看出几点:

  • DataSource必须指定,否则会抛出异常。
  • select查询列必须明确,不能使用select *。
  • fromClause必须有,否则不知道从哪个表查询数据,如果不传会抛出异常。
  • sortKey是必须的,Spring Batch要求传一个sortKey,且该sortKey必须能确定数据的唯一性,否则在批处理时会遗漏数据(需要注意的是,分页查询必须指定sortKey,这对查询性能有一定影响。如果不想指定sortKey,直接会抛出异常;如果指定了唯一key作为sortKey,但select中没有sortKey,会报列名无效的异常,且该异常不会明确指出是哪个列无效,只能通过异常堆栈判断)。

此外,Spring Batch的PagingQueryProvider只支持单表查询,不支持join类型的查询。

5、SortedKey的结构是怎样的?

public void setSortKeys(Map sortKeys) {
    this.sortKeys = sortKeys;
}

可以看出,SortedKey是一个Map对象,其中key是数据库表的唯一key字段名称,value是一个Order对象。Order对象只有两个属性:升序或降序,Order是一个枚举类型:

public enum Order {
    ASCENDING, DESCENDING
}

今天主要分享了Spring Batch中从数据库数据源读取数据的方式PagingQueryProvider。对于开源工具,我们不评价其好坏,而是吸收其设计思想,发现其不足之处。如果有余力,可以自行研发。

如果你有数据库、消息类、文件类等数据源,可以选择Spring Batch。建议每个reader读取单表数据,然后在processor中处理多个结果集,最后将数据插入目标数据源。对于database类型,希望你在使用Spring Batch的Reader读取数据时能提高性能,如使用索引,避免全表扫描等。

当然,对于数据的抽取、清洗和转换,你也可以考虑其他技术方案,如kettle、DataX(商业版是DataWorks),以及大数据类型的解决方案。同时,你还需要考虑资源问题,如时间、人力等。


# mysql  # oracle  # git  # access  # 工具  # ai  # red  # batch  # sql  # spring  # 架构  # 数据类型  # 封装  # select  # 枚举类型  # int  # 继承  # 接口  #   #   # 线程  # 多线程  # map  # 对象  # database  # 数据库  # 是一个  # 分页  # 的是  # 可以看出  # 抛出  # 两次  # 有一定  # 批处理  # 会报  # 开源 


相关栏目: 【 Google疑问12 】 【 Facebook疑问10 】 【 网络优化76771 】 【 技术知识130152 】 【 IDC云计算60162 】 【 营销推广131313 】 【 AI优化88182 】 【 百度推广37138 】 【 网站推荐60173 】 【 精选阅读31334


相关推荐: Python与GPU加速技术_CUDA与Numba高性能计算实践  如何用正则与预处理结合精准拦截拼接式垃圾域名  如何在Golang中使用replace替换模块_指定本地或远程路径  Win11怎么设置声音输出设备_Windows11音量合成器单独调节应用  Windows10电脑怎么设置虚拟内存_Win10高级系统设置性能  如何高效获取循环末次生成的 NumPy 数组最后一个元素(无需额外循环)  如何在Golang中实现服务熔断与限流_Golang微服务容错与流控方法  Windows服务持续崩溃怎样修复_系统服务保护机制解析  Mac的“调度中心”与“空间”怎么用_Mac多桌面高效管理【技巧】  Win11怎么设置DNS服务器_Windows11修改网络适配器DNS优选  php8.4xdebug无法调试怎么办_php8.4xdebug配置问题解决【解答】  Win11开始菜单打不开_修复Windows 11点击开始图标无响应【教程】  php订单日志怎么记录评价_php记录订单评价日志方法【方法】  Win10电脑怎么设置休眠快捷键_Windows10电源按钮功能定义  Win11任务栏怎么放到顶部_Win11修改任务栏位置方法【详细】  为什么Go建议使用error接口作为错误返回_Go Error接口设计原因说明  如何使用Golang反射将map转换为struct_Golang reflect类型映射技巧  Windows蓝屏BAD_POOL_HEADER故障详解_蓝屏池损坏错误修复指南  如何使用Golang捕获并记录协程panic_保证主程序稳定运行  如何在 Django 中修改用户密码后保持会话不丢失  Windows11怎么自定义任务栏_Windows11任务栏自定义教程【步骤】  如何诊断并终止卡死的 multiprocessing 子进程  Win11怎么设置夜间模式_Windows11显示设置蓝光过滤强度  Linux如何挂载新硬盘_Linux磁盘分区格式化与开机自动挂载【指南】  如何使用Golang写入二进制文件_Golang io Write二进制写入示例  Win10怎么更改用户名 Win10修改账户名称操作教程  php下载安装包太大怎么下载_分卷压缩下载方法【教程】  Windows怎样拦截WPS弹窗广告_Windows拦截WPS弹窗广告设置【步骤】  Win11如何设置开机自动联网 Win11宽带连接自动拨号【步骤】  SAX解析器是什么,它与DOM在处理大型XML文件时有何不同?  Python性能剖析高级教程_cProfileLineProfiler优化案例解析  php中self::能调用子类重写的方法吗_静态绑定与重写关系【介绍】  PHP主流架构如何处理会话管理_Session与Cookie【技巧】  Windows音频驱动无声音原因解析_声卡驱动错误修复步骤  Win11搜索栏无法输入_解决Win11开始菜单搜索没反应问题【技巧】  php会话怎么开启_session_start函数的作用与使用时机【方法】  Windows任务计划服务异常原因_任务调度失败的处理方案  如何在Golang中写入JSON文件_保存结构体数据到文件  Windows 11如何开启文件夹加密(EFS)_Windows 11文件属性中加密内容以保护数据  Win10怎样设置闹钟贪睡时间 Win10闹钟贪睡时长设置【步骤】  Win11如何设置省电模式 Win11开启电池节电功能【优化】  Win10怎么关闭自动更新错误弹窗_Win10策略屏蔽失败提示减少干扰【防护】  C++中的Pimpl idiom是什么,有什么好处?(隐藏实现)  短链接怎么用php递归还原_多层加密链接的处理法【详解】  Win11怎么设置默认浏览器Chrome_Windows11修改默认网页打开方式  php查询数据怎么分组_groupby分组查询配合聚合函数【技巧】  如何减少Golang内存碎片化_Golang内存分配与回收优化方法  如何使用正则表达式批量替换重复的星号-短横模式为固定字符串  Win10怎么关闭自动更新错误重启 Win10策略禁止失败补丁强制重启【防护】  Win11怎么设置屏保_Windows 11屏幕保护程序开启与设置【详解】 

 2025-07-18

了解您产品搜索量及市场趋势,制定营销计划

同行竞争及网站分析保障您的广告效果

点击免费数据支持

提交您的需求,1小时内享受我们的专业解答。

致胜网络推广营销网


致胜网络推广营销网

致胜网络推广营销网专注海外推广十年,是谷歌推广.Facebook广告全球合作伙伴,我们精英化的技术团队为企业提供谷歌海外推广+外贸网站建设+网站维护运营+Google SEO优化+社交营销为您提供一站式海外营销服务。

 915688610

 17370845950

 915688610@qq.com

Notice

We and selected third parties use cookies or similar technologies for technical purposes and, with your consent, for other purposes as specified in the cookie policy.
You can consent to the use of such technologies by closing this notice, by interacting with any link or button outside of this notice or by continuing to browse otherwise.