묻고 답해요
130만명의 커뮤니티!! 함께 토론해봐요.
인프런 TOP Writers
-
미해결스프링 배치
Flow 생성 시, flowBuilder에 end()와 build()를 각각 호출하는 이유
- 학습 관련 질문을 남겨주세요. 상세히 작성하면 더 좋아요! - 먼저 유사한 질문이 있었는지 검색해보세요. - 서로 예의를 지키며 존중하는 문화를 만들어가요. - 잠깐! 인프런 서비스 운영 관련 문의는 1:1 문의하기를 이용해주세요.안녕하세요, FlowJob 관련하여서 궁금한 점이 있어 문의드립니다.강의 중에 아래와 같이 작성된 코드가 있었는데요. @Bean public Flow flow() { FlowBuilder<Flow> flowBuilder = new FlowBuilder<>("flow"); flowBuilder.start(step1()) .next(step2()) .end(); return flowBuilder.build(); }FlowBuilder 내 end() 메소드를 살펴보면, end() 메소드 내에서 this.build()를 호출하는 것으로 보이는데요. public final Q end() { return this.build(); } 하기와 같이 작성하지 않고 위처럼 end()와 build()를 각각 쓰는 사유가 있는지 궁금합니다.@Bean public Flow flow() { FlowBuilder<Flow> flowBuilder = new FlowBuilder<>("flow"); return flowBuilder.start(step1()) .next(step2()) .end(); } 새해 복 많이 받으세요 :)
-
미해결스프링 배치
v5는 많은 것이 바뀌어서 test해보려는데 왜 Tasklet에 있는 System.out.println는 실행되지 않을까요??
v5로 test해보려는데 왜 Tasklet의 System.out.println는 찍히지 않는 걸까요??package me.victorsung.demobatch; import org.springframework.batch.core.Job; import org.springframework.batch.core.Step; import org.springframework.batch.core.StepContribution; import org.springframework.batch.core.configuration.support.DefaultBatchConfiguration; import org.springframework.batch.core.job.builder.JobBuilder; import org.springframework.batch.core.repository.JobRepository; import org.springframework.batch.core.scope.context.ChunkContext; import org.springframework.batch.core.step.builder.StepBuilder; import org.springframework.batch.core.step.tasklet.Tasklet; import org.springframework.batch.repeat.RepeatStatus; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.transaction.PlatformTransactionManager; @Configuration public class HelloJobConfiguration extends DefaultBatchConfiguration { @Bean public Job myJob(JobRepository jobRepository, Step myStep1, Step myStep2) { System.out.println("this is job"); return new JobBuilder("myJob", jobRepository) .start(myStep1) .next(myStep2) .build(); } @Bean public Step myStep1(JobRepository jobRepository, Tasklet myTasklet1, PlatformTransactionManager transactionManager) { System.out.println("this is step1"); return new StepBuilder("myStep1", jobRepository) .tasklet(myTasklet1, transactionManager) .build(); } @Bean public Step myStep2(JobRepository jobRepository, Tasklet myTasklet2, PlatformTransactionManager transactionManager) { System.out.println("this is step2"); return new StepBuilder("myStep2", jobRepository) .tasklet(myTasklet2, transactionManager) .build(); } @Bean public Tasklet myTasklet1() { System.out.println( """ this is myTasklet1 """ ); // Step에서는 Tasklet을 무한 반복 시킨다. 그래서 RepeatStatus을 null || RepeatStatus.FINISHED로 주어야 한번 실행하고 끝난다. return new Tasklet() { @Override public RepeatStatus execute(StepContribution contribution, ChunkContext chunkContext) throws Exception { System.out.println( """ myTasklet1 ============================================== >> contribution = %s >> chunkContext = %s ============================================== """.formatted(contribution, chunkContext) ); return RepeatStatus.FINISHED; } }; } @Bean public Tasklet myTasklet2() { System.out.println("this is taskLet2"); // Step에서는 Tasklet을 무한 반복 시킨다. 그래서 RepeatStatus을 null || RepeatStatus.FINISHED로 주어야 한번 실행하고 끝난다. return (contribution, chunkContext) -> { System.out.println("test2"); System.out.println( """ myTasklet2 ============================================== >> contribution = %s >> chunkContext = %s ============================================== """.formatted(contribution, chunkContext) ); return RepeatStatus.FINISHED; }; } } . ____ _ __ _ _ /\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \ ( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \ \\/ ___)| |_)| | | | | || (_| | ) ) ) ) ' |____| .__|_| |_|_| |_\__, | / / / / =========|_|==============|___/=/_/_/_/ :: Spring Boot :: (v3.0.0) 2022-12-06T11:11:34.542+09:00 INFO 87250 --- [ main] m.v.demobatch.DemoBatchApplication : Starting DemoBatchApplication using Java 17.0.4.1 with PID 87250 (/Users/victor/Documents/thecommerce/demo-batch/out/production/classes started by victor in /Users/victor/Documents/thecommerce/demo-batch) 2022-12-06T11:11:34.544+09:00 INFO 87250 --- [ main] m.v.demobatch.DemoBatchApplication : No active profile set, falling back to 1 default profile: "default" 2022-12-06T11:11:34.671+09:00 INFO 87250 --- [ main] o.s.b.c.c.annotation.BatchRegistrar : Bean jobRepository already defined in the application context, skipping the registration of a jobRepository 2022-12-06T11:11:34.671+09:00 INFO 87250 --- [ main] o.s.b.c.c.annotation.BatchRegistrar : Bean jobExplorer already defined in the application context, skipping the registration of a jobExplorer 2022-12-06T11:11:34.671+09:00 INFO 87250 --- [ main] o.s.b.c.c.annotation.BatchRegistrar : Bean jobLauncher already defined in the application context, skipping the registration of a jobLauncher 2022-12-06T11:11:34.671+09:00 INFO 87250 --- [ main] o.s.b.c.c.annotation.BatchRegistrar : Bean jobRegistry already defined in the application context, skipping the registration of a jobRegistry 2022-12-06T11:11:34.671+09:00 INFO 87250 --- [ main] o.s.b.c.c.annotation.BatchRegistrar : Bean jobOperator already defined in the application context, skipping the registration of a jobOperator 2022-12-06T11:11:34.671+09:00 INFO 87250 --- [ main] o.s.b.c.c.annotation.BatchRegistrar : Finished Spring Batch infrastructure beans configuration in 0 ms. 2022-12-06T11:11:34.784+09:00 WARN 87250 --- [ main] ocalVariableTableParameterNameDiscoverer : Using deprecated '-debug' fallback for parameter name resolution. Compile the affected code with '-parameters' instead or avoid its introspection: me.victorsung.demobatch.HelloJobConfiguration 2022-12-06T11:11:34.811+09:00 INFO 87250 --- [ main] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Starting... 2022-12-06T11:11:34.891+09:00 INFO 87250 --- [ main] com.zaxxer.hikari.pool.HikariPool : HikariPool-1 - Added connection conn0: url=jdbc:h2:mem:06a2d6ef-e350-45b8-8468-33c43eb10333 user=SA 2022-12-06T11:11:34.891+09:00 INFO 87250 --- [ main] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Start completed. this is myTasklet1 this is step1 2022-12-06T11:11:34.922+09:00 INFO 87250 --- [ main] .c.a.BatchObservabilityBeanPostProcessor : No Micrometer observation registry found, defaulting to ObservationRegistry.NOOP this is taskLet2 this is step2 2022-12-06T11:11:34.923+09:00 INFO 87250 --- [ main] .c.a.BatchObservabilityBeanPostProcessor : No Micrometer observation registry found, defaulting to ObservationRegistry.NOOP this is job 2022-12-06T11:11:34.925+09:00 INFO 87250 --- [ main] .c.a.BatchObservabilityBeanPostProcessor : No Micrometer observation registry found, defaulting to ObservationRegistry.NOOP 2022-12-06T11:11:34.985+09:00 INFO 87250 --- [ main] m.v.demobatch.DemoBatchApplication : Started DemoBatchApplication in 0.659 seconds (process running for 0.901) 2022-12-06T11:11:34.988+09:00 INFO 87250 --- [ionShutdownHook] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Shutdown initiated... 2022-12-06T11:11:34.990+09:00 INFO 87250 --- [ionShutdownHook] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Shutdown completed. Process finished with exit code 0
-
미해결스프링 배치
강의자료 문의드립니다.
안녕하세요. 제가 MAC을 사용해서요.강의자료 PDF로도 혹시 제공 가능한가요. ㅠㅠ 감사합니다.
-
미해결스프링 배치
batch 관련 빈들을 proxy 객체로 생성하는 이유가 뭘까요?
안녕하세요batch 관련 빈들을 proxy 객체로 생성하는 이유가 뭘까요?@EnableBatchProcessing 어노테이션에 의해 SimpleBatchConfiguration.java 설정이 적용되는데요!이 설정에서 배치와 관련한 중요 빈들을 생성하는것을 볼 수 있는데* jobRepository* jobLauncher* jobBuilderFactory* stepBuilderFactory궁금한점은 이 빈들에 대해 왜 프록시로(createLazyProxy()) 생성하는걸까요?보통 프록시로 생성되어야 하는 빈들은 빈후처리기에 의해서 필요에 따라 선택되는걸로 알고있거든요아마도, 빈초기화 시점에 BatchConfigurer.class와 과련이 있어 보이는데, 정확히 알지 못하겠습니다.
-
미해결스프링 배치
안녕하세요
안녕하세요job parameter로 seq(long) 타입을 인식을 못해서 Null Pointer exception이 나고 있네요 Mac에서 zsh 쓰고있는데 혹시 저같은 분 계실까요?
-
미해결스프링 배치
firstPageSql, NamedParameterJdbcTemplate null 로 들어가는데 왜그럴까요..
(사진)
-
미해결스프링 배치
동일한 JobInstance에 대해서는 JobExecution이 복사가 되나요?
안녕하세요? 공유자님의 강의 잘 보고 있습니다 스프링 배치 도메인 이해 - ExecutionContext 영상의 41:46부분을 보면 jobInstance는 하나이지만 JobExecution은 똑같은 데이터로 로우가 하나 더 생겼습니다.이같은 경우는 jobExecution2가 jobExecution1의 데이터를 기반으로 실행되고 DB에 복제되어 저장되었다고 보는게 맞을까요?
-
해결됨스프링 배치
빌드된 jar 파일로 jobParameter를 전달할 경우 DB
빌드된 jar 파일로 jobParameter를 전달할 경우 DB에 Parameter값이나 Job실행 정보들을 남기지 않는데 맞는건가요? 아니면 어떠한 설정 값 영향인가요?
-
미해결스프링 배치
빌드된 jar 파일로 jobParameter를 전달할 때 zsh 사용 시 no matches found
제 환경은 Mac에 Zsh을 설치하여 사용하고 있는데 위와 같은 에러가 발생하는 문제가 존재하였습니다. 해당 문제는 간단하게 해결할 수 있습니다. JobParmeters로 넘길 인자를 따옴표(')로 감싸서 던지면 해결됨을 확인할 수 있습니다. 이런 식으로 정상적으로 나옴을 확인할 수 있었습니다. 동일한 문제가 발생한 분들 참고하시면 도움이 되실 것 같습니다.
-
미해결스프링 배치
@StepScope + JpaItemReader에서 EntityManager Null Pointer exception 발생 문제 도와주세요!
안녕하세요, 강사님. 항상 좋은 강의 제공해주셔서 감사합니다. 다름이 아니라 이번에 강사님 강의 보고 복습하던 도중에 문제가 발생해서 해결 방법이 있을까 해서 여쭤보려고 글을 작성했습니다. 파티셔닝 Step을 생성해서 처리하는 것을 따라해보고 있는데, 강사님께서는 ItemReader를 Jdbc 계열로 사용하셨는데, 저는 JPA를 선호해서 JPAItemReader를 사용했습니다. 먼저 단위 테스트를 위해 @StepScope 없이 JPAItemReader를 사용할 경우, 문제 없이 동작하는 것을 확인했습니다. 그런데 JpaItemReader에 @StepScope를 다는 순간 Null Pointer Exception이 발생하는 것을 확인했습니다. 처음에는 JpaItemReader가 Proxy Bean으로 주입이 안되는가? 라고 생각을 했었는데, 디버그 모드를 타고 가보니 다음과 같은 위치에서 문제가 있는 것을 확인했습니다. 프록시 객체에 타겟 객체는 정상적으로 주입을 해주는데, 문제는 타겟 객체인 JpaItemReader에 entityManager가 null이기 때문에 Reader를 하는 과정에서 문제가 있다는 것을 알게 되었습니다. EntityManager는 위에서 볼 수 있듯이 doOpen() step에서 제공해주는 것으로 보이는데, doOpen() Step을 거치지 않아 문제가 있는 것 같습니다. 조금 더 확인해보니 AbstractStep의 Open 메서드에 전달되는 ItemStream의 Size가 0인 것을 보니... 이것에 대해 문제가 있는 것 같습니다. 혹시 이처럼 JpaItemReader에서 @StepScope로 사용 시, EntityManager가 주입되지 않는 경우는 어떻게 해결해야 할까요? 아래는 제가 PartitionStep을 구성하기 전에 각각의 ItemReader, ItemWriter, ItemProcessor에서 @StepScope가 정상적으로 동작하는지 확인하기 위해 작성한 코드이고, 이 코드를 돌릴 때 문제가 발생하는 것을 확인했습니다. package io.springbatch.springbatchlecture.retry.partitioning;import io.springbatch.springbatchlecture.dbitemreader.Customer;import io.springbatch.springbatchlecture.dbwriter.Customer2;import lombok.RequiredArgsConstructor;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.partition.support.Partitioner;import org.springframework.batch.core.partition.support.SimplePartitioner;import org.springframework.batch.item.ItemProcessor;import org.springframework.batch.item.ItemReader;import org.springframework.batch.item.ItemWriter;import org.springframework.batch.item.database.JdbcPagingItemReader;import org.springframework.batch.item.database.Order;import org.springframework.batch.item.database.builder.JdbcBatchItemWriterBuilder;import org.springframework.batch.item.database.builder.JdbcPagingItemReaderBuilder;import org.springframework.batch.item.database.builder.JpaPagingItemReaderBuilder;import org.springframework.beans.factory.annotation.Value;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;import org.springframework.core.task.SimpleAsyncTaskExecutor;import javax.persistence.EntityManagerFactory;import javax.sql.DataSource;import java.util.HashMap;import java.util.concurrent.atomic.AtomicLong;@Configuration@RequiredArgsConstructorpublic class SimpleTestConfig { private final JobBuilderFactory jobBuilderFactory; private final StepBuilderFactory stepBuilderFactory; private final EntityManagerFactory emf; private final DataSource dataSource; private AtomicLong myId = new AtomicLong(); @Bean public Job batchJob200() { return jobBuilderFactory.get("partitioningJob") .incrementer(new RunIdIncrementer()) .start(slaveStep()) .build(); } @Bean public Step slaveStep() { return stepBuilderFactory.get("slaveStepMaster") .<Customer, Customer2>chunk(1000)// .reader(pagingItemReader()) .reader(batchReader()) .writer(batchWriter()) .processor(batchProcessor()) .build(); } @Bean @StepScope public ItemProcessor<? super Customer, ? extends Customer2> batchProcessor() { System.out.println("itemProcessor Here"); return (ItemProcessor<Customer, Customer2>) item -> Customer2.builder() .id(myId.incrementAndGet()) .birthDate(item.getBirthDate()) .firstName(item.getFirstName()) .lastName(item.getLastName()) .build(); } @Bean @StepScope public ItemWriter<? super Customer2> batchWriter() { return new JdbcBatchItemWriterBuilder<Customer2>() .sql("INSERT INTO Customer2(customer2_id, birth_date, first_name, last_name) values (:id, :birthDate, :firstName, :lastName)") .dataSource(dataSource) .beanMapped() .build(); } @Bean @StepScope public ItemReader<? extends Customer> batchReader() { return new JpaPagingItemReaderBuilder<Customer>() .name("partitionStepJpaReader") .currentItemCount(0) .entityManagerFactory(emf) .maxItemCount(1000) .queryString("select c from Customer c") .build(); } @Bean @StepScope // 앞쪽 강의 봐야함. public JdbcPagingItemReader<Customer> pagingItemReader() { System.out.println("Target Created"); HashMap<String, Order> sortKeys = new HashMap<>(); sortKeys.put("customer_id", Order.ASCENDING); return new JdbcPagingItemReaderBuilder<Customer>() .name("pagingBuilder") .dataSource(dataSource) .fetchSize(1000) .beanRowMapper(Customer.class) .selectClause("customer_id, first_name, last_name, birth_date") .fromClause("from customer")// .whereClause("where customer_id >= " + minValue + " and customer_id <= " + maxValue) .sortKeys(sortKeys) .build(); } @Bean public Partitioner partitioner() { SimplePartitioner simplePartitioner = new SimplePartitioner(); simplePartitioner.partition(4); return simplePartitioner; }}
-
미해결스프링 배치
배치 작업 도중 실패할때
안녕하세요 강의 잘 보고있습니다. 데이터가 100개 있고, 청크 사이즈가 10이라고 할 때 0~9, 10 ~ 19 까지 성공하고 애플리케이션 문제 등으로 프로그램이 종료 혹은 실패가 되었을 때, 다시 0번부터 실행 안 하고, 20번 부터 실행을 하려면 어떻게 해야될까요? 몇번부터 몇번까지 실행했는지 기록하는 메타데이터용 테이블을 만들어서 기록해야되는 거 말고는 없나요?? (예를 들어 실패했을때 다시 실행하면, 실패한 곳부터 알아서 스프링 배치가 실행해주는..)
-
미해결스프링 배치
docker db (mysql) 이 죽는 현상이 있습니다.
안녕하세요. 좋은 강의 만들어주셔서 감사합니다. 아래와 같이 강좌와 유사하게 spring.datasource.hikari 로 mysql 설정을 한 후 간단한 job 을 실행시키면 docker 로 설치한 mysql db 가 죽는 현상이 있습니다. 혹시 hikari 설정이 부족해서 그런 것일까요? 강좌에서는 잘 진행이 되는것 같아서 질문드립니다. 그래서 주석 처리하고 기존에 하던 방식으로 spring.datasource 를 이용해서 mysql 설정을 했습니다. spring.datasoruce 는 datasource 설정인거 같고, spring.datasource.hikari 는 hikari connection pool 설정인거 같은데요. 헷갈리네요. 어디에 db 설정을 정의하는것이 기본일까요? spring: profiles: active: local---spring: config: activate: on-profile: local datasource:# hikari:# jdbc-url: jdbc:mysql://av-api01-dc.nfra.io:23306/aida_project?useUnicode=true&characterEncoding=utf8# username: root# password: root# driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=utf8 username: root password: root driver-class-name: com.mysql.cj.jdbc.Driver batch: jdbc: initialize-schema: always
-
미해결스프링 배치
EXITE_CODE 질문드립니다.
챕터 : 스프링 배치 실행 - flow강의명 : SimpleFlow 예제 해당 강의 13:59 부터 이해가 잘안되고 있습니다. flow1()의 step2에서 예외가 발생하면서 STEP_EXCUTION의 EXITE_CODE가 FAILED이므로flow3()을 실행했고이후 flow3() 그에 해당하는 모든 스탭이 COMPLETED인데 왜 JOB_EXCUTION의 EXITE_CODE가 FAILED가 되는걸까요?
-
미해결스프링 배치
JdbcCursorItemReader 쿼리 실행 시점과 ResultSet 동작 방식이 궁금합니다.
안녕하세요. AbstractCursorItemReader#doOpen() -> JdbcCursorItemReader#openCursor()에서 ......this.rs = preparedStatement.executeQuery();...... 해당 시점에 쿼리가 실행이 되고, 이후 커넥션을 쭉 유지한채 AbstractCursorItemReader#doRead() -> JdbcCursorItemReader#readCursor(rs, currentRow)가 메소드가 호출될 때마다 DB에서 1row씩 데이터를 꺼내오는 방식으로 동작하는 것일까요? openCursor에서 쿼리 실행 후 메모리에 쿼리 수행결과를 가지고 있다가 doRead() -> readCursor()를 수행하는건지 혼란이 와서 질문 드립니다. JpaCursorItemReader의 경우는 JpaCursorItemReader#doOpen()에서 쿼리 실행 후 결과를 메모리에 가지고 있다가 이때는 DB 커넥션 유지하지 않고, doRead() 호출시 한로우씩 반환하는 식으로 동작하는게 맞나요?
-
미해결스프링 배치
Spring batch + mybatis는 사용이 불가한지 궁금합니다.
Spring batch + mybatis는 사용이 불가한지 궁금합니다.
-
해결됨스프링 배치
EXIT_CODE 반영 문의
안녕하세요 강사님 한가지 궁금한게 있어 문의 남깁니다 flow1에서 failed 시점에 flow3으로 흐르도록 했을 때 flow3의 모든 스탭이 성공하여서 제 생각으로는 JobExecution 기록에 EXIT_CODE가 COMPLETED로 기록될 줄 알았는데 JobExecution 기록에 EXIT_CODE 가 FAILED로 기록되어 있습니다. JobExecution의 EXIT_CODE가 마지막 스탭이나 플로우의 상태값을 반영되는 것으로 인지하고 있었는데 위와 같은 경우와 같이 Job의 과정중 하나의 스탭이라도 실패하게 된다면 EXIT_CODE에는 FAILED로 기록이 되는건가요?