SpringBatch结合SpringBoot简单使用实现工资发放批处理操作

news2025/1/13 10:27:23

最近有接触到批处理相关的需求,学习了下SpringBatch的使用方法。SpringBatch能把复杂的批处理任务进行step分解,并能通过reader和writer满足不同来源数据的处理需求,支持在step定义时设置异常重试策略等,比较方便拓展。简单记录下基于SpringBoot写的使用demo。

需求

两张表,user_with_role和role_num,分别有user信息和工资流水信息,role_num冗余。
user_with_role表,包含员工信息和role字段信息

CREATE TABLE `user_with_role` (
  `id` INT NOT NULL AUTO_INCREMENT,
  `username` VARCHAR(45) NULL,
  `role` VARCHAR(45) NULL,
  PRIMARY KEY (`id`));

role_num表,包含发钱信息

CREATE TABLE `role_num1` (
  `id` INT NOT NULL AUTO_INCREMENT,
  `role` VARCHAR(45) NULL,
  `account` INT NULL,
  `username` VARCHAR(45) NULL,
  `inputtime` VARCHAR(45) NULL,
  PRIMARY KEY (`id`));

pom设置和application配置文件

在springboot的基础上使用batch,pom.xml中增加dependency

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-batch</artifactId>
        </dependency>

还需要加上jdbc依赖,不过这个一般都有加。

application中增加:

spring:
  batch:
    initialize-schema: always

这个配置主要是让springbatch自动在数据库中创建运行所需的表,SpringBoot2.7版本后修改为spring.batch.jdbc.initialize-schema了,不过我还在用老土2.3,先这样吧。

bean类

UserWithRole

@Data
public class UserWithRole {
    private int id;
    private String userName;
	private String role;
}

RoleNum

@Data
public class RoleNum {
    private int id;
    private String role;
    private int account;
    private String userName;
	private String inputTime;
}

关键job配置类

包含reader,writer,step和整体job的配置,通过@Bean注解的方法来进行spring管理,下面一步步来。

@Configuration
@EnableBatchProcessing
// batch job设置
// 将user_with_role中的role_user工资信息,输出到role_num表中
public class RoleCountingBatchJobConfig {

    //注入任务对象工厂
    @Autowired
    private JobBuilderFactory jobBuilderFactory;

    //任务的执行由Step决定,注入step对象的factory
    @Autowired
    private StepBuilderFactory stepBuilderFactory;

    @Autowired
    private DataSource dataSource;

    @Autowired
    private NamedParameterJdbcTemplate namedParameterJdbcTemplate;

SpringBatch默认会构建两个Factory用来进行job构建,DataSource默认为Spring配置的数据库,NamedParameterJdbcTemplate由Spring提供,用来将statement中的问号占位符改为利用命名参数映射更方便,不过我后面没用。

    // 设置itemReader
    @Bean
    public JdbcCursorItemReader<UserWithRole> getItemReader(){
        JdbcCursorItemReader<UserWithRole> itemReader = new JdbcCursorItemReader<>();
        itemReader.setDataSource(dataSource);//设置数据源
        // 实体映射
        itemReader.setRowMapper(new RowMapper<UserWithRole>() {
            @Override
            public UserWithRole mapRow(ResultSet rs, int rowNum) throws SQLException {
                UserWithRole uwr = new UserWithRole();
                uwr.setId(rs.getInt("id"));
                uwr.setUserName(rs.getString("userName"));
                uwr.setRole(rs.getString("role"));
                return uwr;
            }
        });
        String sql = "select u.id as id, u.username as userName, u.role as role from user_with_role as u";
        itemReader.setSql(sql);
        return itemReader;
    }

itemReader用的是JdbcCursorItemReader,用来从user_with_role表中读取数据,主要是配置RowMapper,将行数据封装成UserWithRole对象,sql就是简单查询。

