diff --git a/pom.xml b/pom.xml index 38d49b1..0c06eb7 100644 --- a/pom.xml +++ b/pom.xml @@ -14,29 +14,46 @@ spring-batch-lecture Demo project for Spring Boot - 11 + 1.8 org.springframework.boot spring-boot-starter-batch - + + org.springframework.boot + spring-boot-starter-data-jpa + + + org.springframework.batch + spring-batch-integration + mysql mysql-connector-java runtime - org.springframework.boot - spring-boot-configuration-processor - true + com.h2database + h2 + runtime org.projectlombok lombok true + + org.springframework + spring-oxm + 5.3.7 + + + com.thoughtworks.xstream + xstream + 1.4.16 + org.springframework.boot spring-boot-starter-test diff --git a/src/main/java/io/springbatch/springbatchlecture/AsyncConfiguration.java b/src/main/java/io/springbatch/springbatchlecture/AsyncConfiguration.java new file mode 100644 index 0000000..f86c7c3 --- /dev/null +++ b/src/main/java/io/springbatch/springbatchlecture/AsyncConfiguration.java @@ -0,0 +1,151 @@ +package io.springbatch.springbatchlecture; + +import lombok.RequiredArgsConstructor; +import org.springframework.batch.core.*; +import org.springframework.batch.core.configuration.annotation.JobBuilderFactory; +import org.springframework.batch.core.configuration.annotation.StepBuilderFactory; +import org.springframework.batch.core.launch.support.RunIdIncrementer; +import org.springframework.batch.integration.async.AsyncItemProcessor; +import org.springframework.batch.integration.async.AsyncItemWriter; +import org.springframework.batch.item.ItemProcessor; +import org.springframework.batch.item.database.BeanPropertyItemSqlParameterSourceProvider; +import org.springframework.batch.item.database.JdbcBatchItemWriter; +import org.springframework.batch.item.database.JdbcPagingItemReader; +import org.springframework.batch.item.database.Order; +import org.springframework.batch.item.database.support.MySqlPagingQueryProvider; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.core.task.SimpleAsyncTaskExecutor; +import org.springframework.core.task.TaskExecutor; +import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; + +import javax.sql.DataSource; +import java.util.*; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.ThreadPoolExecutor; + +@RequiredArgsConstructor +@Configuration +public class AsyncConfiguration { + + private final JobBuilderFactory jobBuilderFactory; + private final StepBuilderFactory stepBuilderFactory; + private final DataSource dataSource; + + @Bean + public Job job() throws Exception { + return jobBuilderFactory.get("batchJob") + .incrementer(new RunIdIncrementer()) +// .start(step1()) + .start(asyncStep1()) + .listener(new StopWatchJobListener()) + .build(); + } + + @Bean + public Step step1() throws Exception { + return stepBuilderFactory.get("step1") + .chunk(100) + .reader(pagingItemReader()) + .processor(customItemProcessor()) + .writer(customItemWriter()) + .build(); + } + + @Bean + public Step asyncStep1() throws Exception { + return stepBuilderFactory.get("asyncStep1") + .chunk(100) + .reader(pagingItemReader()) + .processor(asyncItemProcessor()) + .writer(asyncItemWriter()) + .taskExecutor(taskExecutor()) + .build(); + } + + @Bean + public JdbcPagingItemReader pagingItemReader() { + JdbcPagingItemReader reader = new JdbcPagingItemReader<>(); + + reader.setDataSource(this.dataSource); + reader.setPageSize(100); + reader.setRowMapper(new CustomerRowMapper()); + + MySqlPagingQueryProvider queryProvider = new MySqlPagingQueryProvider(); + queryProvider.setSelectClause("id, firstName, lastName, birthdate"); + queryProvider.setFromClause("from customer"); + + Map sortKeys = new HashMap<>(1); + + sortKeys.put("id", Order.ASCENDING); + + queryProvider.setSortKeys(sortKeys); + + reader.setQueryProvider(queryProvider); + + return reader; + } + + @Bean + public ItemProcessor customItemProcessor() { + return new ItemProcessor() { + @Override + public Customer process(Customer item) throws Exception { + + Thread.sleep(1000); + + return new Customer(item.getId(), + item.getFirstName().toUpperCase(), + item.getLastName().toUpperCase(), + item.getBirthdate()); + } + }; + } + + @Bean + public JdbcBatchItemWriter customItemWriter() { + JdbcBatchItemWriter itemWriter = new JdbcBatchItemWriter<>(); + + itemWriter.setDataSource(this.dataSource); + itemWriter.setSql("insert into customer2 values (:id, :firstName, :lastName, :birthdate)"); + itemWriter.setItemSqlParameterSourceProvider(new BeanPropertyItemSqlParameterSourceProvider()); + itemWriter.afterPropertiesSet(); + + return itemWriter; + } + + @Bean + public AsyncItemProcessor asyncItemProcessor() throws Exception { + AsyncItemProcessor asyncItemProcessor = new AsyncItemProcessor(); + + asyncItemProcessor.setDelegate(customItemProcessor()); + asyncItemProcessor.setTaskExecutor(new SimpleAsyncTaskExecutor()); +// asyncItemProcessor.setTaskExecutor(taskExecutor()); + asyncItemProcessor.afterPropertiesSet(); + + return asyncItemProcessor; + } + + @Bean + public AsyncItemWriter asyncItemWriter() throws Exception { + AsyncItemWriter asyncItemWriter = new AsyncItemWriter<>(); + + asyncItemWriter.setDelegate(customItemWriter()); + asyncItemWriter.afterPropertiesSet(); + + return asyncItemWriter; + } + + + + @Bean + public TaskExecutor taskExecutor(){ + ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor(); + executor.setCorePoolSize(4); + executor.setMaxPoolSize(8); + executor.setThreadNamePrefix("async-thread-"); + return executor; + } +} + diff --git a/src/main/java/io/springbatch/springbatchlecture/Customer.java b/src/main/java/io/springbatch/springbatchlecture/Customer.java new file mode 100644 index 0000000..d1559d6 --- /dev/null +++ b/src/main/java/io/springbatch/springbatchlecture/Customer.java @@ -0,0 +1,17 @@ + +package io.springbatch.springbatchlecture; + +import lombok.AllArgsConstructor; +import lombok.Data; + +import java.util.Date; + +@Data +@AllArgsConstructor +public class Customer { + + private final long id; + private final String firstName; + private final String lastName; + private final Date birthdate; +} diff --git a/src/main/java/io/springbatch/springbatchlecture/CustomerRowMapper.java b/src/main/java/io/springbatch/springbatchlecture/CustomerRowMapper.java new file mode 100644 index 0000000..fe34382 --- /dev/null +++ b/src/main/java/io/springbatch/springbatchlecture/CustomerRowMapper.java @@ -0,0 +1,16 @@ +package io.springbatch.springbatchlecture; + +import org.springframework.jdbc.core.RowMapper; + +import java.sql.ResultSet; +import java.sql.SQLException; + +public class CustomerRowMapper implements RowMapper { + @Override + public Customer mapRow(ResultSet rs, int i) throws SQLException { + return new Customer(rs.getLong("id"), + rs.getString("firstName"), + rs.getString("lastName"), + rs.getDate("birthdate")); + } +} diff --git a/src/main/java/io/springbatch/springbatchlecture/SpringBatchLectureApplication.java b/src/main/java/io/springbatch/springbatchlecture/SpringBatchLectureApplication.java index 0d8b2d4..8930a69 100644 --- a/src/main/java/io/springbatch/springbatchlecture/SpringBatchLectureApplication.java +++ b/src/main/java/io/springbatch/springbatchlecture/SpringBatchLectureApplication.java @@ -1,9 +1,11 @@ package io.springbatch.springbatchlecture; +import org.springframework.batch.core.configuration.annotation.EnableBatchProcessing; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication +@EnableBatchProcessing public class SpringBatchLectureApplication { public static void main(String[] args) { diff --git a/src/main/java/io/springbatch/springbatchlecture/StopWatchJobListener.java b/src/main/java/io/springbatch/springbatchlecture/StopWatchJobListener.java new file mode 100644 index 0000000..98d4c25 --- /dev/null +++ b/src/main/java/io/springbatch/springbatchlecture/StopWatchJobListener.java @@ -0,0 +1,19 @@ +package io.springbatch.springbatchlecture; + +import org.springframework.batch.core.JobExecution; +import org.springframework.batch.core.JobExecutionListener; + +public class StopWatchJobListener implements JobExecutionListener { + + @Override + public void beforeJob(JobExecution jobExecution) { + } + + @Override + public void afterJob(JobExecution jobExecution) { + long time = jobExecution.getEndTime().getTime() - jobExecution.getStartTime().getTime(); + System.out.println("=========================================="); + System.out.println("총 소요된 시간 : " + time); + System.out.println("=========================================="); + } +} diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties deleted file mode 100644 index 8b13789..0000000 --- a/src/main/resources/application.properties +++ /dev/null @@ -1 +0,0 @@ - diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml new file mode 100644 index 0000000..4fb13ef --- /dev/null +++ b/src/main/resources/application.yml @@ -0,0 +1,37 @@ +spring: + profiles: + active: local + batch: + job: +# enabled: false + names: ${job.name:NONE} + +--- +spring: + config: + activate: + on-profile: local + datasource: + hikari: + jdbc-url: jdbc:h2:mem:testdb;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=FALSE + username: sa + password: + driver-class-name: org.h2.Driver + batch: + jdbc: + initialize-schema: embedded + +--- +spring: + config: + activate: + on-profile: mysql + datasource: + hikari: + jdbc-url: jdbc:mysql://localhost:3306/springbatch?useUnicode=true&characterEncoding=utf8 + username: root + password: pass + driver-class-name: com.mysql.jdbc.Driver + batch: + jdbc: + initialize-schema: always \ No newline at end of file diff --git a/src/main/resources/customer.json b/src/main/resources/customer.json new file mode 100644 index 0000000..e69de29