====== Spring Framework Batch ======
* [[springframework:springboot:batch|SpringBoot와 SpringBatch]]
===== @EnableBatchProcessing =====
* [[https://docs.spring.io/spring-batch/trunk/apidocs/org/springframework/batch/core/configuration/annotation/EnableBatchProcessing.html|@EnableBatchProcessing]]을 통해 기본 배치 설정을 수행한다.
==== DefaultBatchConfigurer ====
* 설정 중 일부를 Override하고자 한다면 [[https://docs.spring.io/spring-batch/trunk/apidocs/org/springframework/batch/core/configuration/annotation/DefaultBatchConfigurer.html|DefaultBatchConfigurer]] 를 Configuration class에서 상속해서 메소드를 오버라이드 한다.
* ''dataSource'' 오버라이드 예
// 다른 configuration class 에서 DataSource 생성 - embedded 예제
@Bean
public DataSource batchDataSource() {
return new EmbeddedDatabaseBuilder().setType(EmbeddedDatabaseType.H2)
.addScript("classpath:/org/springframework/batch/core/schema-h2.sql")
.build();
}
@EnableBatchProcessing
@Configuration
public class BatchApplication extends DefaultBatchConfigurer {
// dataSource 설정부분 override. transactionManager도 자동으로 생성함.
@Autowired
@Override
public void setDataSource(@Qualifier("batchDataSource") DataSource batchDataSource) {
super.setDataSource(batchDataSource);
}
}
* ''dataSource'' 자체를 null로 지정하면 ''MapJobRepository''로 DB없이 작동하게 만들어진다.
* SpringBoot 에서 ''DefaultBatchConfigurer'' 사용시 TransactionManager 오작동 문제가 발생한다. [[springframework:springboot:batch|SpringBoot와 SpringBatch]] 참고
===== JobRepository =====
* ''org.springframework.batch.core.repository.support.JobRepositoryFactoryBean''로 ''SimpleJobRepository'' 생성
* ''MapJobRepositoryFactoryBean'' 로 in memory Job Repository 생성 가능.
* ''org.springframework.batch.core'' 패키지에 ''schema-*.sql'' 파일이 Batch Job Repository DB 스키마이다.
* ''schema-mysql.sql''의 경우
* ''text'' -> ''longtext''
* ''datetime'' -> ''datetime(6)''로 변경해서 실행할 것.
* 테이블 생성시 명백하게 대문자로 테이블명을 생성해줘야한다.
* 만약 [[database:mysql|MySQL]]을 사용하면서 소문자로 테이블을 생성해버렸다면 다음과 같은 설정을 추가해줘야한다.
* see [[database:mysql:config|MySQL 설정]] 대소문자 테이블명 부분의 ''lower_case_table_names=1''
===== Migration =====
* [[https://harinathreddyyanamala.wordpress.com/2015/02/26/spring-batch-upgrade-from-2-1-7-to-2-2-7/|Spring batch upgrade from 2.1 to 2.2]]
* DB 테이블 관련 정보가 바뀌었음.
* MySQL의 경우 시퀀스 따른 방법이 바뀌었음.
===== Transaction 설정 =====
==== Connection Pool 설정 ====
* batch job 이 job 단위로 단독 프로세스로 실행되는 경우
* batch job 은 실제로 Parallel 로 실행되는게 아니면 DB 커넥션이 1~2개 정도 밖에 필요하지 않다.
* parallel, async 실행에 대비해서 maximum connection 갯수는 충분한 숫자로 하되,
* minimum 갯수를 5개 혹은 그 이하로 지정해야 Batch job 이 실행 될 때마다 커넥션을 과도하게 맺느라 들어가는 실행시간 지연 문제를 해소하고, 불필요하게 DB connection 을 점유하여 DB 자체의 한계에 봉착하는 문제를 피할 수 있다.
==== 트랜잭션 매니저를 지정하지 않으면 ====
TransactionManager를 지정하지 않으면 다음과 같은 오류가 발생한다.
java.lang.IllegalStateException: To use the default BatchConfigurer the context must contain precisely one DataSource, found X
==== 트랜잭션 기본 설정 및 트랜잭션 없이 실행하기 ====
TransactionManager 를 [[https://docs.spring.io/spring-batch/trunk/apidocs/org/springframework/batch/support/transaction/ResourcelessTransactionManager.html|ResourcelessTransactionManager]] 로 지정하면 된다.
혹은 아래와 같이 ''propagation="NEVER"''로 지정한다.
==== Java Config ====
* Step 등을 생성할 때 ''@Value("#{jobParameter[paramName]}" String param'' 형태로 Job Parameter를 받을 수 있다.
@Bean
@JobScope
public Step myStep(@Value("#{jobParameters[date]}" String date,
@Value("#{jobParameters[count]}") long count) {
// ...
}
==== Chunk 사이즈가 크고 Transaction 이 긴데, 외부에 write 대상건을 알려주는 경우 DB Lock 발생할 수 있음 ====
* batch 에 트랜잭션 매니저를 설정하고, writer 부분에서 데이터를 insert/update 할 때마다 그 사실을 MQ 등으로 외부에 알려주면,
* 외부에서 그 데이터를 사용할 수 있게 하고자 했다면,
* 절대로 Step 에 트랜잭션 매니저를 지정하지 말고, ''ResourcelessTransactionManager'' 를 사용하거나 CHUNK 를 작게 가져가야 한다.
* ''ResourcelessTransactionManager'' 로 Batch Job 을 지정하면, Batch job 의 chunk 가 아닌, 각각의 ''Service''/''Repository'' 등의 메소드에 설정된 ''@Transactional''을 기준으로 트랜잭션이 작동하게 된다.
* 안그러면 CHUNK size 만큼의 처리가 모두 끝날때까지 DB lock 이 잡혀있어서 외부에서 이벤트를 받아서 처리하는 측에서 lock 획득에 실패해서 오류가 발생하게 된다.
* 이게 아니더라도, **트랜잭션은 항상 짧게 가져가야 한다.**
===== JobInstanceAlreadyCompleteException: A job instance already exists and is complete for parameters 오류 발생 =====
* Job은 Job + Job Parameter 로 Job Key 가 결정되는데 동일한 Job Key 일 경우 실행이 안된다.
* 따라서 Job Parameter를 다는 값으로 주거나 [[https://docs.spring.io/spring-batch/trunk/apidocs/org/springframework/batch/core/launch/support/RunIdIncrementer.html|RunIdIncrementer]]를 사용한다.
* [[https://stackoverflow.com/questions/22148117/spring-batch-error-a-job-instance-already-exists-and-runidincrementer-generate|Spring Batch error (A Job Instance Already Exists) and RunIdIncrementer generates only once]]
* SpringBoot Batch 는 ''RunIdIncrementer''의 특정 파라미터(''run.id'') 값을 계속 증가시켜준다.
jobBuilderFactory.get("myJobName")
.start(step())
. .....
.incrementer(new RunIdIncrementer())
.build();
===== RunIdIncrementer 사용시 기존 파라미터가 현재 파라미터 지정한 것을 덮어씀 =====
* ''RunIdIncrementer''를 사용하게 되면 기존 job이 실패했을 경우 기존 실패 Job의 파라미터가 현재 파라미터를 덮어쓰는 현상이 발생한다.
* 이유는 ''RunIdIncrementer''가 동일 파라미터로 Job을 실행하려고 사용하는 것이라서 인 듯 하다.
* 따라서 ''RunIdIncrementer''를 사용하면서도 파라미터 값을 유지하려면 다른 방식을 써야 한다.
* [[https://jojoldu.tistory.com/487|Spring Batch의 유니크 Job Parameter 활용하기]]
* [[https://github.com/codecentric/spring-boot-starter-batch-web/issues/38|optional/omitted jobParameters are reloaded from previous jobs · Issue #38 · codecentric/spring-boot-starter-batch-web]]
/**
* 파라미터를 복사하지 않는 RunIdIncrementer
*/
public class ParamCleanRunIdIncrementer implements JobParametersIncrementer {
private static String RUN_ID_KEY = "run.id";
private String key = RUN_ID_KEY;
public void setKey(String key) { this.key = key; }
@Override
public JobParameters getNext(JobParameters parameters) {
JobParameters params = (parameters == null) ? new JobParameters() : parameters;
long id = params.getLong(key, 0L) + 1;
return new JobParametersBuilder().addLong(key, id).toJobParameters(); // 이부분이 RunIdIncrementer와 다르다.
}
}
===== Java Config Spring Bean 설정시 리턴 타입을 인터페이스가 아닌 구현 클래스로 지정할 것. =====
* ''@StepScope'' 등을 사용하는 등 할 경우 리턴 타입을 ''ItemReader'', ''ItemWriter'' 처럼 인터페이스로 할 경우 프록시 객체가 생성되고 그로 인해서 ''instanceOf'' 를 사용하는 일부 코드들이 작동하지 않거나, annotation 기반의 listener 등이 동작하지 않는 문제를 일으킨다.
* 따라서 Spring Batch 용 각 스텝별 Bean 을 생성해서 리턴할 때는 항상 **구현 클래스를 리턴 타입으로 지정**할 것.
* [[https://jojoldu.tistory.com/132|[batch] @StepScope 사용시 주의사항]]
* 다음과 같은 WARN 을 볼 수도 있다.
> o.s.b.c.l.AbstractListenerFactoryBean : org.springframework.batch.item.ItemReader is an interface. The implementing class will not be queried for annotation based listener configurations. If using @StepScope on a @Bean method, be sure to return the implementing class so listner annotations can be used.
> org.springframework.batch.item.ItemReader는 인터페이스입니다. 구현 클래스는 어노테이션 기반 listener 구성에 대해 실행되지 않습니다. @Bean 메소드에서 @StepScope를 사용하는 경우 listner 어노테이션을 사용할 수 있도록 구현 클래스를 리턴해야합니다.
===== Test =====
* [[https://docs.spring.io/spring-batch/trunk/apidocs/org/springframework/batch/test/JobLauncherTestUtils.html|JobLauncherTestUtils]]로 테스트 가능.
* 이 클래스는 static utility 가 아니라 Bean 으로 생성해줘야 하는 것이다.
* ''setJob()''으로 특정 Job을 지정하거나, 그냥 ''@Autowired''될 수도 있음.
* 특정 Step만 테스트도 가능함.
* [[springframework:springboot:batch|SpringBoot와 SpringBatch]] 시에는 ''@MockBean''도 가능.
* [[https://www.mkyong.com/spring-batch/spring-batch-unit-test-example/|Spring Batch unit test example – Mkyong.com]]
* [[http://hwannnn.blogspot.com/2018/06/spring-batch-test-joblaunchertestutils_5.html|JobLauncherTestUtils를 이용한 Spring Batch Test]]
===== 참고 =====
* [[https://dzone.com/articles/spring-batch-with-quartz|Run a Spring Batch Job With Quartz]]
* [[https://www.youtube.com/watch?v=CYTj5YT7CZU&feature=youtu.be|Spring Batch (Michael Minella) - YouTube]]
* [[https://www.baeldung.com/spring-batch-skip-logic|Configuring Skip Logic in Spring Batch | Baeldung]]
* [[http://www.javaworld.com/article/2458888/spring-framework/open-source-java-projects-spring-batch.html|Open source Java projects: Spring Batch]] : SpringBatch 2.x의 기본 적인 Tasklet, chunk, reader/writer/processor, retry 등의 개념 들을 예제와 함께 설명하고 있음.
* [[https://www.javacodegeeks.com/2015/03/spring-batch-tutorial.html|Spring Batch Tutorial – The ULTIMATE Guide]]
* [[http://www.mkyong.com/tutorials/spring-batch-tutorial/|Spring Batch Tutorial]] : 매우 상세한 튜토리얼
* [[https://www.tutorialspoint.com/spring_batch/spring_batch_environment.htm|Spring Batch Environment]]
* Spring Batch 2.2 JavaConfig
* [[https://blog.codecentric.de/en/2013/06/spring-batch-2-2-javaconfig-part-1-a-comparison-to-xml/|Spring Batch 2.2 - JavaConfig Part 1: A comparison to XML]]
* [[https://blog.codecentric.de/en/2013/06/spring-batch-2-2-javaconfig-part-2-jobparameters-executioncontext-and-stepscope/|Spring Batch 2.2 - JavaConfig Part 2: JobParameters, ExecutionContext and StepScope]]
* [[https://blog.codecentric.de/en/2013/06/spring-batch-2-2-javaconfig-part-3-profiles-and-environments/|Spring Batch 2.2 - JavaConfig Part 3: Profiles and environments]]
* [[https://blog.codecentric.de/en/2013/06/spring-batch-2-2-javaconfig-part-4-job-inheritance/|Spring Batch 2.2 - JavaConfig Part 4: Job inheritance]]
* [[https://blog.codecentric.de/en/2013/06/spring-batch-2-2-javaconfig-part-5-modular-configurations/|Spring Batch 2.2 - JavaConfig Part 5: Modular configurations]]
* [[https://blog.codecentric.de/en/2013/07/spring-batch-2-2-javaconfig-part-6-partitioning-and-multi-threaded-step/|Spring Batch 2.2 - JavaConfig Part 6: Partitioning and Multi-threaded Step]]
* Spring Batch Transaction
* [[https://blog.codecentric.de/en/2012/03/transactions-in-spring-batch-part-1-the-basics/|Transactions in Spring Batch - Part 1: The Basics]]
* [[https://blog.codecentric.de/en/2012/03/transactions-in-spring-batch-part-2-restart-cursor-based-reading-and-listeners/|Transactions in Spring Batch - Part 2: Restart, cursor based reading and listeners]]
* [[https://blog.codecentric.de/en/2012/03/transactions-in-spring-batch-part-3-skip-and-retry/|Transactions in Spring Batch - Part 3: Skip and retry]]
* [[http://spring.io/blog/2015/02/23/springone2gx-replay-spring-batch-performance-tuning|SpringOne2GX Replay: Spring Batch Performance Tuning]]
* [[http://examples.javacodegeeks.com/enterprise-java/spring/spring-batch-tasklet-example/|Spring Batch Tasklet Example]]
* [[http://examples.javacodegeeks.com/enterprise-java/spring/spring-batch-job-example/|Spring Batch Job Example]]
* [[http://examples.javacodegeeks.com/enterprise-java/spring-batch-scheduler-example/|Spring Batch Scheduler Example]]
* [[http://examples.javacodegeeks.com/enterprise-java/spring/spring-batch-jobrepository-example/|Spring Batch JobRepository Example]]
* [[http://examples.javacodegeeks.com/core-java/spring-batch-partitioning-example/|Spring Batch Partitioning Example]]
* [[http://examples.javacodegeeks.com/enterprise-java/spring/spring-batch-admin-tutorial/|Spring Batch Admin Tutorial]]
* [[http://www.petrikainulainen.net/programming/spring-framework/spring-batch-tutorial-introduction/|petrikainulainen Spring Batch Tutorial - introduction]]
* [[http://www.baeldung.com/spring-batch-partitioner|Spring Batch using Partitioner]]
* [[https://www.javacodegeeks.com/2018/05/java-batch-tutorial.html|Java Batch Tutorial | Java Code Geeks - 2018]]
* [[https://examples.javacodegeeks.com/enterprise-java/java-batch-tutorial/|Java Batch Tutorial | Examples Java Code Geeks - 2018]]
* [[http://opennote46.tistory.com/76|Spring Batch - 작업실행]]
* [[https://grokonez.com/spring-framework/spring-batch/use-spring-batch-late-binding-step-scope-job-scope|How to use Spring Batch Late Binding - Step Scope & Job Scope - grokonez]]
* [[https://grokonez.com/spring-framework-tutorial/spring-batch|Spring Batch - grokonez]]
* [[https://www.mkyong.com/spring-batch/spring-batch-example-mysql-database-to-xml/|Spring Batch Example – MySQL Database To XML – Mkyong.com]]
* [[https://examples.javacodegeeks.com/enterprise-java/spring/batch/spring-batch-exception-handling-example/|Spring Batch Exception Handling Example | Examples Java Code Geeks - 2018]]
* [[http://www.egovframe.go.kr/wiki/doku.php?id=egovframework:rte2:brte:batch_core:parallel_process|egovframework:rte2:brte:batch_core:parallel_process [eGovFrame]]]
* [[https://www.baeldung.com/spring-batch-conditional-flow|Conditional Flow in Spring Batch | Baeldung]]