    // 设置itemWriter
    @Bean
    public JdbcBatchItemWriter<RoleNum> getItemWriter(){
        JdbcBatchItemWriter<RoleNum> itemWriter = new JdbcBatchItemWriter<>();

        itemWriter.setDataSource(dataSource);
        itemWriter.setJdbcTemplate(namedParameterJdbcTemplate);
        String sql = "insert into role_num(role, account, username, inputtime) values(?, ?, ?, ?)";
        itemWriter.setSql(sql);
        itemWriter.setItemPreparedStatementSetter(new RoleNumPreparedStatementSetter());

        return itemWriter;
    }

itemWriter用的是JdbcBatchItemWriter,用来把RoleNum对象存到数据库中,主要的参数写入逻辑写在RoleNumPreparedStatementSetter类中。

public class RoleNumPreparedStatementSetter implements ItemPreparedStatementSetter<RoleNum> {
    // 将role_num的批处理计算结果存入的sql
    @Override
    public void setValues(RoleNum roleNum, PreparedStatement preparedStatement) throws SQLException {
        preparedStatement.setString(1, roleNum.getRole());
        preparedStatement.setInt(2, roleNum.getAccount());
        preparedStatement.setString(3, roleNum.getUserName());
        preparedStatement.setString(4, roleNum.getInputTime());
    }
}

RoleNumPreparedStatementSetter主要是将参数通过占位符放到sql中。

    // 设置process
    @Bean
    public RoleUserCountingProcess getProcess(){
        return new RoleUserCountingProcess();
    }
public class RoleUserCountingProcess implements ItemProcessor<UserWithRole, RoleNum> {
    // 将user的信息转换成roleNum信息,当作发工资
    @Override
    public RoleNum process(UserWithRole userWithRole) throws Exception {
        RoleNum newRoleNum = new RoleNum();
        newRoleNum.setUserName(userWithRole.getUserName());
        newRoleNum.setRole(userWithRole.getRole());
        newRoleNum.setInputTime(new LocalDateTime(new Date()).toString());
        if(userWithRole.getRole()==null)
            return newRoleNum;
        switch (userWithRole.getRole()) {
            case "employee":
                newRoleNum.setAccount(100);
                break;
            case "manager":
                newRoleNum.setAccount(10000);
                break;
            case "boss":
                newRoleNum.setAccount(100000);
                break;
        }
        System.out.println(newRoleNum.getUserName() + "---" + newRoleNum.getAccount());
        return newRoleNum;
    }
}

process中定义了具体的工资写入逻辑,实现了ItemProcess接口的process方法,将itemReader的输出UserWithRole类和itemWriter的输入RoleNum类进行连接,从而完成process过程。

    // 设置step
    @Bean
    public Step getStep(){
        return stepBuilderFactory.get("user_role_convert_step")
                .<UserWithRole, RoleNum>chunk(10)
                .reader(getItemReader())
                .processor(getProcess())
                .writer(getItemWriter())
                .build();
    }

    // 获取job对象
    @Bean
    public Job RoleCountingBatchJob(JobBuilderFactory jobBuilders,
                                    StepBuilderFactory stepBuilders){
        return jobBuilders.get("user_role_convert_step")
                .start(getStep())
                .build();
    }

最后定义step和job整体流程。在配置文件中加入:

spring:
  batch:
    job:
      enabled: false

可以阻止job在项目启动时自动执行。

controller启动jobInstance

@Controller
public class BatchRunnerController {

    @Autowired
    JobLauncher jobLauncher;

    // 自定义job任务
    @Autowired
    Job roleCountingBatchJob;

