微信公众号地址: https://mp.weixin.qq.com/s?__biz=MzIyOTk1NjUyOQ==&mid=2247483652&idx=1&sn=3e75e674eaa9bdd81c5643152a6a2299&chksm=e8bb8fdadfcc06cc2dc557fd386daad713fa38a84a3e448c3c412d7e793106cdc46835b05904#rd
前言: 参考来自 spring 官方文档,地址链接: https://spring.io/guides/gs/batch-processing/
spring batch 。spring batch 是一个大数据量的批处理框架,使用场景,比如每天都要定时的从文件中读取数据,然后插入到数据库中,而spring batch 可以承受数百万上千万的数据量,当然,如果在数据量不大情况下,可以直接读取数据文件,然后读取文件内容到内存中,然后for循环插入到数据库中,这在数据量不大情况下,还是挺好用的。
1.1 创建一个 dto,对应文件中的字段类型,
@Getter@Setter@ToString public class PreUser implements Serializable { private String name; private String phone; private String picture; private Integer sex; }
1.2 创建一个 domain,跟数据库中的表对应
@Getter@Setter@ToString(callSuper = true) public class User extends BaseDomain{ private String name; private String phone; private String picture; private Integer sex; public String getSexName(){ String sexName = null; if(sex != null){ switch (sex){ case 1 : sexName = "男";break; case 0 : sexName = "女";break; default: sexName = "unkonw"; } } return sexName; } }
BaseDomain 是一个基础类,包含数据库表中的必备字段,包含 id,创建时间,创建人,修改时间,修改人等五个信息,
@Getter@Setter@ToString public class BaseDomain implements Serializable { private Integer id; private String createBy; private Date credateAt; private String updateBy; private Date updateAt; }
2.1 创建一个 processor,process 类继承自org.springframework.batch.item.ItemProcessor 接口,输入为PreUser(即从文件中读取的信息),输出为User类(即对应数据库中的类),
public class UserItemProcessor implements ItemProcessor{ @Override public User process(PreUser item) throws Exception { User user = new User(); user.setUpdateBy("system"); user.setCreateBy("system"); user.setUpdateAt(new Date()); user.setSex(item.getSex()); user.setPicture(item.getPicture()); user.setPhone(item.getName()); user.setName(item.getName()); return user; } }
2.2 自定义一个writer,spring batch 也给我们提供了很多的writer,不过这里没有使用,以为在实际中我们会在项目里面集成orm框架,这里使用的是mybatis,自定义一个mapper类来将数据插入数据库。
@Slf4j @Component public class UserItemWriter implements ItemWriter{ @Resource private UserMapper userMapper; @Override public void write(List extends User> items) throws Exception { for (User item : items) { Integer insert = userMapper.insert(item); log.info("insert result [{}],{{}},[{}]",insert,item.getName(),item.getPhone()); } } }
2.3 自定义一个 listener,用于监听在job执行完后,自定义的操作。
@Component @Slf4j public class JobCompletionNotificationListener extends JobExecutionListenerSupport { @Autowired private UserService userService; @Override public void afterJob(JobExecution jobExecution) { if(jobExecution.getStatus() == BatchStatus.COMPLETED){ Listusers = userService.listAll(); for (User user : users) { log.info("insert data [{}],[{}],[{}],[{}]",user.getName(),user.getPhone(),user.getPicture(),user.getSexName()); } } } }
3 装载配置文件
3.1 输入使用 FlatFileItemReader 来读取csv文件
3.2 组装一个上一步自定义的 UserItemProcessor
3.3 组装一个 Step , Step 里面设置之前组装好的reader,process,writer
3.4 组装一个 Job ,Job 里面设置好 Setp ,Listener 等信息。
@Configuration @EnableBatchProcessing public class BatchConfiguration { @Autowired JobBuilderFactory jobBuilderFactory; @Autowired StepBuilderFactory stepBuilderFactory; @Autowired private UserItemWriter userItemWriter; @Bean public FlatFileItemReaderreader(){ return new FlatFileItemReaderBuilder () .name("personItemReader") .resource(new ClassPathResource("sample-data.csv")) .delimited() .names(new String[]{"name", "phone", "picture", "sex"}) .fieldSetMapper(new BeanWrapperFieldSetMapper (){ { setTargetType(PreUser.class); }}) .build(); } @Bean public UserItemProcessor processor(){ return new UserItemProcessor(); } @Bean public Job importUserJob(JobCompletionNotificationListener listener, Step step1) { return jobBuilderFactory.get("importUserJob") .incrementer(new RunIdIncrementer()) .listener(listener) .flow(step1) .end() .build(); } @Bean public Step step1() { return stepBuilderFactory.get("step1") . chunk(10) .reader(reader()) .processor(processor()) .writer(userItemWriter) .build(); } /** * 作业仓库 * * @param dataSource * @param transactionManager * @return * @throws Exception */ @Bean public JobRepository jobRepository(DataSource dataSource, PlatformTransactionManager transactionManager) throws Exception{ JobRepositoryFactoryBean jobRepositoryFactoryBean = new JobRepositoryFactoryBean(); jobRepositoryFactoryBean.setDataSource(dataSource); jobRepositoryFactoryBean.setTransactionManager(transactionManager); jobRepositoryFactoryBean.setDatabaseType(DatabaseType.MYSQL.name()); return jobRepositoryFactoryBean.getObject(); } /** * 作业调度器 * * @param dataSource * @param transactionManager * @return * @throws Exception */ @Bean public SimpleJobLauncher jobLauncher(DataSource dataSource, PlatformTransactionManager transactionManager) throws Exception{ SimpleJobLauncher jobLauncher = new SimpleJobLauncher(); jobLauncher.setJobRepository(this.jobRepository(dataSource, transactionManager)); return jobLauncher; } @Bean public MyJobListener myJobListener(){ return new MyJobListener(); } }
4 配置 joblancher 等信息。到这里我们已经把 job 给配置好了 ,但是还不能执行,在上面的配置文件中,我们还初始化了 JobRepository(作业仓库),
SimpleJobLauncher (作业调度器),MyJobListener (Job监听器)。在这个test类中 作业调度器来执行job。最终的效果是文件被读取到数据库中。
@Slf4j public class TestJob extends HellenBatchApplicationTests { @Resource SimpleJobLauncher simpleJobLauncher; public JobParameters jobParameters; @Autowired Job importJob; @Test public void testJob() throws JobParametersInvalidException, JobExecutionAlreadyRunningException, JobRestartException, JobInstanceAlreadyCompleteException { jobParameters = new JobParametersBuilder().addLong("time", System.currentTimeMillis()).toJobParameters(); simpleJobLauncher.run(importJob, jobParameters); } }
5 在开始上面的配置之前,还要创建项目,集成 Mybatis ,导入数据库表等。这里就不详述了
6 spring batch 是一个大数据量的批处理框架,但是并没有提供 定时执行的能力,这个还要依靠外部框架,比如 quartz,elastic job等,接下来还会在项目中集成 elastc job 这个框架,elastic job 是一个分布式的定时器框架,在分布式系统中可以控制他单机或者多机跑。
7 参考的 git项目地址为:[email protected]:ds245486410/hellen-spring-batch.git
在上文中有什么没有疑问的,可以看看项目的内容。