spring batch

SpringBatch是解决企业数据逻辑较简单,重复性高,大数据量而设计的.从他提供的各种Reader就能看出来.起码我是这样理解的.最适合做的如:数据清洗,数据分析后转移,或者定时的和其他系统交互的地方等.

在上一篇文章中,我使用了 JdbcPagingItemReader读取HSQLDB数据库的数据.

01<bean id="sysAppStoreMapper" class="net.dbatch.mapper.SysAppStoreMapper" />
02  
03<bean id="dbReader"
04          class="org.springframework.batch.item.database.JdbcPagingItemReader">
05        <property name="dataSource" ref="dataSource"/>
06        <property name="rowMapper" ref="sysAppStoreMapper"/>
07        <property name="queryProvider" ref="appQueryProvider"/>
08    </bean>
09  
10  
11    <bean id="appQueryProvider"
12          class="org.springframework.batch.item.database.support.HsqlPagingQueryProvider">
13        <property name="selectClause" value="a.APP_ID, a.PARENT_ID, a.APP_DESC, a.APP_URL, a.FOLDER, a.SEQ"/>
14        <property name="fromClause" value="sys_appstore a"/>
15        <property name="sortKey" value="SEQ"/>
16    </bean>

事实上SpringBatch提供了很多的Reader,自定义的Reader只要是继承自org.springframework.batch.item.ItemReader接口的都可以.但是好多都不用你麻烦了,SpringBatch都替你做好了.2.1.8API中基本常用的和数据库[Hibernate/Ibatis/JDBC],文件系统,JMS消息等Reader现成的实现.如图:

 


对于喜欢SpringJDBC的用户[我就非常不喜欢Hibernate ]可以使用JdbcPagingItemReader

,然后指定一个queryProvider ,queryProvider 是针对各种数据库的一个分页的实现,常用的数据库 queryProvider也有现成的.如图:



好了.如果上面你实在找不到你可以使用的数据库对应的实现,而你又了解你的数据库SQL,你可以使用JdbcCursorItemReader.这个Reader允许你自己set SQL.

如我上面实现的例子,用JdbcCursorItemReader改写也非常简单:

1<bean id="dbReader"
2          class="org.springframework.batch.item.database.JdbcCursorItemReader">
3        <property name="dataSource" ref="dataSource" />
4        <property name="sql" value="select a.APP_ID, a.PARENT_ID, a.APP_DESC, a.APP_URL, a.FOLDER from sys_appstore a order by a.SEQ"/>
5        <property name="rowMapper" ref="sysAppStoreMapper" />
6    </bean>

他仍然可以工作的很好,而且还简单了.

如果我的数据来源不是从数据库,从文件的怎么办

看到刚才的Reader实现里有个FlatFileItemReader没他就是读取文件[文本文件]的.

假如我要分析这样结构的log日志信息

01User1,20
02User2,21
03User3,22
04User4,23
05User5,24
06User6,25
07User7,26
08User8,27
09User9,28
10User10,29

他都是一些结构化的文本文件,我可以很容易的实现.如Spring代码:

01<bean id="delimitedLineTokenizer" class="org.springframework.batch.item.file.transform.DelimitedLineTokenizer" />
02  
03    <bean id="lineMapper" class="org.springframework.batch.item.file.mapping.DefaultLineMapper">
04        <property name="lineTokenizer" ref="delimitedLineTokenizer" />
05        <property name="fieldSetMapper">
06            <bean class="net.dbatch.sample.UserMapper" />
07        </property>
08    </bean>
09  
10    <bean id="messageReader" class="org.springframework.batch.item.file.FlatFileItemReader">
11        <property name="lineMapper" ref="lineMapper" />
12        <property name="resource" value="classpath:/users.txt" />
13    </bean>

再写上一个对应的Bean

01public class UserMapper implements FieldSetMapper<User> {
02      
03      
04    public User mapFieldSet(FieldSet fs) throws BindException {
05        User u = new User();
06        u.setName(fs.readString(0));
07        u.setAge(fs.readInt(1));
08        return u;
09    }
10}

Processor:

01public class MessagesItemProcessor implements ItemProcessor<User, Message> {
02  
03    public Message process(User user) throws Exception {
04        if(!StringUtils.hasText(user.getName())){
05            throw new RuntimeException("The user name is required!");
06        }
07        Message m = new Message();//Message是user一个简单的包装
08        m.setUser(user);
09        m.setContent("Hello " + user.getName()
10                ",please pay promptly at end of this month.");
11        return m;
12    }
13}