    @RequestMapping("/countingRoleUser")
    @ResponseBody
    public String countingRollUser() throws JobInstanceAlreadyCompleteException, JobExecutionAlreadyRunningException, JobParametersInvalidException, JobRestartException {
        // 设置时间parameter来区分instance
        JobParametersBuilder jobParametersBuilder = new JobParametersBuilder();
        jobParametersBuilder.addDate("startDate", new Date());
        jobLauncher.run(roleCountingBatchJob, jobParametersBuilder.toJobParameters());
        return "role user counting batch success --- " +
                Objects.requireNonNull(jobParametersBuilder.toJobParameters().getDate("startDate")).toString();
    }
}

利用jobLauncher来执行对应的job,date时间来区分不同的jobInstance。

实现效果

在这里插入图片描述
user_with_role表

在这里插入图片描述
批处理job正常执行

在这里插入图片描述
duideduide没有role所以没发到工资

总结

本文简单记录了下简单的springbatch的使用,包括item相关,process编写,step构建和最终的job使用。springbatch还有一点是可以设置出现异常的处理策略,比如容忍数次异常,调过某些异常等,在真实使用中比较灵活,有机会再补充。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/961236.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【Java基础】深入理解反射、反射的应用(工厂模式、代理模式)

文章目录 1. Java反射机制是什么&#xff1f;1.2 Java反射例子 2. Java反射机制中获取Class的三种方式及区别&#xff1f;3. Java反射机制的应用场景有哪些&#xff1f;3.1. 优化静态工厂模式&#xff08;解耦&#xff09;3.1.1 优化前&#xff08;工厂类和产品类耦合&#xff…

【仿写spring之ioc篇】一、预期目标以及项目结构介绍

前言 最近系统的学习了一下spring的源码&#xff0c;准备简单仿写一下spring&#xff0c;目前是仿写ioc篇&#xff0c;在ioc篇中将完成整套的bean的生命周期&#xff0c;当然是简单的实现&#xff0c;不会像spring真正源码那样做非常系统性的校验以及接口设计。 预期目标 将…

关于“VS2022无法打开头文件<graphics.h>” 以及编译时 “没有与参数列表匹配的重载函数实例”俩个问题的解决思路

前言&#xff1a; 今天无聊没什么事干&#xff0c;突然看见一条视频梦回20年前杀马特横行的年代&#xff0c;炫酷而又土嗨的灯光闪烁&#xff0c;我又想了想之前写扫雷小游戏的时候&#xff0c;有的人写的游戏界面非常的炫酷&#xff0c;各种颜色都有&#xff0c;抱着没事没事干…

c#事件(event)

概述&#xff1a; C#中的事件是一种特殊的委托&#xff0c;它用于实现观察者模式&#xff0c;允许对象在特定事件发生时通知其他对象。 以下是使用C#事件的示例&#xff1a; 首先&#xff0c;定义一个包含事件的类&#xff1a; public class EventPublisher {// 声明一个事…

向新NEW · 数智赋能新未来 | 2023TechWorld 绿盟科技智慧安全大会圆满召开

9月1日&#xff0c;以“向新NEW数智赋能新未来”为主题的2023TechWorld绿盟科技智慧安全大会在北京如约启幕。站在新十年开篇之际&#xff0c;TechWorld技术嘉年华正式更名TechWorld智慧安全大会&#xff0c;名字全新升级背后反映着广大用户对绿盟科技始终坚持的技术战略——“…

WEBGL(3):鼠标动态绘制点

1 实现思路 绘制单个点鼠标事件监听点击事件将点推送到数组中绘制数组中所有点 2 实现代码 <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8"><meta http-equiv"X-UA-Compatible" content"IEedge&…

MySQL 数据库常用命令大全(完整版)

文章目录 1. MySQL命令2. MySQL基础命令3. MySQL命令简介4. MySQL常用命令4.1 MySQL准备篇4.1.1 启动和停止MySQL服务4.1.2 修改MySQL账户密码4.1.3 MySQL的登陆和退出4.1.4 查看MySQL版本 4.2 DDL篇&#xff08;数据定义&#xff09;4.2.1 查询数据库4.2.2 创建数据库4.2.3 使…

手机怎么压缩图片?压缩方法看这些

手机怎么压缩图片&#xff1f;在现代社会中&#xff0c;手机已经成为我们日常生活中不可或缺的一部分。我们使用手机拍照、上传照片和分享照片&#xff0c;但是在分享照片之前&#xff0c;我们经常需要将其压缩&#xff0c;以便在互联网上更轻松地共享。下面这篇文章就给大家介…

下行抢占指示 DCI format 2_1

这部分R17和R15基本内容一样&#xff0c;只是有写细节描述略有区别&#xff0c;这里就以R17版本的协议看下downlinkPreemption&#xff0c;即DCI format 2_1有关内容。 R15支持eMBB和URLLC服务。 由于URLLC业务是优先级较高的业务&#xff0c;当无线资源不足时&#xff0c;已经…

如何把pdf文件合并?分享最新pdf合并方法

在所有文档格式中&#xff0c;pdf应该是最常用的&#xff0c;像产品介绍、商务合同、法律文书等等&#xff0c;这些都是pdf格式的。有时候出于工作需要&#xff0c;我们要把两份或者多份pdf文件合并在一起&#xff0c;那么问题来了&#xff0c;如何把pdf文件合并呢?小编最近发…

【勘误】基于多目标粒子群算法的微电网优化调度【风光、储能、柴油、燃气、电网交互】

目录 1 主要内容 2 部分代码 3 程序结果 4 下载链接 1 主要内容 这是一篇代码勘误&#xff0c;该程序复现文献《基于多目标粒子群算法的微电网优化调度》&#xff0c;代码实现了多目标优化调度模型——考虑微电网系统运行成本和环境保护成本的并网模式下微电网多目标优化调…

【NCRE 二级Java语言程序设计01】全国计算机等级考试初识

目录 前言一、认识全国计算机等级考试1.官方的自我介绍2.省级和全国级的区别3.考试内容 二、NCRE正确入口三、官方重要资源分布1.大纲教材2.相关下载3.试题选登4.常见问题 总结 前言 &#x1f4dc; 本专栏主要是分享自己备考全国计算机二级Java语言程序设计所学心得体会、所搜集…

SpringBoot自定义工具类—基于定时器完成文件清理功能

直接复制粘贴既可&#xff01;&#xff01; import org.springframework.scheduling.annotation.Scheduled; import org.springframework.stereotype.Component; import java.io.File; import java.time.LocalDate; import java.time.LocalDateTime; import java.time.ZoneOff…

【云原生进阶之PaaS中间件】第一章Redis-2.5缓存持久化

1 Redis持久化 1.1 Redis持久化分类 Redis 中的数据都是保存在内存中的&#xff0c;当Redis服务重启后&#xff0c;内存中的数据都会丢失&#xff0c;所以需要将内存中的数据保存到磁盘上&#xff0c;方便系统故障时&#xff0c;从磁盘上的备份数据恢复到内存中。 Redis 中的持…

PMP考试难度大吗?该如何备考?

PMP&#xff08;Project Management Professional)&#xff09;认证考试是全球范围内最重要、最权威的项目管理行业认证之一。但是&#xff0c;很多人对PMP考试的难度心存疑虑。在这篇文章中&#xff0c;我们将讨论PMP考试的难度&#xff0c;并提供一些备考建议。 首先&#x…

python“魂牵”京东商品历史价格数据接口(含代码示例)

要通过京东的API获取商品详情历史价格数据&#xff0c;您可以使用京东开放平台提供的接口来实现。以下是一种使用Java编程语言实现的示例&#xff0c;展示如何通过京东开放平台API获取商品详情历史价格数据&#xff1a; 首先&#xff0c;确保您已注册成为京东开放平台的开发者…

一款windows的终端神奇,类似mac的iTem2

终于找到了一款windows的终端神奇。类似mac的iTem2 来&#xff0c;上神器 cmder cmder是一款windows的命令行工具&#xff0c;就是我们的linux的终端&#xff0c;用起来和linux的命令一样。所以我们今天要做的是安装并配置cmder ![在这里插入图片描述](https://img-blog.csdni…

前端需要学习哪些技术?

前端工程师岗位缺口一直很大&#xff0c;符合岗位要求的人越来越少&#xff0c;所以学习前端的同学要注意&#xff0c;一定要把技能学到扎实&#xff0c;做有含金量的项目&#xff0c;这样在找工作的时候展现更大的优势。 缺人才&#xff0c;又薪资高&#xff0c;那么怎样才能…

赞奇科技参与华为云828 B2B企业节,云工作站入选精选产品解决方案

8月27日&#xff0c;由华为云携手上万家伙伴共同发起的第二届 828 B2B 企业节拉开帷幕&#xff0c;围绕五大系列活动&#xff0c;为万千中小企业带来精细化商机对接。 聚焦行业数字化所需最优产品&#xff0c;举办超1000场供需对接会&#xff0c;遍及20多个省100多个城市&…

企业架构LNMP学习笔记3

服务器基本环境配置&#xff1a; 1、安装虚拟机&#xff0c;centos7.9 操作系统&#xff1b; 2、网络配置&#xff1b; 3、机器名FQDN设置&#xff1b; 4、DNS解析设置&#xff0c;本地hosts设置&#xff1b; 5、配置yum源环境&#xff1b; 6、vim安装配置&#xff1b; …