-
Spring Batch 구현 - 3[공부] 프로그래밍/Spring・Spring Boot (JAVA) 2024. 4. 24. 15:32
♧ 전체 코드 : https://github.com/woodisco/pass-batch
GitHub - woodisco/pass-batch
Contribute to woodisco/pass-batch development by creating an account on GitHub.
github.com
이용권 일괄 지급 배치
① AddPassesJopConfig 작성
package com.fastcampus.pass.job.pass; import org.springframework.batch.core.Job; import org.springframework.batch.core.Step; import org.springframework.batch.core.job.builder.JobBuilder; import org.springframework.batch.core.repository.JobRepository; import org.springframework.batch.core.step.builder.StepBuilder; import org.springframework.batch.core.step.tasklet.Tasklet; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.transaction.PlatformTransactionManager; @Configuration public class AddPassesJopConfig { private final AddPassesTasklet addPassesTasklet; public AddPassesJopConfig(AddPassesTasklet addPassesTasklet) { this.addPassesTasklet = addPassesTasklet; } @Bean public Job addPassesJob(JobRepository jobRepository, Step addPassesStep) { return new JobBuilder("addPassesJob", jobRepository) .start(addPassesStep) .build(); } @Bean public Step addPassesStep(JobRepository jobRepository, PlatformTransactionManager transactionManager, Tasklet addPassesTasklet) { return new StepBuilder("addPassesStep", jobRepository) .tasklet(addPassesTasklet, transactionManager) .build(); } }
② AddPassesTasklet 작성 (Bean으로 등록)package com.fastcampus.pass.job.pass; import com.fastcampus.pass.repository.pass.*; import com.fastcampus.pass.repository.user.UserGroupMappingEntity; import com.fastcampus.pass.repository.user.UserGroupMappingRepository; import lombok.extern.slf4j.Slf4j; import org.springframework.batch.core.StepContribution; import org.springframework.batch.core.scope.context.ChunkContext; import org.springframework.batch.core.step.tasklet.Tasklet; import org.springframework.batch.repeat.RepeatStatus; import org.springframework.stereotype.Component; import java.time.LocalDateTime; import java.util.ArrayList; import java.util.List; @Slf4j @Component public class AddPassesTasklet implements Tasklet { private final PassRepository passRepository; private final BulkPassRepository bulkPassRepository; private final UserGroupMappingRepository userGroupMappingRepository; public AddPassesTasklet(PassRepository passRepository, BulkPassRepository bulkPassRepository, UserGroupMappingRepository userGroupMappingRepository) { this.passRepository = passRepository; this.bulkPassRepository = bulkPassRepository; this.userGroupMappingRepository = userGroupMappingRepository; } @Override public RepeatStatus execute(StepContribution contribution, ChunkContext chunkContext) throws Exception { // 이용권 시작 일시 1일 전 user group 내 각 사용자에게 이용권을 추가해줍니다. final LocalDateTime startedAt = LocalDateTime.now().minusDays(1); final List<BulkPassEntity> bulkPassEntities = bulkPassRepository.findByStatusAndStartedAtGreaterThan(BulkPassStatus.READY, startedAt); int count = 0; for (BulkPassEntity bulkPassEntity : bulkPassEntities) { // user group에 속한 userId들을 조회합니다. final List<String> userIds = userGroupMappingRepository.findByUserGroupId(bulkPassEntity.getUserGroupId()) .stream().map(UserGroupMappingEntity::getUserId).toList(); // 각 userId로 이용권을 추가합니다. count += addPasses(bulkPassEntity, userIds); // pass 추가 이후 상태를 COMPLETED로 업데이트합니다. bulkPassEntity.setStatus(BulkPassStatus.COMPLETED); } log.info("AddPassesTasklet - execute: 이용권 {}건 추가 완료, startedAt={}", count, startedAt); return RepeatStatus.FINISHED; } // bulkPass의 정보로 pass 데이터를 생성합니다. private int addPasses(BulkPassEntity bulkPassEntity, List<String> userIds) { List<PassEntity> passEntities = new ArrayList<>(); for (String userId : userIds) { PassEntity passEntity = PassModelMapper.INSTANCE.toPassEntity(bulkPassEntity, userId); passEntities.add(passEntity); } return passRepository.saveAll(passEntities).size(); } }
@Component :
@Component 주석은 클래스를 Spring 컴포넌트로 선언하는 데 사용됩니다. 이를 통해 Spring에게 클래스를 Bean으로 취급하고 해당 Bean을 관리하도록 지시합니다.
Spring 애플리케이션 콘텍스트에서 컴포넌트 스캐닝이 활성화되어 있는 경우, Spring은 자동으로 @Component로 주석이 달린 클래스를 감지하고 Spring 빈으로 등록합니다.
③ AddPassesTaskletTest 작성package com.fastcampus.pass.job.pass; import com.fastcampus.pass.repository.pass.*; import com.fastcampus.pass.repository.user.UserGroupMappingEntity; import com.fastcampus.pass.repository.user.UserGroupMappingRepository; import lombok.extern.slf4j.Slf4j; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.ArgumentCaptor; import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; import org.springframework.batch.core.StepContribution; import org.springframework.batch.core.scope.context.ChunkContext; import org.springframework.batch.repeat.RepeatStatus; import java.time.LocalDateTime; import java.util.List; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.mockito.ArgumentMatchers.*; import static org.mockito.Mockito.*; @Slf4j @ExtendWith(MockitoExtension.class) class AddPassesTaskletTest { @Mock private StepContribution stepContribution; @Mock private ChunkContext chunkContext; @Mock private PassRepository passRepository; @Mock private BulkPassRepository bulkPassRepository; @Mock private UserGroupMappingRepository userGroupMappingRepository; // @InjectMocks 클래스의 인스턴스를 생성하고 @Mock으로 생성된 객체를 주입합니다. @InjectMocks private AddPassesTasklet addPassesTasklet; @Test public void test_execute() throws Exception { // given final LocalDateTime now = LocalDateTime.now(); final String userId = "A1000000"; final String userGroupId = "GROUP"; BulkPassEntity bulkPassEntity = new BulkPassEntity(); bulkPassEntity.setPackageSeq(1); bulkPassEntity.setUserGroupId(userGroupId); bulkPassEntity.setStatus(BulkPassStatus.COMPLETED); bulkPassEntity.setCount(10); bulkPassEntity.setStartedAt(now.minusDays(60)); bulkPassEntity.setEndedAt(now); final UserGroupMappingEntity userGroupMappingEntity = new UserGroupMappingEntity(); userGroupMappingEntity.setUserGroupId(userGroupId); userGroupMappingEntity.setUserId(userId); // when when(bulkPassRepository.findByStatusAndStartedAtGreaterThan(eq(BulkPassStatus.READY), any())).thenReturn(List.of(bulkPassEntity)); when(userGroupMappingRepository.findByUserGroupId(eq(userGroupId))).thenReturn(List.of(userGroupMappingEntity)); RepeatStatus repeatStatus = addPassesTasklet.execute(stepContribution, chunkContext); // then assertEquals(RepeatStatus.FINISHED, repeatStatus); ArgumentCaptor<List> passEntitiesCaptor = ArgumentCaptor.forClass(List.class); // passRepository.saveAll이 1번 호출되었고, 매개변수가 passEntitiesCaptor로 캡처되었는지 확인합니다. verify(passRepository, times(1)).saveAll(passEntitiesCaptor.capture()); final List<PassEntity> passEntities = passEntitiesCaptor.getValue(); assertEquals(1, passEntities.size()); final PassEntity passEntity = passEntities.get(0); assertEquals(1, passEntity.getPackageSeq()); assertEquals(userId, passEntity.getUserId()); assertEquals(PassStatus.READY, passEntity.getStatus()); assertEquals(10, passEntity.getRemainingCount()); } }
출처 : 패스트캠퍼스 10개 프로젝트로 완성하는 백엔드 웹개발(Java/Spring) 초격차 패키지 Online
'[공부] 프로그래밍 > Spring・Spring Boot (JAVA)' 카테고리의 다른 글
Spring Batch 구현 - 4 (0) 2024.04.25 Mockito 기반의 테스트 (0) 2024.04.24 Spring Batch 구현 - 2 (0) 2024.04.18 Spring Batch 구현 - 1 (0) 2024.04.16 Scheduler 구현 (0) 2024.04.09