Writer:

1public class MessagesItemWriter implements ItemWriter<Message> {
2  
3    public void write(List<? extends Message> messages) throws Exception {
4        System.out.println("write results");
5        for (Message m : messages) {
6            System.out.println(m.getContent());  //只做输出
7        }
8    }
9}

测试代码:

01public static void main(String[] args) {
02        ClassPathXmlApplicationContext c = new ClassPathXmlApplicationContext("localfile_job.xml");
03        SimpleJobLauncher launcher = new SimpleJobLauncher();
04        launcher.setJobRepository((JobRepository) c.getBean("jobRepository"));
05        launcher.setTaskExecutor(new SyncTaskExecutor());
06        try {
07            JobExecution je = launcher.run((Job) c.getBean("messageJob"),
08                    new JobParametersBuilder().toJobParameters());
09            System.out.println(je);
10            System.out.println(je.getJobInstance());
11            System.out.println(je.getStepExecutions());
12        catch (Exception e) {
13            e.printStackTrace();
14        }
15    }

输出:

0110-20 15:28:32 INFO [job.SimpleStepHandler] - <Executing step: [messageStep]>
02write results
03Hello User1,please pay promptly at end of this month.
04Hello User2,please pay promptly at end of this month.
05Hello User3,please pay promptly at end of this month.
06Hello User4,please pay promptly at end of this month.
07Hello User5,please pay promptly at end of this month.
08write results
09Hello User6,please pay promptly at end of this month.
10Hello User7,please pay promptly at end of this month.
11Hello User8,please pay promptly at end of this month.
12Hello User9,please pay promptly at end of this month.
13Hello User10,please pay promptly at end of this month.
1410-20 15:28:32 INFO [support.SimpleJobLauncher] - <Job: [FlowJob: [name=messageJob]] completed with the following parameters: [{run.month=2011-10}] and the following status: [COMPLETED]>
15JobExecution: id=0, version=2, startTime=Sat Oct 20 15:28:32 CST 2012, endTime=Sat Oct 20 15:28:32 CST 2012, lastUpdated=Sat Oct 20 15:28:32 CST 2012, status=COMPLETED, exitStatus=exitCode=COMPLETED;exitDescription=, job=[JobInstance: id=0, version=0, JobParameters=[{run.month=2011-10}], Job=[messageJob]]
16JobInstance: id=0, version=0, JobParameters=[{run.month=2011-10}], Job=[messageJob]
17[StepExecution: id=1, version=5, name=messageStep, status=COMPLETED, exitStatus=COMPLETED, readCount=10, filterCount=0, writeCount=10 readSkipCount=0, writeSkipCount=0, processSkipCount=0, commitCount=3, rollbackCount=0, exitDescription=]

从日志里,我们可以清楚的看到.他是每行的读取并送入Processor中处理,完成5次读取进行一次性的写入.tasklet的属性 commit-interval可以调节此值.

全部的Spring配置:

01<batch:job id="messageJob" restartable="true">
02        <batch:step id="messageStep">
03            <batch:tasklet>
04                <batch:chunk reader="messageReader" 
05                processor="messageProcessor" 
06                writer="messageWriter"
07                commit-interval="5" 
08                chunk-completion-policy="" 
09                retry-limit="2">
10                    <batch:retryable-exception-classes>
11                        <batch:include class="java.lang.RuntimeException" />
12                    </batch:retryable-exception-classes>
13                </batch:chunk>
14            </batch:tasklet>
15        </batch:step>
16    </batch:job>
### Spring Batch 框架批处理任务开发指南 Spring Batch 是一个强大的批处理框架,适用于处理大量数据的自动化任务。它提供了丰富的组件和高可靠性、高扩展性的能力,使得开发者能够专注于业务逻辑的实现,而无需过多关注底层的技术细节。 #### 核心概念 - **Job** 是 Spring Batch 中的一个核心概念,用于封装整个批处理过程。`Job` 接口是整个批处理过程的顶层抽象,它定义了批处理任务的基本行为,如执行任务、获取任务名称、检查任务是否可重启等。`Job` 接口的具体实现通常由框架提供,开发者只需要根据需求配置相应的参数即可[^4]。 - **Step** 是 `Job` 的组成部分,每个 `Job` 可以包含多个 `Step`。每个 `Step` 代表一个具体的处理步骤,例如读取数据、处理数据、写入数据等。`Step` 的设计允许开发者将复杂的批处理任务分解为多个简单的步骤,从而提高任务的可维护性和可测试性。 - **ItemReader**、**ItemProcessor** 和 **ItemWriter** 是 Spring Batch 提供的三个核心接口,分别用于读取数据、处理数据和写入数据。开发者可以通过实现这些接口来定义自己的数据处理逻辑。例如,`ItemReader` 可以从数据库、文件或其他数据源读取数据,`ItemProcessor` 可以对读取的数据进行转换或计算,`ItemWriter` 可以将处理后的数据写入目标存储。 #### 配置与启动 - **配置 Job** 在 Spring Batch 中,`Job` 的配置通常通过 XML 配置文件或 Java 注解来完成。开发者可以使用 `@EnableBatchProcessing` 注解来启用批处理功能,并通过 `JobBuilderFactory` 和 `StepBuilderFactory` 来构建 `Job` 和 `Step`。例如: ```java @Configuration @EnableBatchProcessing public class BatchConfig { @Autowired private JobBuilderFactory jobBuilderFactory; @Autowired private StepBuilderFactory stepBuilderFactory; @Bean public Job myJob(Step myStep) { return jobBuilderFactory.get("myJob") .start(myStep) .build(); } @Bean public Step myStep(ItemReader<String> reader, ItemProcessor<String, String> processor, ItemWriter<String> writer) { return stepBuilderFactory.get("myStep") .<String, String>chunk(10) .reader(reader) .processor(processor) .writer(writer) .build(); } } ``` 上述代码展示了如何通过 Java 配置类定义一个简单的 `Job` 和 `Step`。`Job` 包含一个 `Step`,该 `Step` 使用 `ItemReader` 读取数据,`ItemProcessor` 处理数据,`ItemWriter` 写入数据。`chunk(10)` 表示每次处理 10 条数据。 - **启动 Job** `Job` 的启动可以通过 `JobLauncher` 来完成。`JobLauncher` 是一个接口,提供了启动 `Job` 的方法。开发者可以通过 `CommandLineJobRunner` 或者 `JobOperator` 来启动 `Job`。例如: ```java @Component public class JobLauncherRunner implements CommandLineRunner { @Autowired private JobLauncher jobLauncher; @Autowired private Job myJob; @Override public void run(String... args) throws Exception { JobParameters jobParameters = new JobParametersBuilder() .addString("JobID", String.valueOf(System.currentTimeMillis())) .toJobParameters(); jobLauncher.run(myJob, jobParameters); } } ``` 上述代码展示了如何通过 `CommandLineRunner` 启动 `Job`。`JobParameters` 用于传递任务的参数,`JobLauncher` 会根据这些参数启动 `Job`。 #### 数据库支持 - **JDBC 批处理模式** Spring Batch 支持多种数据存储方式,包括关系型数据库、NoSQL 数据库等。对于关系型数据库,Spring Batch 提供了 JDBC 批处理模式的支持。开发者可以通过配置 `spring.batch.jdbc.initialize-schema` 和 `spring.batch.job.enabled` 等属性来启用 JDBC 批处理模式。例如: ```properties spring.batch.jdbc.initialize-schema=never spring.batch.job.enabled=false spring.jpa.open-in-view=false ``` 上述配置禁用了自动初始化数据库模式,并关闭了默认的批处理任务。开发者可以根据实际需求调整这些配置。 - **JVM 优化** 为了提高批处理任务的性能,开发者还可以对 JVM 进行优化。例如,启用字符串去重、使用压缩指针、限制元空间大小等。这些优化可以通过 JVM 参数来实现: ```bash -XX:+UseStringDeduplication -XX:+UseCompressedOops -XX:MaxMetaspaceSize=512m ``` 上述参数启用了字符串去重功能,减少了内存占用;使用压缩指针,降低了对象头的大小;限制了元空间的最大大小为 512MB,防止元空间无限增长。 #### 定时任务集成 - **结合定时任务框架** 虽然 Spring Batch 本身是一个定时任务框架,但它提供了对任务执行时间的控制。开发者可以将 Spring Batch 与定时任务框架(如 Quartz、Spring Scheduler)结合使用,实现定时触发批处理任务的功能。例如: ```java @Component public class ScheduledJobLauncher { @Autowired private JobLauncher jobLauncher; @Autowired private Job myJob; @Scheduled(cron = "0 0 0 * * ?") // 每天凌晨执行 public void launchJob() throws Exception { JobParameters jobParameters = new JobParametersBuilder() .addString("JobID", String.valueOf(System.currentTimeMillis())) .toJobParameters(); jobLauncher.run(myJob, jobParameters); } } ``` 上述代码展示了如何通过 `@Scheduled` 注解定时触发 `Job` 的执行。`cron` 表达式 `0 0 0 * * ?` 表示每天凌晨 0 点执行任务。 #### 异常处理与重试机制 - **异常处理** 在批处理任务中,异常处理是非常重要的。Spring Batch 提供了多种异常处理机制,开发者可以通过 `SkipPolicy` 和 `RetryPolicy` 来处理异常。例如,`SkipPolicy` 允许开发者指定哪些异常可以跳过,`RetryPolicy` 允许开发者指定哪些异常可以重试。 ```java @Bean public Step myStep(ItemReader<String> reader, ItemProcessor<String, String> processor, ItemWriter<String> writer) { return stepBuilderFactory.get("myStep") .<String, String>chunk(10) .reader(reader) .processor(processor) .writer(writer) .faultTolerant() .skipLimit(10) .skip(Exception.class) .retryLimit(3) .retry(Exception.class) .build(); } ``` 上述代码展示了如何配置 `SkipPolicy` 和 `RetryPolicy`。`skipLimit(10)` 表示最多跳过 10 次异常,`skip(Exception.class)` 表示跳过所有 `Exception` 类型的异常;`retryLimit(3)` 表示最多重试 3 次,`retry(Exception.class)` 表示重试所有 `Exception` 类型的异常。 - **重启机制** Spring Batch 还支持任务的重启机制。如果任务在执行过程中失败,开发者可以通过 `JobParameters` 中的 `JobID` 来重启任务。重启时,Spring Batch 会从上次失败的位置继续执行,而是从头开始。 ```java @Override public void run(String... args) throws Exception { JobParameters jobParameters = new JobParametersBuilder() .addString("JobID", "123456789") .toJobParameters(); jobLauncher.run(myJob, jobParameters); } ``` 上述代码展示了如何通过 `JobParameters` 中的 `JobID` 来重启任务。`JobID` 是一个唯一的标识符,用于标识任务的执行实例。 #### 监控与日志 - **监控任务执行状态** Spring Batch 提供了多种方式来监控任务的执行状态。开发者可以通过 `JobExplorer` 和 `JobRepository` 来查询任务的执行信息。例如: ```java @Component public class JobMonitor { @Autowired private JobExplorer jobExplorer; @Autowired private JobRepository jobRepository; public void monitorJob(String jobName) { List<JobInstance> jobInstances = jobExplorer.findJobInstancesByJobName(jobName, 0, 10); for (JobInstance jobInstance : jobInstances) { List<JobExecution> jobExecutions = jobExplorer.getJobExecutions(jobInstance); for (JobExecution jobExecution : jobExecutions) { System.out.println("Job Name: " + jobExecution.getJobInstance().getJobName()); System.out.println("Job ID: " + jobExecution.getJobId()); System.out.println("Start Time: " + jobExecution.getStartTime()); System.out.println("End Time: " + jobExecution.getEndTime()); System.out.println("Status: " + jobExecution.getStatus()); } } } } ``` 上述代码展示了如何通过 `JobExplorer` 查询任务的执行信息。`findJobInstancesByJobName` 方法用于查找指定任务名称的任务实例,`getJobExecutions` 方法用于获取任务实例的所有执行记录。 - **日志记录** Spring Batch 支持详细的日志记录,开发者可以通过日志文件来跟踪任务的执行情况。日志记录可以通过 `org.springframework.batch.core.launch.support.CommandLineJobRunner` 或 `org.springframework.batch.core.job.AbstractJob` 中的日志输出来实现。此外,开发者还可以使用第三方日志框架(如 Logback、Log4j)来增强日志记录功能。 #### 总结 Spring Batch 是一个功能强大且灵活的批处理框架,适用于处理大量数据的自动化任务。通过 `Job` 和 `Step` 的设计,开发者可以轻松地构建复杂的批处理流程。同时,Spring Batch 还提供了丰富的配置选项和扩展机制,支持多种数据存储方式、定时任务集成、异常处理、重启机制以及监控与日志记录等功能。开发者可以根据实际需求选择合适的功能组合,构建高效、可靠的批处理应用。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值