diff --git a/springboot-batch-2/src/main/java/com/jojoldu/blogcode/springbootbatch2/Application.java b/springboot-batch-2/src/main/java/com/jojoldu/blogcode/springbootbatch2/Application.java new file mode 100644 index 000000000..c8dc70fd2 --- /dev/null +++ b/springboot-batch-2/src/main/java/com/jojoldu/blogcode/springbootbatch2/Application.java @@ -0,0 +1,12 @@ +package com.jojoldu.blogcode.springbootbatch2; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication +public class Application { + + public static void main(String[] args) { + SpringApplication.run(Application.class, args); + } +} diff --git a/springboot-batch-2/src/main/java/com/jojoldu/blogcode/springbootbatch2/readupdate/Pay.java b/springboot-batch-2/src/main/java/com/jojoldu/blogcode/springbootbatch2/readupdate/Pay.java new file mode 100644 index 000000000..dc7795403 --- /dev/null +++ b/springboot-batch-2/src/main/java/com/jojoldu/blogcode/springbootbatch2/readupdate/Pay.java @@ -0,0 +1,38 @@ +package com.jojoldu.blogcode.springbootbatch2.readupdate; + +/** + * Created by jojoldu@gmail.com on 2018. 9. 15. + * Blog : http://jojoldu.tistory.com + * Github : https://github.com/jojoldu + */ + +import lombok.AccessLevel; +import lombok.Getter; +import lombok.NoArgsConstructor; + +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.GenerationType; +import javax.persistence.Id; + +@Getter +@NoArgsConstructor(access = AccessLevel.PROTECTED) +@Entity +public class Pay { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + private Long amount; + private boolean successStatus; + + public Pay(Long amount, boolean successStatus) { + this.amount = amount; + this.successStatus = successStatus; + } + + public void success() { + this.successStatus = true; + } +} diff --git a/springboot-batch-2/src/main/java/com/jojoldu/blogcode/springbootbatch2/readupdate/PayCursorJobConfiguration.java b/springboot-batch-2/src/main/java/com/jojoldu/blogcode/springbootbatch2/readupdate/PayCursorJobConfiguration.java new file mode 100644 index 000000000..cd52a7e8c --- /dev/null +++ b/springboot-batch-2/src/main/java/com/jojoldu/blogcode/springbootbatch2/readupdate/PayCursorJobConfiguration.java @@ -0,0 +1,94 @@ +package com.jojoldu.blogcode.springbootbatch2.readupdate; + +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +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.JobScope; +import org.springframework.batch.core.configuration.annotation.StepBuilderFactory; +import org.springframework.batch.core.configuration.annotation.StepScope; +import org.springframework.batch.item.ItemProcessor; +import org.springframework.batch.item.database.JdbcCursorItemReader; +import org.springframework.batch.item.database.JpaItemWriter; +import org.springframework.batch.item.database.builder.JdbcCursorItemReaderBuilder; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.jdbc.core.BeanPropertyRowMapper; + +import javax.persistence.EntityManagerFactory; +import javax.sql.DataSource; + +import static com.jojoldu.blogcode.springbootbatch2.readupdate.PayCursorJobConfiguration.JOB_NAME; + + +/** + * Created by jojoldu@gmail.com on 2018. 9. 15. + * Blog : http://jojoldu.tistory.com + * Github : https://github.com/jojoldu + */ + +@Slf4j +@RequiredArgsConstructor +@Configuration +@ConditionalOnProperty(name = "job.name", havingValue = JOB_NAME) +public class PayCursorJobConfiguration { + + public static final String JOB_NAME = "payCursorJob"; + + private final EntityManagerFactory entityManagerFactory; + private final StepBuilderFactory stepBuilderFactory; + private final JobBuilderFactory jobBuilderFactory; + private final DataSource dataSource; + + private final int chunkSize = 10; + + @Bean + public Job payPagingJob() { + return jobBuilderFactory.get(JOB_NAME) + .start(payPagingStep()) + .build(); + } + + @Bean + @JobScope + public Step payPagingStep() { + return stepBuilderFactory.get("payPagingStep") + .chunk(chunkSize) + .reader(payPagingReader()) + .processor(payPagingProcessor()) + .writer(writer()) + .build(); + } + + @Bean + @StepScope + public JdbcCursorItemReader payPagingReader() { + return new JdbcCursorItemReaderBuilder() + .sql("SELECT * FROM pay p WHERE p.success_status = false") + .rowMapper(new BeanPropertyRowMapper<>(Pay.class)) + .fetchSize(chunkSize) + .dataSource(dataSource) + .name("payPagingReader") + .build(); + } + + @Bean + @StepScope + public ItemProcessor payPagingProcessor() { + return item -> { + item.success(); + return item; + }; + } + + @Bean + @StepScope + public JpaItemWriter writer() { + JpaItemWriter writer = new JpaItemWriter<>(); + writer.setEntityManagerFactory(entityManagerFactory); + return writer; + } + +} diff --git a/springboot-batch-2/src/main/java/com/jojoldu/blogcode/springbootbatch2/readupdate/PayPagingFailJobConfiguration.java b/springboot-batch-2/src/main/java/com/jojoldu/blogcode/springbootbatch2/readupdate/PayPagingFailJobConfiguration.java new file mode 100644 index 000000000..c00d2e566 --- /dev/null +++ b/springboot-batch-2/src/main/java/com/jojoldu/blogcode/springbootbatch2/readupdate/PayPagingFailJobConfiguration.java @@ -0,0 +1,91 @@ +package com.jojoldu.blogcode.springbootbatch2.readupdate; + +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +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.JobScope; +import org.springframework.batch.core.configuration.annotation.StepBuilderFactory; +import org.springframework.batch.core.configuration.annotation.StepScope; +import org.springframework.batch.item.ItemProcessor; +import org.springframework.batch.item.database.JpaItemWriter; +import org.springframework.batch.item.database.JpaPagingItemReader; +import org.springframework.batch.item.database.builder.JpaPagingItemReaderBuilder; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +import javax.persistence.EntityManagerFactory; + +import static com.jojoldu.blogcode.springbootbatch2.readupdate.PayPagingFailJobConfiguration.JOB_NAME; + + +/** + * Created by jojoldu@gmail.com on 2018. 9. 15. + * Blog : http://jojoldu.tistory.com + * Github : https://github.com/jojoldu + */ + +@Slf4j +@RequiredArgsConstructor +@Configuration +@ConditionalOnProperty(name = "job.name", havingValue = JOB_NAME) +public class PayPagingFailJobConfiguration { + + public static final String JOB_NAME = "payPagingFailJob"; + + private final EntityManagerFactory entityManagerFactory; + private final StepBuilderFactory stepBuilderFactory; + private final JobBuilderFactory jobBuilderFactory; + + private final int chunkSize = 10; + + @Bean + public Job payPagingJob() { + return jobBuilderFactory.get(JOB_NAME) + .start(payPagingStep()) + .build(); + } + + @Bean + @JobScope + public Step payPagingStep() { + return stepBuilderFactory.get("payPagingStep") + .chunk(chunkSize) + .reader(payPagingReader()) + .processor(payPagingProcessor()) + .writer(writer()) + .build(); + } + + @Bean + @StepScope + public JpaPagingItemReader payPagingReader() { + return new JpaPagingItemReaderBuilder() + .queryString("SELECT p FROM Pay p WHERE p.successStatus = false") + .pageSize(chunkSize) + .entityManagerFactory(entityManagerFactory) + .name("payPagingReader") + .build(); + } + + + @Bean + @StepScope + public ItemProcessor payPagingProcessor() { + return item -> { + item.success(); + return item; + }; + } + + @Bean + @StepScope + public JpaItemWriter writer() { + JpaItemWriter writer = new JpaItemWriter<>(); + writer.setEntityManagerFactory(entityManagerFactory); + return writer; + } + +} diff --git a/springboot-batch-2/src/main/java/com/jojoldu/blogcode/springbootbatch2/readupdate/PayPagingJobConfiguration.java b/springboot-batch-2/src/main/java/com/jojoldu/blogcode/springbootbatch2/readupdate/PayPagingJobConfiguration.java new file mode 100644 index 000000000..43773378d --- /dev/null +++ b/springboot-batch-2/src/main/java/com/jojoldu/blogcode/springbootbatch2/readupdate/PayPagingJobConfiguration.java @@ -0,0 +1,98 @@ +package com.jojoldu.blogcode.springbootbatch2.readupdate; + +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +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.JobScope; +import org.springframework.batch.core.configuration.annotation.StepBuilderFactory; +import org.springframework.batch.core.configuration.annotation.StepScope; +import org.springframework.batch.item.ItemProcessor; +import org.springframework.batch.item.database.JpaItemWriter; +import org.springframework.batch.item.database.JpaPagingItemReader; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +import javax.persistence.EntityManagerFactory; + +import static com.jojoldu.blogcode.springbootbatch2.readupdate.PayPagingJobConfiguration.JOB_NAME; + + +/** + * Created by jojoldu@gmail.com on 2018. 9. 15. + * Blog : http://jojoldu.tistory.com + * Github : https://github.com/jojoldu + */ + +@Slf4j +@RequiredArgsConstructor +@Configuration +@ConditionalOnProperty(name = "job.name", havingValue = JOB_NAME) +public class PayPagingJobConfiguration { + + public static final String JOB_NAME = "payPagingJob"; + + private final EntityManagerFactory entityManagerFactory; + private final StepBuilderFactory stepBuilderFactory; + private final JobBuilderFactory jobBuilderFactory; + + private final int chunkSize = 10; + + @Bean + public Job payPagingJob() { + return jobBuilderFactory.get(JOB_NAME) + .start(payPagingStep()) + .build(); + } + + @Bean + @JobScope + public Step payPagingStep() { + return stepBuilderFactory.get("payPagingStep") + .chunk(chunkSize) + .reader(payPagingReader()) + .processor(payPagingProcessor()) + .writer(writer()) + .build(); + } + + @Bean + @StepScope + public JpaPagingItemReader payPagingReader() { + + JpaPagingItemReader reader = new JpaPagingItemReader() { + @Override + public int getPage() { + return 0; + } + }; + + reader.setQueryString("SELECT p FROM Pay p WHERE p.successStatus = false"); + reader.setPageSize(chunkSize); + reader.setEntityManagerFactory(entityManagerFactory); + reader.setName("payPagingReader"); + + return reader; + } + + + @Bean + @StepScope + public ItemProcessor payPagingProcessor() { + return item -> { + item.success(); + return item; + }; + } + + @Bean + @StepScope + public JpaItemWriter writer() { + JpaItemWriter writer = new JpaItemWriter<>(); + writer.setEntityManagerFactory(entityManagerFactory); + return writer; + } + +} diff --git a/springboot-batch-2/src/main/java/com/jojoldu/blogcode/springbootbatch2/readupdate/PayRepository.java b/springboot-batch-2/src/main/java/com/jojoldu/blogcode/springbootbatch2/readupdate/PayRepository.java new file mode 100644 index 000000000..3bb3afaab --- /dev/null +++ b/springboot-batch-2/src/main/java/com/jojoldu/blogcode/springbootbatch2/readupdate/PayRepository.java @@ -0,0 +1,18 @@ +package com.jojoldu.blogcode.springbootbatch2.readupdate; + +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Query; + +import java.util.List; + +/** + * Created by jojoldu@gmail.com on 2018. 9. 15. + * Blog : http://jojoldu.tistory.com + * Github : https://github.com/jojoldu + */ + +public interface PayRepository extends JpaRepository { + + @Query("SELECT p FROM Pay p WHERE p.successStatus = true") + List findAllSuccess(); +} diff --git a/springboot-batch-2/src/main/resources/application.yml b/springboot-batch-2/src/main/resources/application.yml new file mode 100644 index 000000000..6613f8fd7 --- /dev/null +++ b/springboot-batch-2/src/main/resources/application.yml @@ -0,0 +1,14 @@ +spring: + h2: + console: + enabled: true + jpa: + show-sql: true + hibernate: + ddl-auto: update + +logging: + level: + org.hibernate.type: trace + +spring.batch.job.names: ${job.name:NONE} diff --git a/springboot-batch-2/src/test/java/com/jojoldu/blogcode/springbootbatch2/TestJobConfiguration.java b/springboot-batch-2/src/test/java/com/jojoldu/blogcode/springbootbatch2/TestJobConfiguration.java new file mode 100644 index 000000000..1b0edde61 --- /dev/null +++ b/springboot-batch-2/src/test/java/com/jojoldu/blogcode/springbootbatch2/TestJobConfiguration.java @@ -0,0 +1,16 @@ +package com.jojoldu.blogcode.springbootbatch2; + +import org.springframework.batch.core.configuration.annotation.EnableBatchProcessing; +import org.springframework.batch.test.JobLauncherTestUtils; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +@EnableBatchProcessing +@Configuration +public class TestJobConfiguration { + + @Bean + public JobLauncherTestUtils jobLauncherTestUtils() { + return new JobLauncherTestUtils(); + } +} \ No newline at end of file diff --git a/springboot-batch-2/src/test/java/com/jojoldu/blogcode/springbootbatch2/readupdate/PayCursorJobConfigurationTest.java b/springboot-batch-2/src/test/java/com/jojoldu/blogcode/springbootbatch2/readupdate/PayCursorJobConfigurationTest.java new file mode 100644 index 000000000..37af32e1b --- /dev/null +++ b/springboot-batch-2/src/test/java/com/jojoldu/blogcode/springbootbatch2/readupdate/PayCursorJobConfigurationTest.java @@ -0,0 +1,48 @@ +package com.jojoldu.blogcode.springbootbatch2.readupdate; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.batch.core.BatchStatus; +import org.springframework.batch.core.JobExecution; +import org.springframework.batch.test.JobLauncherTestUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.TestPropertySource; +import org.springframework.test.context.junit4.SpringRunner; + +import static org.hamcrest.CoreMatchers.is; +import static org.junit.Assert.assertThat; + +/** + * Created by jojoldu@gmail.com on 2018. 9. 15. + * Blog : http://jojoldu.tistory.com + * Github : https://github.com/jojoldu + */ + +@RunWith(SpringRunner.class) +@SpringBootTest +@TestPropertySource(properties = {"job.name=" + PayCursorJobConfiguration.JOB_NAME}) +public class PayCursorJobConfigurationTest { + + @Autowired + private JobLauncherTestUtils jobLauncherTestUtils; + + @Autowired + private PayRepository payRepository; + + @Test + public void 같은조건을읽고_업데이트할때() throws Exception { + //given + for (long i = 0; i < 50; i++) { + payRepository.save(new Pay(i, false)); + } + + //when + JobExecution jobExecution = jobLauncherTestUtils.launchJob(); + + //then + assertThat(jobExecution.getStatus(), is(BatchStatus.COMPLETED)); + assertThat(payRepository.findAllSuccess().size(), is(50)); + + } +} diff --git a/springboot-batch-2/src/test/java/com/jojoldu/blogcode/springbootbatch2/readupdate/PayPagingFailJobConfigurationTest.java b/springboot-batch-2/src/test/java/com/jojoldu/blogcode/springbootbatch2/readupdate/PayPagingFailJobConfigurationTest.java new file mode 100644 index 000000000..f54958351 --- /dev/null +++ b/springboot-batch-2/src/test/java/com/jojoldu/blogcode/springbootbatch2/readupdate/PayPagingFailJobConfigurationTest.java @@ -0,0 +1,48 @@ +package com.jojoldu.blogcode.springbootbatch2.readupdate; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.batch.core.BatchStatus; +import org.springframework.batch.core.JobExecution; +import org.springframework.batch.test.JobLauncherTestUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.TestPropertySource; +import org.springframework.test.context.junit4.SpringRunner; + +import static org.hamcrest.CoreMatchers.is; +import static org.junit.Assert.assertThat; + +/** + * Created by jojoldu@gmail.com on 2018. 9. 15. + * Blog : http://jojoldu.tistory.com + * Github : https://github.com/jojoldu + */ + +@RunWith(SpringRunner.class) +@SpringBootTest +@TestPropertySource(properties = {"job.name=" + PayPagingFailJobConfiguration.JOB_NAME}) +public class PayPagingFailJobConfigurationTest { + + @Autowired + private JobLauncherTestUtils jobLauncherTestUtils; + + @Autowired + private PayRepository payRepository; + + @Test + public void 같은조건을읽고_업데이트할때() throws Exception { + //given + for (long i = 0; i < 50; i++) { + payRepository.save(new Pay(i, false)); + } + + //when + JobExecution jobExecution = jobLauncherTestUtils.launchJob(); + + //then + assertThat(jobExecution.getStatus(), is(BatchStatus.COMPLETED)); + assertThat(payRepository.findAllSuccess().size(), is(50)); + + } +} diff --git a/springboot-batch-2/src/test/java/com/jojoldu/blogcode/springbootbatch2/readupdate/PayPagingJobConfigurationTest.java b/springboot-batch-2/src/test/java/com/jojoldu/blogcode/springbootbatch2/readupdate/PayPagingJobConfigurationTest.java new file mode 100644 index 000000000..8e04d874e --- /dev/null +++ b/springboot-batch-2/src/test/java/com/jojoldu/blogcode/springbootbatch2/readupdate/PayPagingJobConfigurationTest.java @@ -0,0 +1,48 @@ +package com.jojoldu.blogcode.springbootbatch2.readupdate; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.batch.core.BatchStatus; +import org.springframework.batch.core.JobExecution; +import org.springframework.batch.test.JobLauncherTestUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.TestPropertySource; +import org.springframework.test.context.junit4.SpringRunner; + +import static org.hamcrest.CoreMatchers.is; +import static org.junit.Assert.assertThat; + +/** + * Created by jojoldu@gmail.com on 2018. 9. 15. + * Blog : http://jojoldu.tistory.com + * Github : https://github.com/jojoldu + */ + +@RunWith(SpringRunner.class) +@SpringBootTest +@TestPropertySource(properties = {"job.name=" + PayPagingJobConfiguration.JOB_NAME}) +public class PayPagingJobConfigurationTest { + + @Autowired + private JobLauncherTestUtils jobLauncherTestUtils; + + @Autowired + private PayRepository payRepository; + + @Test + public void 같은조건을읽고_업데이트할때() throws Exception { + //given + for (long i = 0; i < 50; i++) { + payRepository.save(new Pay(i, false)); + } + + //when + JobExecution jobExecution = jobLauncherTestUtils.launchJob(); + + //then + assertThat(jobExecution.getStatus(), is(BatchStatus.COMPLETED)); + assertThat(payRepository.findAllSuccess().size(), is(50)); + + } +} diff --git a/springboot-batch-2/src/test/resources/application.yml b/springboot-batch-2/src/test/resources/application.yml new file mode 100644 index 000000000..efe46591e --- /dev/null +++ b/springboot-batch-2/src/test/resources/application.yml @@ -0,0 +1,10 @@ +spring: + h2: + console: + enabled: true + jpa: + show-sql: true + hibernate: + ddl-auto: update + +spring.batch.job.names: ${job.name:NONE}