ããã¯ããªã«ããããã¦æ¸ãããã®ï¼
Spring Batchã¨Bean Validationãçµã¿åããã¦ãItemã®ããªãã¼ã·ã§ã³ãããããNGã«ãªã£ãItemãã¹ãããã§ãããããã¨ããã®ã§
ã¡ãã£ã¨è©¦ãã¦ã¿ããããªã¨ã
Spring Batchã¨Bean Validation
Spring Batchã§Bean Validationã使ãããã¨ã¯ããã¥ã¡ã³ãã«æ¸ããã¦ããããã§ãããItemProcessor
ã®ç¨éã®ã²ã¨ã¤ã¨ãã¦ç»å ´ãã¾ãã
Item processing / Validating Input
BeanValidatingItemProcessor
ã¨ããItemProcessor
ãæä¾ããã¦ããã®ã§ããã¡ãã使ã£ã¦å®ç¾ããããã§ããã
BeanValidatingItemProcessor (Spring Batch 4.3.5 API)
Springã®ããªãã¼ã·ã§ã³ã使ãå ´åã¯ããã¡ãã®ããã§ãã
ValidatingItemProcessor (Spring Batch 4.3.5 API)
ããªãã¼ã·ã§ã³ã§NGã«ãªãã¨ä¾å¤ãã¹ãã¼ãããã®ã§ããã®ã¾ã¾ã ã¨ã¢ããªã±ã¼ã·ã§ã³ãåæ¢ããããã§ãã
åå ã¨ãªã£ãItem
ãã¹ãããããã«ã¯ãStep
ã§è¨å®ããããã§ãã
Configuring a Step / Chunk-oriented Processing / Configuring Skip Logic
ä»åã¯ããã®ãããã試ãã¦ã¿ã¾ãã
ãé¡
ä»åã®ãé¡ã¯ã以ä¸ã¨ãã¾ãã
- æ¸ç±ãã¼ã¿ã®CSVãèªã¿è¾¼ã¿ãèªã¿è¾¼ãã ãã¼ã¿ãæ¨æºåºåã«æ¸ãåºãChunkå½¢å¼ã®
Job
ããã³Step
ãä½æãã - Bean Validationãè¡ã
itemProcessor
ã追å ããã¨ã©ã¼ã«ãªãItem
ãå«ãã CSVãå ¥åãã - ã¾ãã¯ããªãã¼ã·ã§ã³NGã«ãªã£ã¦åæ¢ããã¨ããã確èªãã次ã«ã¨ã©ã¼ã«ãªã£ã
Item
ãã¹ãããããè¨å®ãè¡ã
ãããªæãã§ãã£ã¦ããã¾ãã
ç°å¢
ä»åã®ç°å¢ã¯ããã¡ãã
$ java --version openjdk 17.0.3 2022-04-19 OpenJDK Runtime Environment (build 17.0.3+7-Ubuntu-0ubuntu0.20.04.1) OpenJDK 64-Bit Server VM (build 17.0.3+7-Ubuntu-0ubuntu0.20.04.1, mixed mode, sharing) $ mvn --version Apache Maven 3.8.5 (3599d3414f046de2324203b78ddcf9b5e4388aa0) Maven home: $HOME/.sdkman/candidates/maven/current Java version: 17.0.3, vendor: Private Build, runtime: /usr/lib/jvm/java-17-openjdk-amd64 Default locale: ja_JP, platform encoding: UTF-8 OS name: "linux", version: "5.4.0-109-generic", arch: "amd64", family: "unix"
JobRepository
ã®ãã¼ã¿ã¯ãMySQLã«æ ¼ç´ãããã¨ã«ãã¾ãããã¼ã¸ã§ã³ã¯ä»¥ä¸ã§ã172.17.0.2
ã§åä½ãã¦ãããã®ã¨ãã¾ãã
$ mysql --version mysql Ver 8.0.28 for Linux on x86_64 (MySQL Community Server - GPL)
æºå
ã§ã¯ãSpring Bootããã¸ã§ã¯ããä½æãã¦ããã¾ãã
ä¾åé¢ä¿ã«batch
ãvalidation
ãmysql
ãæå®ãã¦ãä½æã
$ curl -s https://start.spring.io/starter.tgz \ -d bootVersion=2.6.7 \ -d javaVersion=17 \ -d name=batch-beanvalidation \ -d groupId=org.littlewings \ -d artifactId=batch-beanvalidation \ -d version=0.0.1-SNAPSHOT \ -d packageName=org.littlewings.spring.batch \ -d dependencies=batch,validation,mysql \ -d baseDir=batch-beanvalidation | tar zxvf -
ããã¸ã§ã¯ãå ã«ç§»åã
$ cd batch-beanvalidation
èªåçæãããã½ã¼ã¹ã³ã¼ãã¯ãåé¤ãã¦ããã¾ãã
$ rm src/main/java/org/littlewings/spring/batch/BatchBeanvalidationApplication.java src/test/java/org/littlewings/spring/batch/BatchBeanvalidationApplicationTests.java
Mavenã®ä¾åé¢ä¿ããã³ãã©ã°ã¤ã³ã®è¨å®ã¯ããã¡ãã
<properties> <java.version>17</java.version> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-batch</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-validation</artifactId> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <scope>runtime</scope> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> <dependency> <groupId>org.springframework.batch</groupId> <artifactId>spring-batch-test</artifactId> <scope>test</scope> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build>
ç¶ãã¦ãã½ã¼ã¹ã³ã¼ããä½æãã¦ããã¾ãã
Item
ç¸å½ã®ã¯ã©ã¹ã
src/main/java/org/littlewings/spring/batch/Book.java
package org.littlewings.spring.batch; import javax.validation.constraints.Min; import javax.validation.constraints.Pattern; import javax.validation.constraints.Size; public class Book { @Size(min = 14, max = 14) @Pattern(regexp = "\\d{3}-\\d{10}") String isbn; @Size(max = 200) String title; @Min(1000) Integer price; // getterï¼setterã¯çç¥ }
è¬ã«ã1,000å以ä¸ã®æ¬ããåãä»ããªãããã«ãªã£ã¦ãã¾ãã
Job
ã®å®ç¾©ã
src/main/java/org/littlewings/spring/batch/BeanValidationJobConfig.java
package org.littlewings.spring.batch; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.batch.core.Job; import org.springframework.batch.core.Step; import org.springframework.batch.core.configuration.annotation.JobBuilderFactory; import org.springframework.batch.core.configuration.annotation.StepBuilderFactory; import org.springframework.batch.core.configuration.annotation.StepScope; import org.springframework.batch.core.launch.support.RunIdIncrementer; import org.springframework.batch.core.step.skip.AlwaysSkipItemSkipPolicy; import org.springframework.batch.item.ItemWriter; import org.springframework.batch.item.file.FlatFileItemReader; import org.springframework.batch.item.file.builder.FlatFileItemReaderBuilder; import org.springframework.batch.item.validator.BeanValidatingItemProcessor; import org.springframework.batch.item.validator.ValidationException; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.core.io.FileSystemResource; import org.springframework.core.io.Resource; import org.springframework.validation.beanvalidation.LocalValidatorFactoryBean; @Configuration public class BeanValidationJobConfig { @Autowired JobBuilderFactory jobBuilderFactory; @Autowired StepBuilderFactory stepBuilderFactory; @Autowired LocalValidatorFactoryBean localValidatorFactoryBean; @Bean public Job beanValidationJob() { return jobBuilderFactory .get("beanValidationJob") .incrementer(new RunIdIncrementer()) // å¾ã§ .build(); } // å¾ã§ @StepScope @Bean public FlatFileItemReader<Book> flatFileItemReader(@Value("#{jobParameters['filePath']}") String filePath) { Resource fileResource = new FileSystemResource(filePath); return new FlatFileItemReaderBuilder<Book>() .name("flatFileItemReader") .resource(fileResource) .encoding("UTF-8") .delimited() .names(new String[]{"isbn", "title", "price"}) .linesToSkip(1) .targetType(Book.class) .saveState(false) .build(); } // å¾ã§ @StepScope @Bean public ItemWriter<Book> consoleItemWriter() { Logger logger = LoggerFactory.getLogger("consoleItemWriter"); return books -> books.forEach(book -> logger.info("[writer] isbn = {}, title = {}, price = {}", book.getIsbn(), book.getTitle(), book.getPrice()) ); } }
Job
ã«å«ã¾ããStep
ã®æ§æã
@Bean public Job beanValidationJob() { return jobBuilderFactory .get("beanValidationJob") .incrementer(new RunIdIncrementer()) // å¾ã§ .build(); } // å¾ã§
Bean Validationã使ãItemProcessor
ã®è¨è¿°ã¯å¾åãã«ãã¦ãã¾ãã
// å¾ã§
ãªãããã¡ã¤ã«ã®èªã¿è¾¼ã¿ã¯FlatFileItemReaderBuilder
ã§è¡ãã
@StepScope @Bean public FlatFileItemReader<Book> flatFileItemReader(@Value("#{jobParameters['filePath']}") String filePath) { Resource fileResource = new FileSystemResource(filePath); return new FlatFileItemReaderBuilder<Book>() .name("flatFileItemReader") .resource(fileResource) .encoding("UTF-8") .delimited() .names(new String[]{"isbn", "title", "price"}) .linesToSkip(1) .targetType(Book.class) .saveState(false) .build(); }
ItemWriter
ã¯æ¸¡ã£ã¦ãããã£ã³ã¯ãæ¨æºåºåã«æ¸ãåºãããã«ä½æã
@StepScope @Bean public ItemWriter<Book> consoleItemWriter() { Logger logger = LoggerFactory.getLogger("consoleItemWriter"); return books -> books.forEach(book -> logger.info("[writer] isbn = {}, title = {}, price = {}", book.getIsbn(), book.getTitle(), book.getPrice()) ); }
mainã¯ã©ã¹ã
src/main/java/org/littlewings/spring/batch/App.java
package org.littlewings.spring.batch; import org.springframework.batch.core.configuration.annotation.EnableBatchProcessing; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication @EnableBatchProcessing public class App { public static void main(String... args) { SpringApplication.run(App.class, args); } }
è¨å®ã¯ããããªæãã«ãã¦ããã¾ãã
src/main/resources/application.properties
spring.datasource.url=jdbc:mysql://172.17.0.2:3306/practice?characterEncoding=UTF-8 spring.datasource.username=kazuhira spring.datasource.password=password spring.batch.jdbc.initialize-schema=always
CSVãä½æãã¾ãããã
ã¾ãã¯ãå
¨Item
ãåé¡ãªããã¼ã¿ã®CSVã
src/main/resources/book.csv
isbn,title,price 978-4798142470,Springå¾¹åºå ¥é Spring Frameworkã«ããJavaã¢ããªã±ã¼ã·ã§ã³éçº,4400 978-4774182179,[æ¹è¨æ°ç]Springå ¥é ââJavaãã¬ã¼ã ã¯ã¼ã¯ã»ããè¯ãè¨è¨ã¨ã¢ã¼ããã¯ãã£,4180 978-1492076988,Spring Boot: Up and Running: Building Cloud Native Java and Kotlin Applications,6265 978-4295000198,ããããå¦ã¹ãMySQLéç¨ã»ç®¡çå ¥éã5.7対å¿ã,2860 978-1484237236,The Definitive Guide to Spring Batch: Modern Finite Batch Processing in the Cloud,7361 978-4798147406,詳解MySQL 5.7 æ¢ã¾ãã¬é²åã«ä¹ãé ããªãããã®ãã¯ãã«ã«ã¬ã¤ã (NEXT ONE),3960 978-4798161488,MySQLå¾¹åºå ¥é 第4ç MySQL 8.0対å¿,4180 978-4797393118,åºç¤ããã®MySQL 第3ç (åºç¤ããã·ãªã¼ãº),6038 978-4873116389,å®è·µãã¤ããã©ã¼ãã³ã¹MySQL 第3ç,5280 978-4774170206,MariaDB&MySQLå ¨æ©è½ãã¤ãã«,3850
å ¨10件ããããããã¼å ¥ãã§ãã
次ã«ãã¨ããã©ããããããªãã¼ã¿ãå ¥ã£ãCSVã
src/main/resources/book_invalid.csv
isbn,title,price 978-4798142470,Springå¾¹åºå ¥é Spring Frameworkã«ããJavaã¢ããªã±ã¼ã·ã§ã³éçº,4400 xx-xxxxxxx,[æ¹è¨æ°ç]Springå ¥é ââJavaãã¬ã¼ã ã¯ã¼ã¯ã»ããè¯ãè¨è¨ã¨ã¢ã¼ããã¯ãã£,4180 978-1492076988,Spring Boot: Up and Running: Building Cloud Native Java and Kotlin Applications,6265 978-4295000198,ããããå¦ã¹ãMySQLéç¨ã»ç®¡çå ¥éã5.7対å¿ã,860 978-1484237236,The Definitive Guide to Spring Batch: Modern Finite Batch Processing in the Cloud,7361 978-4798147406,詳解MySQL 5.7 æ¢ã¾ãã¬é²åã«ä¹ãé ããªãããã®ãã¯ãã«ã«ã¬ã¤ã (NEXT ONE),960 978-4798161488,MySQLå¾¹åºå ¥é 第4ç MySQL 8.0対å¿,4180 978-4797393118,åºç¤ããã®MySQL 第3ç (åºç¤ããã·ãªã¼ãº),6038 978-4873116389,å®è·µãã¤ããã©ã¼ãã³ã¹MySQL 第3ç,5280 978-4774170206,MariaDB&MySQLå ¨æ©è½ãã¤ãã«,850
4件ãã¨ã©ã¼ã«ãªããã¼ã¿ï¼ISBNã®å½¢å¼èª¤ããä¾¡æ ¼ã1,000åæªæºï¼ãå ¥ã£ã¦ãã¾ãã
ããã¾ã§ã§ãæºåã¯å®äºã§ãã
BeanValidatingItemProcessorã使ã
ã§ã¯ãä½æããJob
ã«Bean Validationãè¡ãããã«Step
ãæ§æãã¦ããã¾ãããã
ããããã¯ãå ã»ã©ã// å¾ã§ãã¨ã³ã¡ã³ããæ¸ãã¦ããé¨åãåããããå¤æ´ãã¦ãã£ãããã¾ãã
æåã¯ãããªãã¼ã·ã§ã³ãªãã§
ã¾ãã¯ãBean Validationãè¡ããªãããã«Step
ãæ§æãã¾ãã
Step
ã®å®ç¾©ã
@Bean public Step noBeanValidationStep() { return stepBuilderFactory .get("withBeanValidationStep") .<Book, Book>chunk(3) .reader(flatFileItemReader(null)) .writer(consoleItemWriter()) .build(); }
Job
ã®å®ç¾©ã
@Bean public Job beanValidationJob() { return jobBuilderFactory .get("beanValidationJob") .incrementer(new RunIdIncrementer()) .start(noBeanValidationStep()) .build(); }
ããã§ããã±ã¼ã¸ã³ã°ãã¦
$ mvn package
å®è¡ãã¾ãã¯ãæ£ãããã¼ã¿ãå ¥ã£ãCSVãèªã¿è¾¼ãã¦ã¿ã¾ãã
$ java -Dspring.batch.job.names=beanValidationJob -jar target/batch-beanvalidation-0.0.1-SNAPSHOT.jar filePath=src/main/resources/book.csv
çµæã
2022-04-28 01:37:52.114 INFO 17646 --- [ main] o.s.b.a.b.JobLauncherApplicationRunner : Running default command line with: [filePath=src/main/resources/book.csv] 2022-04-28 01:37:52.291 INFO 17646 --- [ main] o.s.b.c.l.support.SimpleJobLauncher : Job: [SimpleJob: [name=beanValidationJob]] launched with the following parameters: [{run.id=5, filePath=src/main/resources/book.csv}] 2022-04-28 01:37:52.394 INFO 17646 --- [ main] o.s.batch.core.job.SimpleStepHandler : Executing step: [withBeanValidationStep] 2022-04-28 01:37:52.510 INFO 17646 --- [ main] consoleItemWriter : [writer] isbn = 978-4798142470, title = Springå¾¹åºå ¥é Spring Frameworkã«ããJavaã¢ããªã±ã¼ã·ã§ã³éçº, price = 4400 2022-04-28 01:37:52.511 INFO 17646 --- [ main] consoleItemWriter : [writer] isbn = 978-4774182179, title = [æ¹è¨æ°ç]Springå ¥é ââJavaãã¬ã¼ã ã¯ã¼ã¯ã»ããè¯ãè¨è¨ã¨ã¢ã¼ããã¯ãã£, price = 4180 2022-04-28 01:37:52.511 INFO 17646 --- [ main] consoleItemWriter : [writer] isbn = 978-1492076988, title = Spring Boot: Up and Running: Building Cloud Native Java and Kotlin Applications, price = 6265 2022-04-28 01:37:52.540 INFO 17646 --- [ main] consoleItemWriter : [writer] isbn = 978-4295000198, title = ããããå¦ã¹ãMySQLéç¨ã»ç®¡çå ¥éã5.7対å¿ã, price = 2860 2022-04-28 01:37:52.540 INFO 17646 --- [ main] consoleItemWriter : [writer] isbn = 978-1484237236, title = The Definitive Guide to Spring Batch: Modern Finite Batch Processing in the Cloud, price = 7361 2022-04-28 01:37:52.540 INFO 17646 --- [ main] consoleItemWriter : [writer] isbn = 978-4798147406, title = 詳解MySQL 5.7 æ¢ã¾ãã¬é²åã«ä¹ãé ããªãããã®ãã¯ãã«ã«ã¬ã¤ã (NEXT ONE), price = 3960 2022-04-28 01:37:52.566 INFO 17646 --- [ main] consoleItemWriter : [writer] isbn = 978-4798161488, title = MySQLå¾¹åºå ¥é 第4ç MySQL 8.0対å¿, price = 4180 2022-04-28 01:37:52.566 INFO 17646 --- [ main] consoleItemWriter : [writer] isbn = 978-4797393118, title = åºç¤ããã®MySQL 第3ç (åºç¤ããã·ãªã¼ãº), price = 6038 2022-04-28 01:37:52.567 INFO 17646 --- [ main] consoleItemWriter : [writer] isbn = 978-4873116389, title = å®è·µãã¤ããã©ã¼ãã³ã¹MySQL 第3ç, price = 5280 2022-04-28 01:37:52.594 INFO 17646 --- [ main] consoleItemWriter : [writer] isbn = 978-4774170206, title = MariaDB&MySQLå ¨æ©è½ãã¤ãã«, price = 3850 2022-04-28 01:37:52.621 INFO 17646 --- [ main] o.s.batch.core.step.AbstractStep : Step: [withBeanValidationStep] executed in 226ms 2022-04-28 01:37:52.675 INFO 17646 --- [ main] o.s.b.c.l.support.SimpleJobLauncher : Job: [SimpleJob: [name=beanValidationJob]] completed with the following parameters: [{run.id=5, filePath=src/main/resources/book.csv}] and the following status: [COMPLETED] in 339ms
次ã«ãã¨ã©ã¼ã«ãªãCSVãèªã¿è¾¼ã¾ãã¦ã¿ã¾ãã
$ java -Dspring.batch.job.names=beanValidationJob -jar target/batch-beanvalidation-0.0.1-SNAPSHOT.jar filePath=src/main/resources/book_invalid.csv
ã§ãããããããBean Validationãã¾ã å ¥ãã¦ããªãã®ã§å ¨ä»¶ééãã¾ãã
2022-04-28 01:38:43.458 INFO 17727 --- [ main] o.s.b.a.b.JobLauncherApplicationRunner : Running default command line with: [filePath=src/main/resources/book_invali d.csv] 2022-04-28 01:38:43.638 INFO 17727 --- [ main] o.s.b.c.l.support.SimpleJobLauncher : Job: [SimpleJob: [name=beanValidationJob]] launched with the following parameters: [{run.id=6, filePath=src/main/resources/book_invalid.csv}] 2022-04-28 01:38:43.768 INFO 17727 --- [ main] o.s.batch.core.job.SimpleStepHandler : Executing step: [withBeanValidationStep] 2022-04-28 01:38:43.890 INFO 17727 --- [ main] consoleItemWriter : [writer] isbn = 978-4798142470, title = Springå¾¹åºå ¥é Spring Frameworkã«ããJavaã¢ããªã±ã¼ã·ã§ã³éçº, price = 4400 2022-04-28 01:38:43.890 INFO 17727 --- [ main] consoleItemWriter : [writer] isbn = xx-xxxxxxx, title = [æ¹è¨æ°ç]Springå ¥é ââJavaãã¬ã¼ã ã¯ã¼ã¯ã»ããè¯ãè¨è¨ã¨ã¢ã¼ããã¯ãã£, price = 4180 2022-04-28 01:38:43.890 INFO 17727 --- [ main] consoleItemWriter : [writer] isbn = 978-1492076988, title = Spring Boot: Up and Running: Building Cloud Native Java and Kotlin Applications, price = 6265 2022-04-28 01:38:43.920 INFO 17727 --- [ main] consoleItemWriter : [writer] isbn = 978-4295000198, title = ããããå¦ã¹ãMySQLéç¨ã»ç®¡çå ¥éã5.7対å¿ã, price = 860 2022-04-28 01:38:43.921 INFO 17727 --- [ main] consoleItemWriter : [writer] isbn = 978-1484237236, title = The Definitive Guide to Spring Batch: Modern Finite Batch Processing in the Cloud, price = 7361 2022-04-28 01:38:43.921 INFO 17727 --- [ main] consoleItemWriter : [writer] isbn = 978-4798147406, title = 詳解MySQL 5.7 æ¢ã¾ãã¬é²åã«ä¹ãé ããªãããã®ãã¯ãã«ã«ã¬ã¤ã (NEXT ONE), price = 960 2022-04-28 01:38:43.948 INFO 17727 --- [ main] consoleItemWriter : [writer] isbn = 978-4798161488, title = MySQLå¾¹åºå ¥é 第4ç MySQL 8.0対å¿, price = 4180 2022-04-28 01:38:43.948 INFO 17727 --- [ main] consoleItemWriter : [writer] isbn = 978-4797393118, title = åºç¤ããã®MySQL 第3ç (åºç¤ããã·ãªã¼ãº), price = 6038 2022-04-28 01:38:43.948 INFO 17727 --- [ main] consoleItemWriter : [writer] isbn = 978-4873116389, title = å®è·µãã¤ããã©ã¼ãã³ã¹MySQL 第3ç, price = 5280 2022-04-28 01:38:43.975 INFO 17727 --- [ main] consoleItemWriter : [writer] isbn = 978-4774170206, title = MariaDB&MySQLå ¨æ©è½ãã¤ãã«, price = 850 2022-04-28 01:38:44.002 INFO 17727 --- [ main] o.s.batch.core.step.AbstractStep : Step: [withBeanValidationStep] executed in 234ms 2022-04-28 01:38:44.061 INFO 17727 --- [ main] o.s.b.c.l.support.SimpleJobLauncher : Job: [SimpleJob: [name=beanValidationJob]] completed with the following parameters: [{run.id=6, filePath=src/main/resources/book_invalid.csv}] and the following status: [COMPLETED] in 373ms
BeanValidatingItemProcessorã追å ãã
ã§ã¯ãBeanValidatingItemProcessor
ã追å ãã¦ã¿ã¾ãããã
Item processing / Validating Input
以ä¸ã®å®ç¾©ã追å ãã¾ãã
@StepScope @Bean public BeanValidatingItemProcessor<Book> beanValidatingItemProcessor() { return new BeanValidatingItemProcessor<>(localValidatorFactoryBean); }
ã³ã³ã¹ãã©ã¯ã¿ã§æå®ãã¦ããLocalValidatorFactoryBean
ã¯ãSpring Bootã®Auto Configurationã§ã»ããã¢ããããããã®ã§ãã
æå®ããªãã¦ãå
é¨çã«LocalValidatorFactoryBean
ãçæããã®ã§ããããã£ãããªãSpring Bootã§ã»ããã¢ããããããã®ã
使ã£ãæ¹ãããããªã¨æãã¾ãã
Step
å®ç¾©ã¯ä»¥ä¸ã®ããã«ItemProcessor
ã追å ãã¦
@Bean public Step withBeanValidationStep() { return stepBuilderFactory .get("withBeanValidationStep") .<Book, Book>chunk(3) .reader(flatFileItemReader(null)) .processor(beanValidatingItemProcessor()) .writer(consoleItemWriter()) .build(); }
Job
ãå¤æ´ã
@Bean public Job beanValidationJob() { return jobBuilderFactory .get("beanValidationJob") .incrementer(new RunIdIncrementer()) .start(withBeanValidationStep()) .build(); }
ããã±ã¼ã¸ã³ã°ãã¦å®è¡ã
$ mvn package $ java -Dspring.batch.job.names=beanValidationJob -jar target/batch-beanvalidation-0.0.1-SNAPSHOT.jar filePath=src/main/resources/book.csv
ã¨ã©ã¼ã«ãªããªãæ¹ã®çµæã¯ãçç¥ãã¾ãã
ç¶ãã¦ãã¨ã©ã¼ã«ãªãæ¹ã®CSVãèªã¿è¾¼ã¾ãã¾ãã
$ java -Dspring.batch.job.names=beanValidationJob -jar target/batch-beanvalidation-0.0.1-SNAPSHOT.jar filePath=src/main/resources/book_invalid.csv
ããã¨ãã¨ã©ã¼ã«ãªãItem
ã«ééããæç¹ã§ä¾å¤ãã¹ãã¼ãã¦ã¢ããªã±ã¼ã·ã§ã³ãçµäºãã¾ãã
2022-04-28 01:42:46.217 INFO 17951 --- [ main] o.s.b.a.b.JobLauncherApplicationRunner : Running default command line with: [filePath=src/main/resources/book_invalid.csv] 2022-04-28 01:42:46.432 INFO 17951 --- [ main] o.s.b.c.l.support.SimpleJobLauncher : Job: [SimpleJob: [name=beanValidationJob]] launched with the following parameters: [{run.id=8, filePath=src/main/resources/book_invalid.csv}] 2022-04-28 01:42:46.530 INFO 17951 --- [ main] o.s.batch.core.job.SimpleStepHandler : Executing step: [withBeanValidationStep] 2022-04-28 01:42:46.718 ERROR 17951 --- [ main] o.s.batch.core.step.AbstractStep : Encountered an error executing step withBeanValidationStep in job beanValidationJob org.springframework.batch.item.validator.ValidationException: Validation failed for org.littlewings.spring.batch.Book@11dee337: Field error in object 'item' on field 'isbn': rejected value [xx-xxxxxxx]; codes [Pattern.item.isbn,Pattern.isbn,Pattern.java.lang.String,Pattern]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [item.isbn,isbn]; arguments []; default message [isbn],[Ljavax.validation.constraints.Pattern$Flag;@532a02d9,\d{3}-\d{10}]; default message [æ£è¦è¡¨ç¾ "\d{3}-\d{10}" ã«ãããããã¦ãã ãã] Field error in object 'item' on field 'isbn': rejected value [xx-xxxxxxx]; codes [Size.item.isbn,Size.isbn,Size.java.lang.String,Size]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [item.isbn,isbn]; arguments []; default message [isbn],14,14]; default message [14 ãã 14 ã®éã®ãµã¤ãºã«ãã¦ãã ãã] at org.springframework.batch.item.validator.SpringValidator.validate(SpringValidator.java:54) ~[spring-batch-infrastructure-4.3.5.jar!/:4.3.5] at org.springframework.batch.item.validator.ValidatingItemProcessor.process(ValidatingItemProcessor.java:84) ~[spring-batch-infrastructure-4.3.5.jar!/:4.3.5] at org.springframework.batch.item.validator.ValidatingItemProcessor$$FastClassBySpringCGLIB$$39980290.invoke(<generated>) ~[spring-batch-infrastructure-4.3.5.jar!/:4.3.5] at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:218) ~[spring-core-5.3.19.jar!/:5.3.19] at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.invokeJoinpoint(CglibAopProxy.java:793) ~[spring-aop-5.3.19.jar!/:5.3.19] at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:163) ~[spring-aop-5.3.19.jar!/:5.3.19] at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:763) ~[spring-aop-5.3.19.jar!/:5.3.19] at org.springframework.aop.support.DelegatingIntroductionInterceptor.doProceed(DelegatingIntroductionInterceptor.java:137) ~[spring-aop-5.3.19.jar!/:5.3.19] at org.springframework.aop.support.DelegatingIntroductionInterceptor.invoke(DelegatingIntroductionInterceptor.java:124) ~[spring-aop-5.3.19.jar!/:5.3.19] at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186) ~[spring-aop-5.3.19.jar!/:5.3.19] at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:763) ~[spring-aop-5.3.19.jar!/:5.3.19] at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:708) ~[spring-aop-5.3.19.jar!/:5.3.19] at org.springframework.batch.item.validator.BeanValidatingItemProcessor$$EnhancerBySpringCGLIB$$b5c9b6d8.process(<generated>) ~[spring-batch-infrastructure-4.3.5.jar!/:4.3.5] at org.springframework.batch.core.step.item.SimpleChunkProcessor.doProcess(SimpleChunkProcessor.java:134) ~[spring-batch-core-4.3.5.jar!/:4.3.5] at org.springframework.batch.core.step.item.SimpleChunkProcessor.transform(SimpleChunkProcessor.java:319) ~[spring-batch-core-4.3.5.jar!/:4.3.5] at org.springframework.batch.core.step.item.SimpleChunkProcessor.process(SimpleChunkProcessor.java:210) ~[spring-batch-core-4.3.5.jar!/:4.3.5] at org.springframework.batch.core.step.item.ChunkOrientedTasklet.execute(ChunkOrientedTasklet.java:77) ~[spring-batch-core-4.3.5.jar!/:4.3.5] at org.springframework.batch.core.step.tasklet.TaskletStep$ChunkTransactionCallback.doInTransaction(TaskletStep.java:407) ~[spring-batch-core-4.3.5.jar!/:4.3.5] at org.springframework.batch.core.step.tasklet.TaskletStep$ChunkTransactionCallback.doInTransaction(TaskletStep.java:331) ~[spring-batch-core-4.3.5.jar!/:4.3.5] at org.springframework.transaction.support.TransactionTemplate.execute(TransactionTemplate.java:140) ~[spring-tx-5.3.19.jar!/:5.3.19] at org.springframework.batch.core.step.tasklet.TaskletStep$2.doInChunkContext(TaskletStep.java:273) ~[spring-batch-core-4.3.5.jar!/:4.3.5] at org.springframework.batch.core.scope.context.StepContextRepeatCallback.doInIteration(StepContextRepeatCallback.java:82) ~[spring-batch-core-4.3.5.jar!/:4.3.5] at org.springframework.batch.repeat.support.RepeatTemplate.getNextResult(RepeatTemplate.java:375) ~[spring-batch-infrastructure-4.3.5.jar!/:4.3.5] at org.springframework.batch.repeat.support.RepeatTemplate.executeInternal(RepeatTemplate.java:215) ~[spring-batch-infrastructure-4.3.5.jar!/:4.3.5] at org.springframework.batch.repeat.support.RepeatTemplate.iterate(RepeatTemplate.java:145) ~[spring-batch-infrastructure-4.3.5.jar!/:4.3.5] at org.springframework.batch.core.step.tasklet.TaskletStep.doExecute(TaskletStep.java:258) ~[spring-batch-core-4.3.5.jar!/:4.3.5] at org.springframework.batch.core.step.AbstractStep.execute(AbstractStep.java:208) ~[spring-batch-core-4.3.5.jar!/:4.3.5] at org.springframework.batch.core.job.SimpleStepHandler.handleStep(SimpleStepHandler.java:152) ~[spring-batch-core-4.3.5.jar!/:4.3.5] at org.springframework.batch.core.job.AbstractJob.handleStep(AbstractJob.java:413) ~[spring-batch-core-4.3.5.jar!/:4.3.5] at org.springframework.batch.core.job.SimpleJob.doExecute(SimpleJob.java:136) ~[spring-batch-core-4.3.5.jar!/:4.3.5] at org.springframework.batch.core.job.AbstractJob.execute(AbstractJob.java:320) ~[spring-batch-core-4.3.5.jar!/:4.3.5] at org.springframework.batch.core.launch.support.SimpleJobLauncher$1.run(SimpleJobLauncher.java:149) ~[spring-batch-core-4.3.5.jar!/:4.3.5] at org.springframework.core.task.SyncTaskExecutor.execute(SyncTaskExecutor.java:50) ~[spring-core-5.3.19.jar!/:5.3.19] at org.springframework.batch.core.launch.support.SimpleJobLauncher.run(SimpleJobLauncher.java:140) ~[spring-batch-core-4.3.5.jar!/:4.3.5] at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:na] at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77) ~[na:na] at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:na] at java.base/java.lang.reflect.Method.invoke(Method.java:568) ~[na:na] at org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:344) ~[spring-aop-5.3.19.jar!/:5.3.19] at org.springframework.aop.framework.ReflectiveMethodInvocation.invokeJoinpoint(ReflectiveMethodInvocation.java:198) ~[spring-aop-5.3.19.jar!/:5.3.19] at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:163) ~[spring-aop-5.3.19.jar!/:5.3.19] at org.springframework.batch.core.configuration.annotation.SimpleBatchConfiguration$PassthruAdvice.invoke(SimpleBatchConfiguration.java:128) ~[spring-batch-core-4.3.5.jar!/:4.3.5] at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186) ~[spring-aop-5.3.19.jar!/:5.3.19] at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:215) ~[spring-aop-5.3.19.jar!/:5.3.19] at jdk.proxy2/jdk.proxy2.$Proxy58.run(Unknown Source) ~[na:na] at org.springframework.boot.autoconfigure.batch.JobLauncherApplicationRunner.execute(JobLauncherApplicationRunner.java:199) ~[spring-boot-autoconfigure-2.6.7.jar!/:2.6.7] at org.springframework.boot.autoconfigure.batch.JobLauncherApplicationRunner.executeLocalJobs(JobLauncherApplicationRunner.java:173) ~[spring-boot-autoconfigure-2.6.7.jar!/:2.6.7] at org.springframework.boot.autoconfigure.batch.JobLauncherApplicationRunner.launchJobFromProperties(JobLauncherApplicationRunner.java:160) ~[spring-boot-autoconfigure-2.6.7.jar!/:2.6.7] at org.springframework.boot.autoconfigure.batch.JobLauncherApplicationRunner.run(JobLauncherApplicationRunner.java:155) ~[spring-boot-autoconfigure-2.6.7.jar!/:2.6.7] at org.springframework.boot.autoconfigure.batch.JobLauncherApplicationRunner.run(JobLauncherApplicationRunner.java:150) ~[spring-boot-autoconfigure-2.6.7.jar!/:2.6.7] at org.springframework.boot.SpringApplication.callRunner(SpringApplication.java:768) ~[spring-boot-2.6.7.jar!/:2.6.7] at org.springframework.boot.SpringApplication.callRunners(SpringApplication.java:758) ~[spring-boot-2.6.7.jar!/:2.6.7] at org.springframework.boot.SpringApplication.run(SpringApplication.java:310) ~[spring-boot-2.6.7.jar!/:2.6.7] at org.springframework.boot.SpringApplication.run(SpringApplication.java:1312) ~[spring-boot-2.6.7.jar!/:2.6.7] at org.springframework.boot.SpringApplication.run(SpringApplication.java:1301) ~[spring-boot-2.6.7.jar!/:2.6.7] at org.littlewings.spring.batch.App.main(App.java:11) ~[classes!/:0.0.1-SNAPSHOT] at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:na] at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77) ~[na:na] at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:na] at java.base/java.lang.reflect.Method.invoke(Method.java:568) ~[na:na] at org.springframework.boot.loader.MainMethodRunner.run(MainMethodRunner.java:49) ~[batch-beanvalidation-0.0.1-SNAPSHOT.jar:0.0.1-SNAPSHOT] at org.springframework.boot.loader.Launcher.launch(Launcher.java:108) ~[batch-beanvalidation-0.0.1-SNAPSHOT.jar:0.0.1-SNAPSHOT] at org.springframework.boot.loader.Launcher.launch(Launcher.java:58) ~[batch-beanvalidation-0.0.1-SNAPSHOT.jar:0.0.1-SNAPSHOT] at org.springframework.boot.loader.JarLauncher.main(JarLauncher.java:88) ~[batch-beanvalidation-0.0.1-SNAPSHOT.jar:0.0.1-SNAPSHOT] Caused by: org.springframework.validation.BindException: org.springframework.validation.BeanPropertyBindingResult: 2 errors Field error in object 'item' on field 'isbn': rejected value [xx-xxxxxxx]; codes [Pattern.item.isbn,Pattern.isbn,Pattern.java.lang.String,Pattern]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [item.isbn,isbn]; arguments []; default message [isbn],[Ljavax.validation.constraints.Pattern$Flag;@532a02d9,\d{3}-\d{10}]; default message [æ£è¦è¡¨ç¾ "\d{3}-\d{10}" ã«ãããããã¦ãã ãã] Field error in object 'item' on field 'isbn': rejected value [xx-xxxxxxx]; codes [Size.item.isbn,Size.isbn,Size.java.lang.String,Size]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [item.isbn,isbn]; arguments []; default message [isbn],14,14]; default message [14 ãã 14 ã®éã®ãµã¤ãºã«ãã¦ãã ãã] ... 64 common frames omitted 2022-04-28 01:42:46.722 INFO 17951 --- [ main] o.s.batch.core.step.AbstractStep : Step: [withBeanValidationStep] executed in 192ms 2022-04-28 01:42:46.778 INFO 17951 --- [ main] o.s.b.c.l.support.SimpleJobLauncher : Job: [SimpleJob: [name=beanValidationJob]] completed with the following parameters: [{run.id=8, filePath=src/main/resources/book_invalid.csv}] and the following status: [FAILED] in 303ms
FAILED
ã«ãªã£ã¦ãã¾ãã¾ããã
ã¨ã©ã¼ã«ãªã£ãItemãèªã¿é£ã°ã
次ã«ãã¨ã©ã¼ã«ãªã£ãItem
ãã¹ãããããããã«è¨å®ãã¦ã¿ã¾ãããã
Configuring a Step / Chunk-oriented Processing / Configuring Skip Logic
faultTolerant
ã使ãã次ã«ã¹ãããããä¾å¤ï¼skip
ï¼ã¨ã¹ãããå¯è½ãªæ°ï¼skipLimit
ï¼ãæå®ãã¾ãã
@Bean public Step faultTolerantBeanValidationStep() { return stepBuilderFactory .get("withBeanValidationStep") .<Book, Book>chunk(3) .reader(flatFileItemReader(null)) .processor(beanValidatingItemProcessor()) .writer(consoleItemWriter()) .faultTolerant() .skip(ValidationException.class) .skipLimit(5) .build(); }
skipLimit
ã¯ã0ãã大ããªå¤ãæå®ããå¿
è¦ãããã¾ããç¡å¶éãã¿ãããªãã¨ã¯ã§ããªãããã§ãã
Job
å®ç¾©ãå¤æ´ã
@Bean public Job beanValidationJob() { return jobBuilderFactory .get("beanValidationJob") .incrementer(new RunIdIncrementer()) .start(faultTolerantBeanValidationStep()) .build(); }
ããã±ã¼ã¸ã³ã°ãã¦ãå®è¡ãããããå ã¯ãã¨ã©ã¼ã«ãªããã¡ã¤ã«ã®ã¿èªã¿è¾¼ã¾ãã¾ãã
$ mvn package $ java -Dspring.batch.job.names=beanValidationJob -jar target/batch-beanvalidation-0.0.1-SNAPSHOT.jar filePath=src/main/resources/book_invalid.csv
ãã°ã
2022-04-28 01:48:14.347 INFO 18194 --- [ main] org.littlewings.spring.batch.App : Started App in 1.941 seconds (JVM running for 2.392) 2022-04-28 01:48:14.348 INFO 18194 --- [ main] o.s.b.a.b.JobLauncherApplicationRunner : Running default command line with: [filePath=src/main/resources/book_invalid.csv] 2022-04-28 01:48:14.522 INFO 18194 --- [ main] o.s.b.c.l.support.SimpleJobLauncher : Job: [SimpleJob: [name=beanValidationJob]] launched with the following parameters: [{run.id=9, filePath=src/main/resources/book_invalid.csv}] 2022-04-28 01:48:14.649 INFO 18194 --- [ main] o.s.batch.core.job.SimpleStepHandler : Executing step: [withBeanValidationStep] 2022-04-28 01:48:14.849 INFO 18194 --- [ main] consoleItemWriter : [writer] isbn = 978-4798142470, title = Springå¾¹åºå ¥é Spring Frameworkã«ããJavaã¢ããªã±ã¼ã·ã§ã³éçº, price = 4400 2022-04-28 01:48:14.849 INFO 18194 --- [ main] consoleItemWriter : [writer] isbn = 978-1492076988, title = Spring Boot: Up and Running: Building Cloud Native Java and Kotlin Applications, price = 6265 2022-04-28 01:48:14.883 INFO 18194 --- [ main] consoleItemWriter : [writer] isbn = 978-1484237236, title = The Definitive Guide to Spring Batch: Modern Finite Batch Processing in the Cloud, price = 7361 2022-04-28 01:48:14.911 INFO 18194 --- [ main] consoleItemWriter : [writer] isbn = 978-4798161488, title = MySQLå¾¹åºå ¥é 第4ç MySQL 8.0対å¿, price = 4180 2022-04-28 01:48:14.911 INFO 18194 --- [ main] consoleItemWriter : [writer] isbn = 978-4797393118, title = åºç¤ããã®MySQL 第3ç (åºç¤ããã·ãªã¼ãº), price = 6038 2022-04-28 01:48:14.911 INFO 18194 --- [ main] consoleItemWriter : [writer] isbn = 978-4873116389, title = å®è·µãã¤ããã©ã¼ãã³ã¹MySQL 第3ç, price = 5280 2022-04-28 01:48:14.965 INFO 18194 --- [ main] o.s.batch.core.step.AbstractStep : Step: [withBeanValidationStep] executed in 316ms 2022-04-28 01:48:15.019 INFO 18194 --- [ main] o.s.b.c.l.support.SimpleJobLauncher : Job: [SimpleJob: [name=beanValidationJob]] completed with the following parameters: [{run.id=9, filePath=src/main/resources/book_invalid.csv}] and the following status: [COMPLETED] in 448ms
ä»åº¦ã¯ãæ£å¸¸ã«çµäºããããã«ãªãã¾ããã
ItemWriter
ã¯ãã®ã¾ã¾å¦çãããItem
ã®ã¿ãåºåããããã¡ãã¯æ³å®éãã§ãããã©ã®ãããªItem
ãã¨ã©ã¼ã«ãªã£ããã¯ãããã¾ããã
ã¹ãããããItemããã°åºåãã
ã¹ãããããItem
ããã°åºåããããã«ãã¦ã¿ã¾ãããã
Common Batch Patterns / Logging Item Processing and Failures
SkipListener
ã¤ã³ã¿ã¼ãã§ã¼ã¹ãå®è£
ãããã@OnSkipInProcess
ã¢ããã¼ã·ã§ã³ãä»ä¸ããã¡ã½ãããæã¤ã¯ã©ã¹ãä½æããå¿
è¦ãããã¾ãã
SkipListener (Spring Batch 4.3.5 API)
OnSkipInProcess (Spring Batch 4.3.5 API)
ãã®ãããªListener
ãä½æã
src/main/java/org/littlewings/spring/batch/RejectItemLoggingListener.java
package org.littlewings.spring.batch; import java.util.stream.Collectors; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.batch.core.annotation.OnSkipInProcess; import org.springframework.batch.item.validator.ValidationException; import org.springframework.validation.BindException; public class RejectItemLoggingListener { Logger logger = LoggerFactory.getLogger(RejectItemLoggingListener.class); @OnSkipInProcess public void OnSkipInProcess(Book book, Throwable throwable) { if (throwable instanceof ValidationException) { ((BindException) throwable.getCause()).getMessage(); logger.info( "validation error, isbn = {}, title = {}, reject reason = {}", book.getIsbn(), book.getTitle(), ((BindException) throwable.getCause()) .getBindingResult() .getFieldErrors() .stream().map(e -> e.getObjectName() + "#" + e.getField() + ":" + e.getDefaultMessage()).collect(Collectors.joining(", ")) ); } else { logger.error("error", throwable); } } }
ãã¡ããBeanå®ç¾©ãã¦
@StepScope @Bean public RejectItemLoggingListener rejectItemLoggingListener() { return new RejectItemLoggingListener(); }
faultTolerant
ã®å¾ã«Listener
ã¨ãã¦è¿½å ãã¾ãã
@Bean public Step loggingInvalidItemBeanValidationStep() { return stepBuilderFactory .get("loggingInvalidItemBeanValidationStep") .<Book, Book>chunk(3) .reader(flatFileItemReader(null)) .processor(beanValidatingItemProcessor()) .writer(consoleItemWriter()) .faultTolerant() .skip(ValidationException.class) .skipLimit(5) .listener(rejectItemLoggingListener()) .build(); }
Job
ãæ§æã
@Bean public Job beanValidationJob() { return jobBuilderFactory .get("beanValidationJob") .incrementer(new RunIdIncrementer()) .start(loggingInvalidItemBeanValidationStep()) .build(); }
å®è¡ã
$ mvn package $ java -Dspring.batch.job.names=beanValidationJob -jar target/batch-beanvalidation-0.0.1-SNAPSHOT.jar filePath=src/main/resources/book_invalid.csv
ãã°ã
2022-04-28 01:53:07.154 INFO 18429 --- [ main] o.s.b.a.b.JobLauncherApplicationRunner : Running default command line with: [filePath=src/main/resources/book_invalid.csv] 2022-04-28 01:53:07.330 INFO 18429 --- [ main] o.s.b.c.l.support.SimpleJobLauncher : Job: [SimpleJob: [name=beanValidationJob]] launched with the following parameters: [{run.id=10, filePath=src/main/resources/book_invalid.csv}] 2022-04-28 01:53:07.460 INFO 18429 --- [ main] o.s.batch.core.job.SimpleStepHandler : Executing step: [loggingInvalidItemBeanValidationStep] 2022-04-28 01:53:07.693 INFO 18429 --- [ main] consoleItemWriter : [writer] isbn = 978-4798142470, title = Springå¾¹åºå ¥é Spring Frameworkã«ããJavaã¢ããªã±ã¼ã·ã§ã³éçº, price = 4400 2022-04-28 01:53:07.693 INFO 18429 --- [ main] consoleItemWriter : [writer] isbn = 978-1492076988, title = Spring Boot: Up and Running: Building Cloud Native Java and Kotlin Applications, price = 6265 2022-04-28 01:53:07.697 INFO 18429 --- [ main] o.l.s.batch.RejectItemLoggingListener : validation error, isbn = xx-xxxxxxx, title = [æ¹è¨æ°ç]Springå ¥é ââJavaã ã¬ã¼ã ã¯ã¼ã¯ã»ããè¯ãè¨è¨ã¨ã¢ã¼ããã¯ãã£, reject reason = item#isbn:æ£è¦è¡¨ç¾ "\d{3}-\d{10}" ã«ãããããã¦ãã ãã, item#isbn:14 ãã 14 ã®éã®ãµã¤ãºã«ãã¦ãã ãã 2022-04-28 01:53:07.748 INFO 18429 --- [ main] consoleItemWriter : [writer] isbn = 978-1484237236, title = The Definitive Guide to Spring Batch: Modern Finite Batch Processing in the Cloud, price = 7361 2022-04-28 01:53:07.748 INFO 18429 --- [ main] o.l.s.batch.RejectItemLoggingListener : validation error, isbn = 978-4295000198, title = ããããå¦ã¹ãMySQLéç¨ã» 管çå ¥éã5.7対å¿ã, reject reason = item#price:1000 以ä¸ã®å¤ã«ãã¦ãã ãã 2022-04-28 01:53:07.748 INFO 18429 --- [ main] o.l.s.batch.RejectItemLoggingListener : validation error, isbn = 978-4798147406, title = 詳解MySQL 5.7 æ¢ã¾ãã¬é²åã«ä¹ãé ããªãããã®ãã¯ãã«ã«ã¬ã¤ã (NEXT ONE), reject reason = item#price:1000 以ä¸ã®å¤ã«ãã¦ãã ãã 2022-04-28 01:53:07.776 INFO 18429 --- [ main] consoleItemWriter : [writer] isbn = 978-4798161488, title = MySQLå¾¹åºå ¥é 第4ç MySQL 8.0対å¿, price = 4180 2022-04-28 01:53:07.776 INFO 18429 --- [ main] consoleItemWriter : [writer] isbn = 978-4797393118, title = åºç¤ããã®MySQL 第3ç (åºç¤ããã·ãªã¼ãº), price = 6038 2022-04-28 01:53:07.776 INFO 18429 --- [ main] consoleItemWriter : [writer] isbn = 978-4873116389, title = å®è·µãã¤ããã©ã¼ãã³ã¹MySQL 第3ç, price = 5280 2022-04-28 01:53:07.805 INFO 18429 --- [ main] o.l.s.batch.RejectItemLoggingListener : validation error, isbn = 978-4774170206, title = MariaDB&MySQLå ¨æ©è½ãã¤ã ã«, reject reason = item#price:1000 以ä¸ã®å¤ã«ãã¦ãã ãã 2022-04-28 01:53:07.833 INFO 18429 --- [ main] o.s.batch.core.step.AbstractStep : Step: [loggingInvalidItemBeanValidationStep] executed in 372ms 2022-04-28 01:53:07.883 INFO 18429 --- [ main] o.s.b.c.l.support.SimpleJobLauncher : Job: [SimpleJob: [name=beanValidationJob]] completed with the following parameters: [{run.id=10, filePath=src/main/resources/book_invalid.csv}] and the following status: [COMPLETED] in 514ms
ã¨ã©ã¼ã«ãªã£ãItem
ãããã°ã«åºåãããããã«ãªãã¾ããã
ã¡ãªã¿ã«ã以ä¸ã®ããã«faultTolerant
ã®åã«Listener
ã追å ãã¦ãæ©è½ãã¾ããã
@Bean public Step invalidSkipItemListenerBeanValidationStep() { return stepBuilderFactory .get("invalidSkipItemListenerBeanValidationStep") .<Book, Book>chunk(3) .reader(flatFileItemReader(null)) .processor(beanValidatingItemProcessor()) .writer(consoleItemWriter()) .listener(rejectItemLoggingListener()) .faultTolerant() .skip(ValidationException.class) .skipLimit(5) .build(); }
listener
ã¡ã½ããã®å¼æ°ã®åãObject
ã®ãã®ãããã®ã§æ¸¡ãã¦ãã¾ãã®ã§ãããSkipListener
ã¤ã³ã¿ã¼ãã§ã¼ã¹ãå®è£
ãã¦ããå ´åã¯ã
åãåããã«ã³ã³ãã¤ã«ãéããªããªãã®ã§ãã®ãããªãã¹ã¯çºçãã¾ããâ¦ã
skipã§æå®ããå¤ãä¸åã£ãå ´å
skip
ã§æå®ããæ°ãè¶
ãã¦Item
ãã¹ãããããå ´åã«ãã©ããªããã確èªãã¦ã¿ã¾ãããã
ããªãã¼ã·ã§ã³ãNGã«ãªãItem
ã®æ°ã¯4ãªã®ã§ãskip
ã«3ãæå®ãã¦ã¿ã¾ãã
@Bean public Step loggingInvalidItemBeanValidationStopStep() { return stepBuilderFactory .get("loggingInvalidItemBeanValidationStep") .<Book, Book>chunk(3) .reader(flatFileItemReader(null)) .processor(beanValidatingItemProcessor()) .writer(consoleItemWriter()) .faultTolerant() .skip(ValidationException.class) .skipLimit(3) .listener(rejectItemLoggingListener()) .build(); }
Job
ã®æ§æã
@Bean public Job beanValidationJob() { return jobBuilderFactory .get("beanValidationJob") .incrementer(new RunIdIncrementer()) .start(loggingInvalidItemBeanValidationStopStep()) .build(); }
å®è¡ã
$ mvn package $ java -Dspring.batch.job.names=beanValidationJob -jar target/batch-beanvalidation-0.0.1-SNAPSHOT.jar filePath=src/main/resources/book_invalid.csv
çµæã
2022-04-28 01:57:38.529 INFO 18665 --- [ main] o.s.b.a.b.JobLauncherApplicationRunner : Running default command line with: [filePath=src/main/resources/book_invalid.csv] 2022-04-28 01:57:38.734 INFO 18665 --- [ main] o.s.b.c.l.support.SimpleJobLauncher : Job: [SimpleJob: [name=beanValidationJob]] launched with the following parameters: [{run.id=11, filePath=src/main/resources/book_invalid.csv}] 2022-04-28 01:57:38.827 INFO 18665 --- [ main] o.s.batch.core.job.SimpleStepHandler : Executing step: [loggingInvalidItemBeanValidationStep] 2022-04-28 01:57:39.020 INFO 18665 --- [ main] consoleItemWriter : [writer] isbn = 978-4798142470, title = Springå¾¹åºå ¥é Spring Frameworkã«ããJavaã¢ããªã±ã¼ã·ã§ã³éçº, price = 4400 2022-04-28 01:57:39.020 INFO 18665 --- [ main] consoleItemWriter : [writer] isbn = 978-1492076988, title = Spring Boot: Up and Running: Building Cloud Native Java and Kotlin Applications, price = 6265 2022-04-28 01:57:39.025 INFO 18665 --- [ main] o.l.s.batch.RejectItemLoggingListener : validation error, isbn = xx-xxxxxxx, title = [æ¹è¨æ°ç]Springå ¥é ââJavaã ã¬ã¼ã ã¯ã¼ã¯ã»ããè¯ãè¨è¨ã¨ã¢ã¼ããã¯ãã£, reject reason = item#isbn:14 ãã 14 ã®éã®ãµã¤ãºã«ãã¦ãã ãã, item#isbn:æ£è¦è¡¨ç¾ "\d{3}-\d{10}" ã«ãããããã¦ãã ãã 2022-04-28 01:57:39.055 INFO 18665 --- [ main] consoleItemWriter : [writer] isbn = 978-1484237236, title = The Definitive Guide to Spring Batch: Modern Finite Batch Processing in the Cloud, price = 7361 2022-04-28 01:57:39.056 INFO 18665 --- [ main] o.l.s.batch.RejectItemLoggingListener : validation error, isbn = 978-4295000198, title = ããããå¦ã¹ãMySQLéç¨ã» 管çå ¥éã5.7対å¿ã, reject reason = item#price:1000 以ä¸ã®å¤ã«ãã¦ãã ãã 2022-04-28 01:57:39.057 INFO 18665 --- [ main] o.l.s.batch.RejectItemLoggingListener : validation error, isbn = 978-4798147406, title = 詳解MySQL 5.7 æ¢ã¾ãã¬é²åã«ä¹ãé ããªãããã®ãã¯ãã«ã«ã¬ã¤ã (NEXT ONE), reject reason = item#price:1000 以ä¸ã®å¤ã«ãã¦ãã ãã 2022-04-28 01:57:39.082 INFO 18665 --- [ main] consoleItemWriter : [writer] isbn = 978-4798161488, title = MySQLå¾¹åºå ¥é 第4ç MySQL 8.0対å¿, price = 4180 2022-04-28 01:57:39.082 INFO 18665 --- [ main] consoleItemWriter : [writer] isbn = 978-4797393118, title = åºç¤ããã®MySQL 第3ç (åºç¤ããã·ãªã¼ãº), price = 6038 2022-04-28 01:57:39.082 INFO 18665 --- [ main] consoleItemWriter : [writer] isbn = 978-4873116389, title = å®è·µãã¤ããã©ã¼ãã³ã¹MySQL 第3ç, price = 5280 2022-04-28 01:57:39.110 ERROR 18665 --- [ main] o.s.batch.core.step.AbstractStep : Encountered an error executing step loggingInvalidItemBeanValidationStep in job beanValidationJob org.springframework.batch.core.step.skip.SkipLimitExceededException: Skip limit of '3' exceeded at org.springframework.batch.core.step.skip.LimitCheckingItemSkipPolicy.shouldSkip(LimitCheckingItemSkipPolicy.java:133) ~[spring-batch-core-4.3.5.jar!/:4.3.5] at org.springframework.batch.core.step.skip.ExceptionClassifierSkipPolicy.shouldSkip(ExceptionClassifierSkipPolicy.java:70) ~[spring-batch-core-4.3.5.jar!/:4.3.5] at org.springframework.batch.core.step.item.FaultTolerantChunkProcessor.shouldSkip(FaultTolerantChunkProcessor.java:519) ~[spring-batch-core-4.3.5.jar!/:4.3.5] at org.springframework.batch.core.step.item.FaultTolerantChunkProcessor.access$500(FaultTolerantChunkProcessor.java:56) ~[spring-batch-core-4.3.5.jar!/:4.3.5] at org.springframework.batch.core.step.item.FaultTolerantChunkProcessor$2.recover(FaultTolerantChunkProcessor.java:289) ~[spring-batch-core-4.3.5.jar!/:4.3.5] at org.springframework.retry.support.RetryTemplate.handleRetryExhausted(RetryTemplate.java:539) ~[spring-retry-1.3.3.jar!/:na] at org.springframework.retry.support.RetryTemplate.doExecute(RetryTemplate.java:387) ~[spring-retry-1.3.3.jar!/:na] at org.springframework.retry.support.RetryTemplate.execute(RetryTemplate.java:255) ~[spring-retry-1.3.3.jar!/:na] at org.springframework.batch.core.step.item.BatchRetryTemplate.execute(BatchRetryTemplate.java:217) ~[spring-batch-core-4.3.5.jar!/:4.3.5] at org.springframework.batch.core.step.item.FaultTolerantChunkProcessor.transform(FaultTolerantChunkProcessor.java:308) ~[spring-batch-core-4.3.5.jar!/:4.3.5] at org.springframework.batch.core.step.item.SimpleChunkProcessor.process(SimpleChunkProcessor.java:210) ~[spring-batch-core-4.3.5.jar!/:4.3.5] at org.springframework.batch.core.step.item.ChunkOrientedTasklet.execute(ChunkOrientedTasklet.java:77) ~[spring-batch-core-4.3.5.jar!/:4.3.5] at org.springframework.batch.core.step.tasklet.TaskletStep$ChunkTransactionCallback.doInTransaction(TaskletStep.java:407) ~[spring-batch-core-4.3.5.jar!/:4.3.5] at org.springframework.batch.core.step.tasklet.TaskletStep$ChunkTransactionCallback.doInTransaction(TaskletStep.java:331) ~[spring-batch-core-4.3.5.jar!/:4.3.5] at org.springframework.transaction.support.TransactionTemplate.execute(TransactionTemplate.java:140) ~[spring-tx-5.3.19.jar!/:5.3.19] at org.springframework.batch.core.step.tasklet.TaskletStep$2.doInChunkContext(TaskletStep.java:273) ~[spring-batch-core-4.3.5.jar!/:4.3.5] at org.springframework.batch.core.scope.context.StepContextRepeatCallback.doInIteration(StepContextRepeatCallback.java:82) ~[spring-batch-core-4.3.5.jar!/:4.3.5] at org.springframework.batch.repeat.support.RepeatTemplate.getNextResult(RepeatTemplate.java:375) ~[spring-batch-infrastructure-4.3.5.jar!/:4.3.5] at org.springframework.batch.repeat.support.RepeatTemplate.executeInternal(RepeatTemplate.java:215) ~[spring-batch-infrastructure-4.3.5.jar!/:4.3.5] at org.springframework.batch.repeat.support.RepeatTemplate.iterate(RepeatTemplate.java:145) ~[spring-batch-infrastructure-4.3.5.jar!/:4.3.5] at org.springframework.batch.core.step.tasklet.TaskletStep.doExecute(TaskletStep.java:258) ~[spring-batch-core-4.3.5.jar!/:4.3.5] at org.springframework.batch.core.step.AbstractStep.execute(AbstractStep.java:208) ~[spring-batch-core-4.3.5.jar!/:4.3.5] at org.springframework.batch.core.job.SimpleStepHandler.handleStep(SimpleStepHandler.java:152) ~[spring-batch-core-4.3.5.jar!/:4.3.5] at org.springframework.batch.core.job.AbstractJob.handleStep(AbstractJob.java:413) ~[spring-batch-core-4.3.5.jar!/:4.3.5] at org.springframework.batch.core.job.SimpleJob.doExecute(SimpleJob.java:136) ~[spring-batch-core-4.3.5.jar!/:4.3.5] at org.springframework.batch.core.job.AbstractJob.execute(AbstractJob.java:320) ~[spring-batch-core-4.3.5.jar!/:4.3.5] at org.springframework.batch.core.launch.support.SimpleJobLauncher$1.run(SimpleJobLauncher.java:149) ~[spring-batch-core-4.3.5.jar!/:4.3.5] at org.springframework.core.task.SyncTaskExecutor.execute(SyncTaskExecutor.java:50) ~[spring-core-5.3.19.jar!/:5.3.19] at org.springframework.batch.core.launch.support.SimpleJobLauncher.run(SimpleJobLauncher.java:140) ~[spring-batch-core-4.3.5.jar!/:4.3.5] at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:na] at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77) ~[na:na] at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:na] at java.base/java.lang.reflect.Method.invoke(Method.java:568) ~[na:na] at org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:344) ~[spring-aop-5.3.19.jar!/:5.3.19] at org.springframework.aop.framework.ReflectiveMethodInvocation.invokeJoinpoint(ReflectiveMethodInvocation.java:198) ~[spring-aop-5.3.19.jar!/:5.3.19] at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:163) ~[spring-aop-5.3.19.jar!/:5.3.19] at org.springframework.batch.core.configuration.annotation.SimpleBatchConfiguration$PassthruAdvice.invoke(SimpleBatchConfiguration.java:128) ~[spring-batch-core-4.3.5.jar!/:4.3.5] at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186) ~[spring-aop-5.3.19.jar!/:5.3.19] at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:215) ~[spring-aop-5.3.19.jar!/:5.3.19] at jdk.proxy2/jdk.proxy2.$Proxy58.run(Unknown Source) ~[na:na] at org.springframework.boot.autoconfigure.batch.JobLauncherApplicationRunner.execute(JobLauncherApplicationRunner.java:199) ~[spring-boot-autoconfigure-2.6.7.jar!/:2.6.7] at org.springframework.boot.autoconfigure.batch.JobLauncherApplicationRunner.executeLocalJobs(JobLauncherApplicationRunner.java:173) ~[spring-boot-autoconfigure-2.6.7.jar!/:2.6.7] at org.springframework.boot.autoconfigure.batch.JobLauncherApplicationRunner.launchJobFromProperties(JobLauncherApplicationRunner.java:160) ~[spring-boot-autoconfigure-2.6.7.jar!/:2.6.7] at org.springframework.boot.autoconfigure.batch.JobLauncherApplicationRunner.run(JobLauncherApplicationRunner.java:155) ~[spring-boot-autoconfigure-2.6.7.jar!/:2.6.7] at org.springframework.boot.autoconfigure.batch.JobLauncherApplicationRunner.run(JobLauncherApplicationRunner.java:150) ~[spring-boot-autoconfigure-2.6.7.jar!/:2.6.7] at org.springframework.boot.SpringApplication.callRunner(SpringApplication.java:768) ~[spring-boot-2.6.7.jar!/:2.6.7] at org.springframework.boot.SpringApplication.callRunners(SpringApplication.java:758) ~[spring-boot-2.6.7.jar!/:2.6.7] at org.springframework.boot.SpringApplication.run(SpringApplication.java:310) ~[spring-boot-2.6.7.jar!/:2.6.7] at org.springframework.boot.SpringApplication.run(SpringApplication.java:1312) ~[spring-boot-2.6.7.jar!/:2.6.7] at org.springframework.boot.SpringApplication.run(SpringApplication.java:1301) ~[spring-boot-2.6.7.jar!/:2.6.7] at org.littlewings.spring.batch.App.main(App.java:11) ~[classes!/:0.0.1-SNAPSHOT] at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:na] at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77) ~[na:na] at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:na] at java.base/java.lang.reflect.Method.invoke(Method.java:568) ~[na:na] at org.springframework.boot.loader.MainMethodRunner.run(MainMethodRunner.java:49) ~[batch-beanvalidation-0.0.1-SNAPSHOT.jar:0.0.1-SNAPSHOT] at org.springframework.boot.loader.Launcher.launch(Launcher.java:108) ~[batch-beanvalidation-0.0.1-SNAPSHOT.jar:0.0.1-SNAPSHOT] at org.springframework.boot.loader.Launcher.launch(Launcher.java:58) ~[batch-beanvalidation-0.0.1-SNAPSHOT.jar:0.0.1-SNAPSHOT] at org.springframework.boot.loader.JarLauncher.main(JarLauncher.java:88) ~[batch-beanvalidation-0.0.1-SNAPSHOT.jar:0.0.1-SNAPSHOT] Caused by: org.springframework.batch.item.validator.ValidationException: Validation failed for org.littlewings.spring.batch.Book@47428937: Field error in object 'item' on field 'price': rejected value [850]; codes [Min.item.price,Min.price,Min.java.lang.Integer,Min]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [item.price,price]; arguments []; default message [price],1000]; default message [1000 以ä¸ã®å¤ã«ãã¦ãã ãã] at org.springframework.batch.item.validator.SpringValidator.validate(SpringValidator.java:54) ~[spring-batch-infrastructure-4.3.5.jar!/:4.3.5] at org.springframework.batch.item.validator.ValidatingItemProcessor.process(ValidatingItemProcessor.java:84) ~[spring-batch-infrastructure-4.3.5.jar!/:4.3.5] at org.springframework.batch.item.validator.ValidatingItemProcessor$$FastClassBySpringCGLIB$$39980290.invoke(<generated>) ~[spring-batch-infrastructure-4.3.5.jar!/:4.3.5] at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:218) ~[spring-core-5.3.19.jar!/:5.3.19] at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.invokeJoinpoint(CglibAopProxy.java:793) ~[spring-aop-5.3.19.jar!/:5.3.19] at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:163) ~[spring-aop-5.3.19.jar!/:5.3.19] at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:763) ~[spring-aop-5.3.19.jar!/:5.3.19] at org.springframework.aop.support.DelegatingIntroductionInterceptor.doProceed(DelegatingIntroductionInterceptor.java:137) ~[spring-aop-5.3.19.jar!/:5.3.19] at org.springframework.aop.support.DelegatingIntroductionInterceptor.invoke(DelegatingIntroductionInterceptor.java:124) ~[spring-aop-5.3.19.jar!/:5.3.19] at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186) ~[spring-aop-5.3.19.jar!/:5.3.19] at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:763) ~[spring-aop-5.3.19.jar!/:5.3.19] at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:708) ~[spring-aop-5.3.19.jar!/:5.3.19] at org.springframework.batch.item.validator.BeanValidatingItemProcessor$$EnhancerBySpringCGLIB$$b5c9b6d8.process(<generated>) ~[spring-batch-infrastructure-4.3.5.jar!/:4.3.5] at org.springframework.batch.core.step.item.SimpleChunkProcessor.doProcess(SimpleChunkProcessor.java:134) ~[spring-batch-core-4.3.5.jar!/:4.3.5] at org.springframework.batch.core.step.item.FaultTolerantChunkProcessor$1.doWithRetry(FaultTolerantChunkProcessor.java:239) ~[spring-batch-core-4.3.5.jar!/:4.3.5] at org.springframework.retry.support.RetryTemplate.doExecute(RetryTemplate.java:329) ~[spring-retry-1.3.3.jar!/:na] ... 52 common frames omitted Caused by: org.springframework.validation.BindException: org.springframework.validation.BeanPropertyBindingResult: 1 errors Field error in object 'item' on field 'price': rejected value [850]; codes [Min.item.price,Min.price,Min.java.lang.Integer,Min]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [item.price,price]; arguments []; default message [price],1000]; default message [1000 以ä¸ã®å¤ã«ãã¦ãã ãã] ... 68 common frames omitted 2022-04-28 01:57:39.115 INFO 18665 --- [ main] o.s.batch.core.step.AbstractStep : Step: [loggingInvalidItemBeanValidationStep] executed in 288ms 2022-04-28 01:57:39.158 INFO 18665 --- [ main] o.s.b.c.l.support.SimpleJobLauncher : Job: [SimpleJob: [name=beanValidationJob]] completed with the following parameters: [{run.id=11, filePath=src/main/resources/book_invalid.csv}] and the following status: [FAILED] in 376ms
ã¹ãããå¯è½ãªItem
ã®æ°ãè¶
ããã¨ãããã¨ã§ãä¾å¤ãã¹ãã¼ããã¢ããªã±ã¼ã·ã§ã³ãåæ¢ãã¾ãã
org.springframework.batch.core.step.skip.SkipLimitExceededException: Skip limit of '3' exceeded
SkipPolicyãè¨å®ãã
æå¾ã«ãSkipPolicy
ãè¨å®ãã¦ã¿ã¾ãã
SkipPolicy (Spring Batch 4.3.5 API)
SkipPolicy
ã¯ã¹ãããå¯è½ãªæ¡ä»¶ãå¤å®ããããã®ã¤ã³ã¿ã¼ãã§ã¼ã¹ã§ãSkipPolicy#shouldSkip(java.lang.Throwable t, int skipCount)
ã¡ã½ããã§
å¤å®ãè¡ãã¾ãã
ä»ã¾ã§è¨å®ãã¦ããã¹ãããã¨å¤å®ããä¾å¤ãã¹ãããå¯è½ãªæ°ã¯ãSkipPolicy
ã®å®è£
ã§ããExceptionClassifierSkipPolicy
ã¨
LimitCheckingItemSkipPolicy
ã®çµã¿åããã§æ©è½ãã¦ãã¾ããã
ExceptionClassifierSkipPolicy (Spring Batch 4.3.5 API)
LimitCheckingItemSkipPolicy (Spring Batch 4.3.5 API)
ããã§ã以ä¸ã®ããã«skipPolicy
ã¡ã½ããã§æ示çã«SkipPolicy
ã®ã¤ã³ã¹ã¿ã³ã¹ãæå®ãããã¨ã§ãã¹ãããã®æ¡ä»¶ãã«ã¹ã¿ãã¤ãº
ãããã¨ãã§ãã¾ãã
@Bean public Step loggingInvalidItemAlwaysSkipBeanValidationStep() { return stepBuilderFactory .get("loggingInvalidItemBeanValidationStep") .<Book, Book>chunk(3) .reader(flatFileItemReader(null)) .processor(beanValidatingItemProcessor()) .writer(consoleItemWriter()) .faultTolerant() //.skip(ValidationException.class) .skipPolicy(new AlwaysSkipItemSkipPolicy()) .listener(rejectItemLoggingListener()) .build(); }
ããã§æå®ãã¦ããAlwaysSkipItemSkipPolicy
ã¯ãä¾å¤ãã¹ãããããItem
ã®æ°ã«é¢ããã常ã«ã¹ãããã許å¯ããã¯ã©ã¹ã§ãã
AlwaysSkipItemSkipPolicy (Spring Batch 4.3.5 API)
skipPolicy
ã¡ã½ããã使ã£ã¦SkipPolicy
ãæå®ããã¨ãCompositeSkipPolicy
ã¨ããSkipPolicy
ãåæããã¯ã©ã¹ã使ããã
ãã¨ãã¨å©ç¨ãããExceptionClassifierSkipPolicy
ã¨LimitCheckingItemSkipPolicy
ã®çµã¿åããã¨åæããã¾ãã
CompositeSkipPolicy (Spring Batch 4.3.5 API)
è©ä¾¡é ã¯ãå
ã«ç»é²ããSkipPolicy
ããé ã«è¡ããããããªã®ã§ãAlwaysSkipItemSkipPolicy
ã使ç¨ããã¨ã¹ããã対象ã®ä¾å¤ã
ã©ã®ããã«æå®ãã¦ãã¹ãããæ°ãã©ã®ããã«æå®ãã¦ãã常ã«ã¹ãããå¯è½ã¨å¤å®ããã¾ãã
ã¡ãªã¿ã«ãä»ã®SkipPolicy
ã®å®è£
ã¯ã¹ãããããªãNeverSkipItemSkipPolicy
ãããããã§ãã
NeverSkipItemSkipPolicy (Spring Batch 4.3.5 API)
Job
å®ç¾©ãå¤æ´ãã¦
@Bean public Job beanValidationJob() { return jobBuilderFactory .get("beanValidationJob") .incrementer(new RunIdIncrementer()) .start(loggingInvalidItemAlwaysSkipBeanValidationStep()) .build(); }
ããã±ã¼ã¸ã³ã°ãã¦å®è¡ã
$ mvn package $ java -Dspring.batch.job.names=beanValidationJob -jar target/batch-beanvalidation-0.0.1-SNAPSHOT.jar filePath=src/main/resources/book_invalid.csv
ãã°ã
2022-05-22 00:44:05.818 INFO 66977 --- [ main] org.littlewings.spring.batch.App : Started App in 2.269 seconds (JVM running for 2.677) 2022-05-22 00:44:05.819 INFO 66977 --- [ main] o.s.b.a.b.JobLauncherApplicationRunner : Running default command line with: [filePath=src/main/resources/book_invalid.csv] 2022-05-22 00:44:06.012 INFO 66977 --- [ main] o.s.b.c.l.support.SimpleJobLauncher : Job: [SimpleJob: [name=beanValidationJob]] launched with the following parameters: [{run.id=4, filePath=src/main/resources/book_invalid.csv}] 2022-05-22 00:44:06.141 INFO 66977 --- [ main] o.s.batch.core.job.SimpleStepHandler : Executing step: [loggingInvalidItemBeanValidationStep] 2022-05-22 00:44:06.379 INFO 66977 --- [ main] consoleItemWriter : [writer] isbn = 978-4798142470, title = Springå¾¹åºå ¥é Spring Frameworkã«ããJavaã¢ããªã±ã¼ã·ã§ã³éçº, price = 4400 2022-05-22 00:44:06.379 INFO 66977 --- [ main] consoleItemWriter : [writer] isbn = 978-1492076988, title = Spring Boot: Up and Running: Building Cloud Native Java and Kotlin Applications, price = 6265 2022-05-22 00:44:06.387 INFO 66977 --- [ main] o.l.s.batch.RejectItemLoggingListener : validation error, isbn = xx-xxxxxxx, title = [æ¹è¨æ°ç]Springå ¥é ââJava ãã¬ã¼ã ã¯ã¼ã¯ã»ããè¯ãè¨è¨ã¨ã¢ã¼ããã¯ãã£, reject reason = item#isbn:æ£è¦è¡¨ç¾ "\d{3}-\d{10}" ã«ãããããã¦ãã ãã, item#isbn:14 ãã 14 ã®éã®ãµã¤ãºã«ãã¦ãã ãã 2022-05-22 00:44:06.510 INFO 66977 --- [ main] consoleItemWriter : [writer] isbn = 978-1484237236, title = The Definitive Guide to Spring Batch: Modern Finite Batch Processing in the Cloud, price = 7361 2022-05-22 00:44:06.510 INFO 66977 --- [ main] o.l.s.batch.RejectItemLoggingListener : validation error, isbn = 978-4295000198, title = ããããå¦ã¹ãMySQLéç¨ ã»ç®¡çå ¥éã5.7対å¿ã, reject reason = item#price:1000 以ä¸ã®å¤ã«ãã¦ãã ãã 2022-05-22 00:44:06.511 INFO 66977 --- [ main] o.l.s.batch.RejectItemLoggingListener : validation error, isbn = 978-4798147406, title = 詳解MySQL 5.7 æ¢ã¾ãã¬é²åã«ä¹ãé ããªãããã®ãã¯ãã«ã«ã¬ã¤ã (NEXT ONE), reject reason = item#price:1000 以ä¸ã®å¤ã«ãã¦ãã ãã 2022-05-22 00:44:06.543 INFO 66977 --- [ main] consoleItemWriter : [writer] isbn = 978-4798161488, title = MySQLå¾¹åºå ¥é 第4ç MySQL 8.0対å¿, price = 4180 2022-05-22 00:44:06.543 INFO 66977 --- [ main] consoleItemWriter : [writer] isbn = 978-4797393118, title = åºç¤ããã®MySQL 第3ç (åºç¤ããã·ãªã¼ãº), price = 6038 2022-05-22 00:44:06.544 INFO 66977 --- [ main] consoleItemWriter : [writer] isbn = 978-4873116389, title = å®è·µãã¤ããã©ã¼ãã³ã¹MySQL 第3ç, price = 5280 2022-05-22 00:44:06.576 INFO 66977 --- [ main] o.l.s.batch.RejectItemLoggingListener : validation error, isbn = 978-4774170206, title = MariaDB&MySQLå ¨æ©è½ã㤠ãã«, reject reason = item#price:1000 以ä¸ã®å¤ã«ãã¦ãã ãã 2022-05-22 00:44:06.607 INFO 66977 --- [ main] o.s.batch.core.step.AbstractStep : Step: [loggingInvalidItemBeanValidationStep] executed in 465ms 2022-05-22 00:44:06.665 INFO 66977 --- [ main] o.s.b.c.l.support.SimpleJobLauncher : Job: [SimpleJob: [name=beanValidationJob]] completed with the following parameters: [{run.id=4, filePath=src/main/resources/book_invalid.csv}] and the following status: [COMPLETED] in 605ms
ã¹ãããå¯è½ãªä¾å¤ãã¹ãããå¯è½ãªåæ°ããã¦ãã¾ããããItem
ã®å¦çä¸ã«ä¾å¤ãã¹ãã¼ããã¦ãã¹ãããã¨ãã¦å¤å®ããããã«ãªãã¾ããã
AlwaysSkipItemSkipPolicy
ã使ç¨ããã¨ãä¸è¦ã¹ãããåæ°ã®å¶éãæ¤å»ã§ããããã«ãè¦ãã¾ãããä¾å¤ããã¹ã¦ç¡è¦ããã®ã§
ã¡ãã£ã¨å¾®å¦ã§ããã
ããå°ãåã£ãã¹ãããæ¡ä»¶ã«ãããå ´åã¯ãèªåã§SkipPolicy
ãå®è£
ããã®ããããã¾ããã
ã¾ã¨ã
Spring Batchã«Bean Validationãå ããã¨ã¨ãã«ãItem
ãã¹ãããããå ´åã¨ãã®å¶å¾¡ã«ã¤ãã¦ç¢ºèªãã¦ã¿ã¾ããã
1度Spring Batchããã£ããè¦ã¦ããã¨ããã®ãããã¯å²ã¨ãããªãã¨å ¥ãã¾ãããã
æå¾ã«ãJob
ãStep
ãå®ç¾©ãã¦ããã¯ã©ã¹ã®ã½ã¼ã¹ã³ã¼ãå
¨ä½ãè¼ãã¦ããã¾ãã
src/main/java/org/littlewings/spring/batch/BeanValidationJobConfig.java
package org.littlewings.spring.batch; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.batch.core.Job; import org.springframework.batch.core.Step; import org.springframework.batch.core.configuration.annotation.JobBuilderFactory; import org.springframework.batch.core.configuration.annotation.StepBuilderFactory; import org.springframework.batch.core.configuration.annotation.StepScope; import org.springframework.batch.core.launch.support.RunIdIncrementer; import org.springframework.batch.core.step.skip.AlwaysSkipItemSkipPolicy; import org.springframework.batch.item.ItemWriter; import org.springframework.batch.item.file.FlatFileItemReader; import org.springframework.batch.item.file.builder.FlatFileItemReaderBuilder; import org.springframework.batch.item.validator.BeanValidatingItemProcessor; import org.springframework.batch.item.validator.ValidationException; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.core.io.FileSystemResource; import org.springframework.core.io.Resource; import org.springframework.validation.beanvalidation.LocalValidatorFactoryBean; @Configuration public class BeanValidationJobConfig { @Autowired JobBuilderFactory jobBuilderFactory; @Autowired StepBuilderFactory stepBuilderFactory; @Autowired LocalValidatorFactoryBean localValidatorFactoryBean; @Bean public Job beanValidationJob() { return jobBuilderFactory .get("beanValidationJob") .incrementer(new RunIdIncrementer()) //.start(noBeanValidationStep()) //.start(withBeanValidationStep()) //.start(faultTolerantBeanValidationStep()) //.start(invalidSkipItemListenerBeanValidationStep()) .start(loggingInvalidItemBeanValidationStep()) //.start(loggingInvalidItemBeanValidationStopStep()) //.start(loggingInvalidItemAlwaysSkipBeanValidationStep()) .build(); } @Bean public Step noBeanValidationStep() { return stepBuilderFactory .get("withBeanValidationStep") .<Book, Book>chunk(3) .reader(flatFileItemReader(null)) .writer(consoleItemWriter()) .build(); } @Bean public Step withBeanValidationStep() { return stepBuilderFactory .get("withBeanValidationStep") .<Book, Book>chunk(3) .reader(flatFileItemReader(null)) .processor(beanValidatingItemProcessor()) .writer(consoleItemWriter()) .build(); } @Bean public Step faultTolerantBeanValidationStep() { return stepBuilderFactory .get("withBeanValidationStep") .<Book, Book>chunk(3) .reader(flatFileItemReader(null)) .processor(beanValidatingItemProcessor()) .writer(consoleItemWriter()) .faultTolerant() .skip(ValidationException.class) .skipLimit(5) .build(); } @Bean public Step invalidSkipItemListenerBeanValidationStep() { return stepBuilderFactory .get("invalidSkipItemListenerBeanValidationStep") .<Book, Book>chunk(3) .reader(flatFileItemReader(null)) .processor(beanValidatingItemProcessor()) .writer(consoleItemWriter()) .listener(rejectItemLoggingListener()) .faultTolerant() .skip(ValidationException.class) .skipLimit(5) .build(); } @Bean public Step loggingInvalidItemBeanValidationStep() { return stepBuilderFactory .get("loggingInvalidItemBeanValidationStep") .<Book, Book>chunk(3) .reader(flatFileItemReader(null)) .processor(beanValidatingItemProcessor()) .writer(consoleItemWriter()) .faultTolerant() .skip(ValidationException.class) .skipLimit(5) .listener(rejectItemLoggingListener()) .build(); } @Bean public Step loggingInvalidItemBeanValidationStopStep() { return stepBuilderFactory .get("loggingInvalidItemBeanValidationStep") .<Book, Book>chunk(3) .reader(flatFileItemReader(null)) .processor(beanValidatingItemProcessor()) .writer(consoleItemWriter()) .faultTolerant() .skip(ValidationException.class) .skipLimit(3) .listener(rejectItemLoggingListener()) .build(); } @Bean public Step loggingInvalidItemAlwaysSkipBeanValidationStep() { return stepBuilderFactory .get("loggingInvalidItemBeanValidationStep") .<Book, Book>chunk(3) .reader(flatFileItemReader(null)) .processor(beanValidatingItemProcessor()) .writer(consoleItemWriter()) .faultTolerant() //.skip(ValidationException.class) .skipPolicy(new AlwaysSkipItemSkipPolicy()) .listener(rejectItemLoggingListener()) .build(); } @StepScope @Bean public FlatFileItemReader<Book> flatFileItemReader(@Value("#{jobParameters['filePath']}") String filePath) { Resource fileResource = new FileSystemResource(filePath); return new FlatFileItemReaderBuilder<Book>() .name("flatFileItemReader") .resource(fileResource) .encoding("UTF-8") .delimited() .names(new String[]{"isbn", "title", "price"}) .linesToSkip(1) .targetType(Book.class) .saveState(false) .build(); } @StepScope @Bean public BeanValidatingItemProcessor<Book> beanValidatingItemProcessor() { return new BeanValidatingItemProcessor<>(localValidatorFactoryBean); } @StepScope @Bean public RejectItemLoggingListener rejectItemLoggingListener() { return new RejectItemLoggingListener(); } @StepScope @Bean public ItemWriter<Book> consoleItemWriter() { Logger logger = LoggerFactory.getLogger("consoleItemWriter"); return books -> books.forEach(book -> logger.info("[writer] isbn = {}, title = {}, price = {}", book.getIsbn(), book.getTitle(), book.getPrice()) ); } }