æ¬è¨äºã¯
ããã°æ¸ãåãã¦ã£ã¼ã¯
6æ¥ç®ã®è¨äºã§ãã
ð
5æ¥ç®
â¶â¶ æ¬è¨äº â¶â¶
7æ¥ç®
ð

- ã¯ããã«
- ãããããããããã£ã¦ãªã«ï¼
- Spring Batchã®åºæ¬æ§æã¨ã¯ï¼
- Spring Batchã§ããããå®è£ ãã¦ãã
- â ãã¼ãã«ã¨CSVãã¡ã¤ã«ã使ãã
- â¡ Modelã使ãã
- ⢠ItemProcessorãå®è£ ãã
- ⣠ã¸ã§ããã¾ã¨ããã¯ã©ã¹ãå®è£ ãã
- ⤠éç¥ãåãåããªã¹ãã¼ãå®è£ ãã
- ⥠ã¡ã¤ã³ã¯ã©ã¹ãå®è£ ãã
- ⦠å®è¡çµæã確èªãã
- ãããã«
ã¯ããã«
ããã«ã¡ã¯ï¼å ¥ç¤¾1å¹´ç®ã®æ± ä¸ã§ãã æ°å ¥ç¤¾å¡ç ä¿®ãçµããé¨ç½²ã«é å±ããã¦ããç´åå¹´ãçµã¡ã¾ããã ç¾å¨åç»ãã¦ããããã¸ã§ã¯ãã§ã¯ãéçºã§Spring Batchã使ç¨ãã¦ããã®ã§ãããç§èªèº«ãããã«ã¤ãã¦ã¯ç¥èãã»ã¼ã¼ãã®ç¶æ ããã®ã¹ã¿ã¼ãã§ããã ã§ãããæ¥åãéãã¦ããããã©ããªå½¹å²ãæ ãããã¯ã¨ã³ãã§ã¯ã©ãåãã¦ãã®ããç¥ããããã«å®éã«ã³ã¼ããæ¸ãã¦Spring Batchã®æ§é ãçè§£ãããã¨ãã§ããã®ã§ãã¢ã¦ããããããå 容ããã¡ãã®è¨äºã«ã¾ã¨ãã¾ããï¼
ãããããããããã£ã¦ãªã«ï¼
ãããå¦çã¨ããè¨èãèãããã¨ã¯ãããã©ãå
·ä½çã«ä½ãæãã¦ããã®ãããããããªãã¨ããæ¹ãå¤ãã®ã§ã¯ãªãã§ããããã
ãããå¦çã¨ã¯ããããããå®è¡ãããæéãå¦çæ¡ä»¶ãäºåã«ç»é²ãã¦ãããèªåçã«ãã®å¦çã䏿¬ã§è¡ããã¨ã§ãã
ä¾ãã°ãï¼æ¥åã®å£²ä¸ãã¼ã¿ãªã©ã䏿¬ã§éè¨ãããã¼ã¿ãã¼ã¹ã«ç»é²ããã¾ã§ãèªååã§ãã¾ãã

ãã®ããã«ãããå¦çã¯ä¸åº¦ã«ã¾ã¨ãã¦å¦çãè¡ããããå¹ççã«å¤§éã®ãã¼ã¿ãæ±ããã¨ãã§ãã¾ãã ããã¦ãjavaã§ãããå¦çãéçºããéã«ä¸è¬çã«ä½¿ç¨ããããã¬ã¼ã ã¯ã¼ã¯ãSpring Batchã«ãªãã¾ãã 以éã§ãã®Spring Batchã«ã¤ãã¦ã®åºæ¬æ§æãã³ã¼ããæ¸ãã¦å®éã«ããããåããã¦ã¿ãçµæãã¾ã¨ãã¾ããã
Spring Batchã®åºæ¬æ§æã¨ã¯ï¼
ã§ã¯ãSpring Batchã®å
¨ä½æ¦è¦ã«ã¤ãã¦èª¬æãã¦ããã¾ãã
ä¸å³ã®ããã«ãSpring Batchã¯ãJob RauncherãJobãStepãTaskletãChunkãJob RepositoryãPlatform Transaction Managerãçµã¿åããã¦Jobãå®ç¾©ãã¾ãã
ãããã¯ãããå¦çãå¹ççã«è¡ãããã«æä¾ãããåå©ç¨å¯è½ãªã³ã³ãã¼ãã³ãããã¼ã«ã®ãã¨ã§ããããã®å
±éé¨åã使ç¨ãããã¨ã§ãéçºè
ã¯ä¸ãããã¹ã¦ãå®è£
ããå¿
è¦ããªããªãã¾ãã

JobRauncher
Jobãèµ·åããããã®æ©è½ã§ããã¸ã§ãå ¨ä½ã®æåã¾ãã¯å¤±æãå¤å®ãã¾ãã
Job
ãããå¦çãã®ãã®ãæãã¾ãã売ä¸ãã¼ã¿ã®åãè¾¼ã¿ãã¬ãã¼ãã®ä½æãªã©1ã¤ã®Jobã1ã¤ã®ãããå¦çã¨ããåä½ã«ãªãã¾ãã
Step
Jobå ã§å®è¡ãããå¦çã®åä½ã§ããStepããå¼ã³åºãå®è£ æ¹å¼ã¨ãã¦ãTaskletã¢ãã«ãChunkã¢ãã«ãããã¾ãã
Taskletã¢ãã«
å ·ä½çãªå¦çå 容ãè¨è¿°ããããå®éã«å¦çãå®è£ ãããå ´æã§ããTaskletã®1ã¤ã«éç´ãã¾ãã
Chunkã¢ãã«
Taskletã¨ã¯ç°ãªãChunkã¯ä¸è¨3ã¤ãçµã¿åããã¦ãã¼ã¿ã®å¦çãè¡ãå ´æã§ããä¸é£ã®ãã¬ã¼ã ã¯ã¼ã¯ã¨ãã¦æä¾ããã¦ããããããã®ã¤ã³ã¿ã¼ãã§ã¼ã¹ãå®è£ ããã¯ã©ã¹ã使ãããã¨ã§ããããå®åå¦çã使ãããã¨ãã§ãã¾ãã
Item Reader ï¼ ãã¡ã¤ã«ããã¼ã¿ãã¼ã¹ã®æ å ±ãèªã¿è¾¼ã¿ã¾ãã
Item Processor ï¼ èªã¿è¾¼ãã ãã¼ã¿ã®å¦çãè¡ãã¾ãã
Item Writer ï¼ å¦çãããã¼ã¿ã®æ¸ãè¾¼ã¿ãè¡ãã¾ãã
Job Repository
Jobã®å®è¡å±¥æ´ãä¿åããããã®ãã®ã§ãããã¼ã¿ãã¼ã¹ã«ã¯ãJobã®å®è¡éå§æéãçµäºæéãJobã®å¤å®çµæãã¹ãã¼ã¿ã¹ãå®è¡ãã©ã¡ã¼ã¿ãªã©ãä¿åãã¾ãã
Platform Transaction Manager
ãã©ã³ã¶ã¯ã·ã§ã³ç®¡çãè¡ãå ´æã§ãããã¼ã¿ã®æ´åæ§ãä¿ã¤ããã«ããã©ã³ã¶ã¯ã·ã§ã³ã®å¢çãå®ç¾©ãã¦ãã¼ã«ããã¯ã³ããããå¶å¾¡ãã¾ãã
Spring Batchã§ããããå®è£ ãã¦ãã
ã§ã¯å®éã«ãä¸è¨ã®æ§æãè¸ã¾ãã¦ããããå®è£ ãã¦ããã¾ãã ä»åã¯ãSpringã®å ¬å¼ããã¥ã¡ã³ããåèã«ãCSVãã¡ã¤ã«ãã売ä¸ãã¼ã¿ãèªã¿è¾¼ãã§ãã¼ã¿ãã¼ã¹ã¸ç»é²ããçµæããã°ã¨ãã¦åºåããããããå®è£ ãã¾ããã
å®è¡ç°å¢
- InteliJ IDEA
- Java17 (coretto)
- Spring Boot 3.4.1
- Gradle8.8
- mysql
â ãã¼ãã«ã¨CSVãã¡ã¤ã«ã使ãã
ã¢ããªã±ã¼ã·ã§ã³èµ·åæã«CSVãã¡ã¤ã«ãèªã¿è¾¼ãããããå®è£ ãã¦ããã¾ãã ã¾ãã売ä¸ãã¼ã¿ãå ¥ã£ãCSVãã¡ã¤ã«ã使ãã¾ãã ããã«ãèªã¿è¾¼ãã 売ä¸ãã¼ã¿ãæ ¼ç´ãã売ä¸ãã¼ãã«ã使ãã¦ããã¾ãã
salesdata.csv
ããã,180 ã¿ãã,150 ãã,300 ã¶ã©ã,500 ã¡ãã³,700
table.sql
CREATE TABLE sales ( sales_id INT IDENTITY NOT NULL PRIMARY KEY, fruit VARCHAR(20), price BIGINT );
â¡ Modelã使ãã
ãã¼ãã«ã«å¯¾å¿ããã¢ãã«ã使ãã¾ãã
SalesData.java
package com.example.batchprocessing; public record SalesData(String fruit, int price) { }
⢠ItemProcessorãå®è£ ãã
ItemProcessor.java
package com.example.batchprocessing; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.batch.item.ItemProcessor; public class SalesDataProcessor implements ItemProcessor<SalesData, SalesData> { private static final Logger log = LoggerFactory.getLogger(SalesDataProcessor.class); private int totalSales = 0; @Override public SalesData process(final SalesData data) { // 売ä¸ãã¼ã¿ã®åè¨ãè¨ç® totalSales += data.price(); return data; } public int getTotalSales() { return totalSales; } }
ä»åã¯Chunkã¢ãã«ã使ç¨ãããããå¦çãå®è£ ãã¦ããã¾ãã åè¿°ã®éããChunkã¢ãã«ã¯ãã¼ã¿ã®èªã¿è¾¼ã¿ãå å·¥ãæ¸ãè¾¼ã¿ã®3ã¤ã®å¦çã«åå²ããã¾ãã ãã¼ã¿ã®å¦çãæ ãItemProcessorã§ã¯ããã¡ã¤ã«ããèªã¿åã£ã売ä¸ãã¼ã¿ã®åè¨ãè¨ç®ãããã©ã³ã¹ãã©ã¼ãã¼ã使ãã¾ããã
⣠ã¸ã§ããã¾ã¨ããã¯ã©ã¹ãå®è£ ãã
BatchConfig.java
package com.example.batchprocessing; import javax.sql.DataSource; import org.springframework.batch.core.Job; import org.springframework.batch.core.Step; import org.springframework.batch.core.job.builder.JobBuilder; import org.springframework.batch.core.repository.JobRepository; import org.springframework.batch.core.step.builder.StepBuilder; import org.springframework.batch.item.database.JdbcBatchItemWriter; import org.springframework.batch.item.database.builder.JdbcBatchItemWriterBuilder; import org.springframework.batch.item.file.FlatFileItemReader; import org.springframework.batch.item.file.builder.FlatFileItemReaderBuilder; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.core.io.ClassPathResource; import org.springframework.jdbc.datasource.DataSourceTransactionManager; @Configuration public class BatchConfig { @Bean public FlatFileItemReader<SalesData> reader() { return new FlatFileItemReaderBuilder<SalesData>() .name("salesDataReader") .resource(new ClassPathResource("salesdata.csv")) .delimited() .names("fruit", "price") .targetType(SalesData.class) .build(); } @Bean public SalesDataProcessor processor() { return new SalesDataProcessor(); } @Bean public JdbcBatchItemWriter<SalesData> writer(DataSource dataSource) { return new JdbcBatchItemWriterBuilder<SalesData>() .sql("INSERT INTO sales (fruit, price) VALUES (:fruit, :price)") .dataSource(dataSource) .beanMapped() .build(); }
Configã¯ã©ã¹ã¯ãã¸ã§ããã¾ã¨ãããããè¨å®ç¨ã¯ã©ã¹ã§ããItemReaderãItemProcessorãItemWriterã®å¦çãè¨è¿°ãã¾ãã
reader()ã§ã¯â ã§ä½æããCSVãã¡ã¤ã«ãèªã¿è¾¼ã¿ããã¼ã¿ãã¼ã¹ã«ç»é²ã§ããããSalesDataåã«å¤æãã¾ãã
processor()ã§ã¯ãâ¢ã§å®ç¾©ããSalesDataProcessorã¤ã³ã¹ã¿ã³ã¹ã使ãã¾ãã
æå¾ã«writer(Datasource dataSource)ã§ããã¼ã¿ãã¼ã¹ã¸ã®æ¸ãè¾¼ã¿ãè¡ãã¾ãã
ãããã®å¦çã¯@Beanã¢ããã¼ã·ã§ã³ã使ç¨ãå®ç¾©ããã¦ãã¾ãã
@Beanã¢ããã¼ã·ã§ã³ã使ç¨ãããã¨ã§ãã¡ã½ãããè¿ããªãã¸ã§ã¯ããã³ã³ããã«ç»é²ããã¾ããããã«ã@Configurationã¢ããã¼ã·ã§ã³ã§ã¢ããªã±ã¼ã·ã§ã³å
¨ä½ã«ããã¦åå©ç¨å¯è½ãªè¨å®ãä¸å
管çã§ãã¾ãã
@Bean public Job importUserJob(JobRepository jobRepository, Step step, JobCompletionNotificationListener listener) { return new JobBuilder("importUserJob", jobRepository) .listener(listener) .start(step) .build(); } @Bean public Step step(JobRepository jobRepository, DataSourceTransactionManager transactionManager, FlatFileItemReader<SalesData> reader, SalesDataProcessor processor, JdbcBatchItemWriter<SalesData> writer) { return new StepBuilder("step1", jobRepository) .<SalesData, SalesData>chunk(3, transactionManager) .reader(reader) .processor(processor) .writer(writer) .build(); } }
åä¸ãã¡ã¤ã«å
ã§Jobã¨Stepãå®ç¾©ãã¦ãã¾ãã
importUserJob()ã§ã¯ãJobRepositoryãã¸ã§ãã®å®è¡ç¶æ
ã管çããã¸ã§ãã®éå§ãçµäºæã«ãªã¹ãã¼ãéãã¦éç¥ãè¡ãã¾ãã
step()ã§ã¯ããã¡ã¤ã«ãããã¼ã¿ãèªã¿åããå¦çãããã¼ã¿ãã¼ã¹ã«æ¸ãè¾¼ãä¸é£ã®ãããå¦çãè¡ãã¾ãã
ããã«ãDataSourceTransactionManagerãStepBuilderã«æ¸¡ãããã¹ãããå
ã®ãã¹ã¦ã®ãã¼ã¿ãã¼ã¹æä½ããã©ã³ã¶ã¯ã·ã§ã³ã¨ãã¦ç®¡çããã¦ãã¾ãã
⤠éç¥ãåãåããªã¹ãã¼ãå®è£ ãã
JobCompletionNotificationListener.java
package com.example.batchprocessing; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.batch.core.BatchStatus; import org.springframework.batch.core.JobExecution; import org.springframework.batch.core.JobExecutionListener; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.jdbc.core.DataClassRowMapper; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.stereotype.Component; @Component public class JobCompletionNotificationListener implements JobExecutionListener { private static final Logger log = LoggerFactory.getLogger(JobCompletionNotificationListener.class); private final SalesDataProcessor processor; private final JdbcTemplate jdbcTemplate; @Autowired public JobCompletionNotificationListener(JdbcTemplate jdbcTemplate, SalesDataProcessor processor) { this.jdbcTemplate = jdbcTemplate; this.processor = processor; } @Override public void afterJob(JobExecution jobExecution) { if (jobExecution.getStatus() == BatchStatus.COMPLETED) { log.info("---- 20XXå¹´XXæXXæ¥ã®å£²ä¸ãã¼ã¿ãéè¨ãã¾ããã----"); jdbcTemplate .query("SELECT fruit, price FROM sales", new DataClassRowMapper<>(SalesData.class)) .forEach(sales -> log.info("Found <{}> in the database.", sales)); log.info("Total sales: " + processor.getTotalSales()); } } }
JobExecutionListenerã¨ã¯ãã¸ã§ãã®éå§åã¨çµäºå¾ã«ç¹å®ã®å¦çãå®è¡ããããã®ã¡ã½ãããæä¾ããã¤ã³ã¿ã¼ãã§ã¼ã¹ã§ãã
ã¸ã§ããæ£å¸¸ã«å®äºããå ´åï¼BatchStatus.COMPLETEDï¼ã以ä¸ã®å¦çãè¡ãããå®è£
ãã¾ããã
JdbcTemplateã使ã£ã¦ãã¼ã¿ãã¼ã¹ãã売ä¸ãã¼ã¿ãã¯ã¨ãªããåã¬ã³ã¼ãããã°ã«åºåSalesDataProcessorããç·å£²ä¸ãåå¾ãããã°ã«åºå
⥠ã¡ã¤ã³ã¯ã©ã¹ãå®è£ ãã
BatchProcessingApplication.java
package com.example.batchprocessing; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class BatchProcessingApplication { public static void main(String[] args) { System.exit(SpringApplication.exit(SpringApplication.run(BatchProcessingApplication.class, args))); } }
Spring Bootã¢ããªã±ã¼ã·ã§ã³ãèµ·åããããã®ã¡ã¤ã³ã¯ã©ã¹ã§ãã
@SpringBootApplicationã¢ããã¼ã·ã§ã³ã¯ã@Configurationã@EnableAutoConfigurationã@ComponentScanã®3ã¤ã®ã¢ããã¼ã·ã§ã³ãçµã¿åããããã®ã§ãã
@EnableAutoConfigurationã¨ã¯ãSpring Bootãã¢ããªã±ã¼ã·ã§ã³ã®ä¾åé¢ä¿ã«åºã¥ãã¦é©åãªæ§æãèªåçã«è¡ãããã®ã¢ããã¼ã·ã§ã³ã§ãã
@ComponentScanã¨ã¯ãæå®ãããããã±ã¼ã¸å
ã®ã³ã³ãã¼ãã³ããèªåçã«æ¤åºããSpringã³ã³ããã«ç»é²ããããã®ãã®ã§ãããã«ããã@Beanã®å®ç¾©ãä¸è¦ã«ãªãã¾ãã
⦠å®è¡çµæã確èªãã
2025-01-17T17:34:40.570+09:00 INFO 4684 --- [ main] c.e.b.BatchProcessingApplication : Started BatchProcessingApplication in 2.282 seconds (process running for 2.884)
2025-01-17T17:34:40.573+09:00 INFO 4684 --- [ main] o.s.b.a.b.JobLauncherApplicationRunner : Running default command line with: []
2025-01-17T17:34:40.633+09:00 INFO 4684 --- [ main] o.s.b.c.l.s.TaskExecutorJobLauncher : Job: [SimpleJob: [name=importUserJob]] launched with the following parameters: [{}]
2025-01-17T17:34:40.662+09:00 INFO 4684 --- [ main] o.s.batch.core.job.SimpleStepHandler : Executing step: [step]
2025-01-17T17:34:40.705+09:00 INFO 4684 --- [ main] o.s.batch.core.step.AbstractStep : Step: [step] executed in 40ms
2025-01-17T17:34:40.712+09:00 INFO 4684 --- [ main] c.e.b.JobCompletionNotificationListener : ---- 20XXå¹´XXæXXæ¥ã®å£²ä¸ãã¼ã¿ãéè¨ãã¾ããã----
2025-01-17T17:34:40.715+09:00 INFO 4684 --- [ main] c.e.b.JobCompletionNotificationListener : Found <SalesData[fruit=ããã, price=180]> in the database.
2025-01-17T17:34:40.720+09:00 INFO 4684 --- [ main] c.e.b.JobCompletionNotificationListener : Found <SalesData[fruit=ã¿ãã, price=150]> in the database.
2025-01-17T17:34:40.720+09:00 INFO 4684 --- [ main] c.e.b.JobCompletionNotificationListener : Found <SalesData[fruit=ãã, price=300]> in the database.
2025-01-17T17:34:40.720+09:00 INFO 4684 --- [ main] c.e.b.JobCompletionNotificationListener : Found <SalesData[fruit=ã¶ã©ã, price=500]> in the database.
2025-01-17T17:34:40.720+09:00 INFO 4684 --- [ main] c.e.b.JobCompletionNotificationListener : Found <SalesData[fruit=ã¡ãã³, price=700]> in the database.
2025-01-17T17:34:40.722+09:00 INFO 4684 --- [ main] c.e.b.JobCompletionNotificationListener : Total sales: 1830
2025-01-17T17:34:40.725+09:00 INFO 4684 --- [ main] o.s.b.c.l.s.TaskExecutorJobLauncher : Job: [SimpleJob: [name=importUserJob]] completed with the following parameters: [{}] and the following status: [COMPLETED] in 66ms
2025-01-17T17:34:40.732+09:00 INFO 4684 --- [ main] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Shutdown initiated...
2025-01-17T17:34:40.735+09:00 INFO 4684 --- [ main] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Shutdown completed.
ã³ã³ã½ã¼ã«ã«åºåããããã°ã確èªããã¨ãCSVãã¡ã¤ã«ã®æ å ±ããã¼ã¿ãã¼ã¹ã«ç»é²ããéè¨çµæãæ£ç¢ºã«ãã°ã¨ãã¦åºåããã¦ãããããæ£å¸¸ã«ããããå®è¡ããããã¨ãåããã¾ãã
ãããã«
ä»åã¯Spring Batchã«ã¤ãã¦ã®æ§æãå®éã«ã³ã¼ããæ¸ãã¦ããããåããã¦ããæµãããç´¹ä»ãã¾ããã Spring Batchã¯ããããå¦çãå¹ççã«è¡ãããã«å¤ãã®å ±éé¨åãæä¾ããã¦ããããããã使ç¨ãããã¨ã§åãå¦çãä½åº¦ãæ¸ãå¿ è¦ããªããªãã¨ãããã¨ãåããã¾ããã ãã®è¨äºãéãã¦ãçãããSpring Batchã«ã¤ãã¦çè§£ãæ·±ããä¸å©ã¨ãªãã°å¹¸ãã§ãã ä»åã®ãããã®å®è£ ãããã°å·çãéãã¦ãæ¹ãã¦ã¢ã¦ããããããå 容ãä»å¾ã®æ¥åã«ãæ´»ããã¦ããããã¨æãã¾ãã