diff --git a/build.gradle b/build.gradle index 0666277f..b4d78963 100644 --- a/build.gradle +++ b/build.gradle @@ -23,6 +23,7 @@ configurations { repositories { mavenCentral() + maven { url 'https://jitpack.io' } } dependencies { @@ -82,6 +83,8 @@ dependencies { // https://mvnrepository.com/artifact/org.redisson/redisson-spring-boot-starter implementation 'org.redisson:redisson-spring-boot-starter:3.18.0' + + implementation 'com.github.ywj9811:QueryDslItemReader:v1.0.0' } tasks.named('test') { diff --git a/src/main/java/com/postgraduate/batch/cancel/CancelJobConfig.java b/src/main/java/com/postgraduate/batch/cancel/CancelJobConfig.java index 760226cb..7b24b499 100644 --- a/src/main/java/com/postgraduate/batch/cancel/CancelJobConfig.java +++ b/src/main/java/com/postgraduate/batch/cancel/CancelJobConfig.java @@ -1,5 +1,10 @@ package com.postgraduate.batch.cancel; +import com.postgraduate.domain.mentoring.domain.entity.Mentoring; +import com.querydslitemreader.core.pagingitemreader.expression.Expression; +import com.querydslitemreader.core.pagingitemreader.options.QueryDslNoOffsetNumberOptions; +import com.querydslitemreader.core.pagingitemreader.options.QueryDslNoOffsetPagingItemReader; +import jakarta.persistence.EntityManagerFactory; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.batch.core.Job; @@ -7,20 +12,17 @@ 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.item.database.JdbcPagingItemReader; -import org.springframework.batch.item.database.PagingQueryProvider; -import org.springframework.batch.item.database.builder.JdbcPagingItemReaderBuilder; -import org.springframework.batch.item.database.support.SqlPagingQueryProviderFactoryBean; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.transaction.PlatformTransactionManager; -import javax.sql.DataSource; -import java.sql.Timestamp; import java.time.LocalDateTime; -import java.util.HashMap; -import java.util.Map; +import static com.postgraduate.domain.mentoring.domain.entity.QMentoring.mentoring; +import static com.postgraduate.domain.mentoring.domain.entity.constant.Status.WAITING; +import static com.postgraduate.domain.payment.domain.entity.QPayment.payment; +import static com.postgraduate.domain.senior.domain.entity.QSenior.senior; +import static com.postgraduate.domain.user.domain.entity.QUser.user; import static java.time.LocalDateTime.now; @Configuration @@ -29,23 +31,25 @@ public class CancelJobConfig { private final JobRepository jobRepository; private final PlatformTransactionManager transactionManager; + private final CancelMentoringProcessor cancelMentoringProcessor; private final CancelMentoringWriter cancelMentoringWriter; - private final DataSource dataSource; + private final EntityManagerFactory entityManagerFactory; private static final int CHUNK_SIZE = 50; @Bean(name = "cancelJob") - public Job cancelJob() throws Exception { + public Job cancelJob() { return new JobBuilder("cancelJob", jobRepository) .start(cancelStep()) .build(); } @Bean(name = "cancelStep") - public Step cancelStep() throws Exception { + public Step cancelStep() { return new StepBuilder("cancelStep", jobRepository) - .chunk(CHUNK_SIZE, transactionManager) + .chunk(CHUNK_SIZE, transactionManager) .reader(itemReader()) + .processor(cancelMentoringProcessor) .writer(cancelMentoringWriter) .faultTolerant() .skip(Exception.class) @@ -54,33 +58,27 @@ public Step cancelStep() throws Exception { } @Bean(name = "cancelReader") - public JdbcPagingItemReader itemReader() throws Exception { - Map parameter = new HashMap<>(); + public QueryDslNoOffsetPagingItemReader itemReader() { + QueryDslNoOffsetNumberOptions options = new QueryDslNoOffsetNumberOptions<>(mentoring.mentoringId, Expression.DESC); + LocalDateTime now = now() .toLocalDate() .atStartOfDay(); - parameter.put("date", Timestamp.valueOf(now)); - - return new JdbcPagingItemReaderBuilder() - .pageSize(CHUNK_SIZE) - .fetchSize(CHUNK_SIZE) - .dataSource(dataSource) - .rowMapper(new CancelMentoringRowMapper()) - .queryProvider(cancelQueryProvider()) - .parameterValues(parameter) - .name("cancelReader") - .build(); - } - @Bean(name = "cancelQuery") - public PagingQueryProvider cancelQueryProvider() throws Exception { - SqlPagingQueryProviderFactoryBean queryProvider = new SqlPagingQueryProviderFactoryBean(); - queryProvider.setDataSource(dataSource); - queryProvider.setSelectClause("select mentoring_id, user_user_id, senior_senior_id, payment_payment_id"); - queryProvider.setFromClause("from mentoring"); - queryProvider.setWhereClause("where status = 'WAITING' and created_at < :date"); - queryProvider.setSortKey("mentoring_id"); - return queryProvider.getObject(); + return new QueryDslNoOffsetPagingItemReader<>(entityManagerFactory, CHUNK_SIZE, options, queryFactory -> + queryFactory.selectFrom(mentoring) + .distinct() + .join(payment) + .on(mentoring.payment.eq(payment)) + .fetchJoin() + .join(user) + .on(mentoring.user.eq(user)) + .fetchJoin() + .join(senior) + .on(mentoring.senior.eq(senior)) + .fetchJoin() + .where(mentoring.status.eq(WAITING).and(mentoring.createdAt.before(now))) + ); } } diff --git a/src/main/java/com/postgraduate/batch/cancel/CancelMentoringProcessor.java b/src/main/java/com/postgraduate/batch/cancel/CancelMentoringProcessor.java new file mode 100644 index 00000000..5cb0a8e0 --- /dev/null +++ b/src/main/java/com/postgraduate/batch/cancel/CancelMentoringProcessor.java @@ -0,0 +1,19 @@ +package com.postgraduate.batch.cancel; + +import com.postgraduate.domain.mentoring.domain.entity.Mentoring; +import com.postgraduate.domain.payment.domain.entity.Payment; +import com.postgraduate.domain.senior.domain.entity.Senior; +import com.postgraduate.domain.user.domain.entity.User; +import org.springframework.batch.item.ItemProcessor; +import org.springframework.stereotype.Component; + +@Component +public class CancelMentoringProcessor implements ItemProcessor { + @Override + public CancelMentoring process(Mentoring mentoring) throws Exception { + User user = mentoring.getUser(); + Senior senior = mentoring.getSenior(); + Payment payment = mentoring.getPayment(); + return new CancelMentoring(mentoring.getMentoringId(), user.getUserId(), senior.getSeniorId(), payment.getPaymentId()); + } +} diff --git a/src/main/java/com/postgraduate/batch/cancel/CancelMentoringRowMapper.java b/src/main/java/com/postgraduate/batch/cancel/CancelMentoringRowMapper.java deleted file mode 100644 index fdcd9d7f..00000000 --- a/src/main/java/com/postgraduate/batch/cancel/CancelMentoringRowMapper.java +++ /dev/null @@ -1,18 +0,0 @@ -package com.postgraduate.batch.cancel; - -import org.springframework.jdbc.core.RowMapper; - -import java.sql.ResultSet; -import java.sql.SQLException; - -public class CancelMentoringRowMapper implements RowMapper { - @Override - public CancelMentoring mapRow(ResultSet rs, int rowNum) throws SQLException { - return new CancelMentoring( - rs.getLong("mentoring_id"), - rs.getLong("user_user_id"), - rs.getLong("senior_senior_id"), - rs.getLong("payment_payment_id") - ); - } -} diff --git a/src/main/java/com/postgraduate/batch/done/DoneJobConfig.java b/src/main/java/com/postgraduate/batch/done/DoneJobConfig.java index f0966f78..3b2429b0 100644 --- a/src/main/java/com/postgraduate/batch/done/DoneJobConfig.java +++ b/src/main/java/com/postgraduate/batch/done/DoneJobConfig.java @@ -1,44 +1,51 @@ package com.postgraduate.batch.done; +import com.postgraduate.domain.mentoring.domain.entity.Mentoring; +import com.postgraduate.domain.mentoring.domain.entity.constant.Status; +import com.querydslitemreader.core.pagingitemreader.expression.Expression; +import com.querydslitemreader.core.pagingitemreader.options.QueryDslNoOffsetNumberOptions; +import com.querydslitemreader.core.pagingitemreader.options.QueryDslNoOffsetPagingItemReader; +import jakarta.persistence.EntityManagerFactory; 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.job.builder.JobBuilder; import org.springframework.batch.core.repository.JobRepository; import org.springframework.batch.core.step.builder.StepBuilder; -import org.springframework.batch.item.database.JdbcPagingItemReader; -import org.springframework.batch.item.database.PagingQueryProvider; -import org.springframework.batch.item.database.builder.JdbcPagingItemReaderBuilder; -import org.springframework.batch.item.database.support.SqlPagingQueryProviderFactoryBean; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.transaction.PlatformTransactionManager; -import javax.sql.DataSource; +import static com.postgraduate.domain.mentoring.domain.entity.QMentoring.mentoring; +import static com.postgraduate.domain.payment.domain.entity.QPayment.payment; +import static com.postgraduate.domain.salary.domain.entity.QSalary.salary; +import static com.postgraduate.domain.senior.domain.entity.QSenior.senior; @Configuration @RequiredArgsConstructor +@Slf4j public class DoneJobConfig { private final JobRepository jobRepository; private final PlatformTransactionManager transactionManager; - private final DataSource dataSource; private final DoneMentoringProcessor doneMentoringProcessor; private final DoneMentoringWriter doneMentoringWriter; private final DoneMentoringSkipListener doneMentoringSkipListener; + private final EntityManagerFactory entityManagerFactory; private static final int CHUNK_SIZE = 50; @Bean(name = "doneJob") - public Job doneJob() throws Exception { + public Job doneJob() { return new JobBuilder("doneJob", jobRepository) .start(doneStep()) .build(); } @Bean(name = "doneStep") - public Step doneStep() throws Exception { + public Step doneStep() { return new StepBuilder("doneStep", jobRepository) - .chunk(CHUNK_SIZE, transactionManager) + .chunk(CHUNK_SIZE, transactionManager) .reader(doneReader()) .processor(doneMentoringProcessor) .writer(doneMentoringWriter) @@ -50,25 +57,23 @@ public Step doneStep() throws Exception { } @Bean(name = "doneReader") - public JdbcPagingItemReader doneReader() throws Exception { - return new JdbcPagingItemReaderBuilder() - .pageSize(CHUNK_SIZE) - .fetchSize(CHUNK_SIZE) - .dataSource(dataSource) - .rowMapper(new DoneMentoringRowMapper()) - .queryProvider(doneQueryProvider()) - .name("doneReader") - .build(); - } + public QueryDslNoOffsetPagingItemReader doneReader() { + QueryDslNoOffsetNumberOptions options = + new QueryDslNoOffsetNumberOptions(mentoring.mentoringId, Expression.DESC); - @Bean(name = "doneQuery") - public PagingQueryProvider doneQueryProvider() throws Exception { - SqlPagingQueryProviderFactoryBean queryProvider = new SqlPagingQueryProviderFactoryBean(); - queryProvider.setDataSource(dataSource); - queryProvider.setSelectClause("select m.mentoring_id, m.senior_senior_id, m.salary_salary_id, m.date, p.pay"); - queryProvider.setFromClause("from mentoring m join payment p on m.payment_payment_id = p.payment_id"); - queryProvider.setWhereClause("where m.status = 'EXPECTED'"); - queryProvider.setSortKey("mentoring_id"); - return queryProvider.getObject(); + return new QueryDslNoOffsetPagingItemReader<>(entityManagerFactory, CHUNK_SIZE, options, queryFactory -> + queryFactory.selectFrom(mentoring) + .distinct() + .join(payment) + .on(mentoring.payment.eq(payment)) + .fetchJoin() + .join(senior) + .on(mentoring.senior.eq(senior)) + .fetchJoin() + .join(salary) + .on(mentoring.salary.eq(salary)) + .fetchJoin() + .where(mentoring.status.eq(Status.EXPECTED)) + ); } } diff --git a/src/main/java/com/postgraduate/batch/done/DoneMentoringProcessor.java b/src/main/java/com/postgraduate/batch/done/DoneMentoringProcessor.java index e1535553..5b02923b 100644 --- a/src/main/java/com/postgraduate/batch/done/DoneMentoringProcessor.java +++ b/src/main/java/com/postgraduate/batch/done/DoneMentoringProcessor.java @@ -1,5 +1,9 @@ package com.postgraduate.batch.done; +import com.postgraduate.domain.mentoring.domain.entity.Mentoring; +import com.postgraduate.domain.payment.domain.entity.Payment; +import com.postgraduate.domain.salary.domain.entity.Salary; +import com.postgraduate.domain.senior.domain.entity.Senior; import org.springframework.batch.item.ItemProcessor; import org.springframework.stereotype.Component; @@ -11,14 +15,17 @@ import static java.time.format.DateTimeFormatter.ofPattern; @Component -public class DoneMentoringProcessor implements ItemProcessor { +public class DoneMentoringProcessor implements ItemProcessor { @Override - public DoneMentoring process(DoneMentoring doneMentoring) { + public DoneMentoring process(Mentoring mentoring) { DateTimeFormatter formatter = ofPattern("yyyy-MM-dd-HH-mm"); - LocalDateTime doneDate = parse(doneMentoring.date(), formatter); - if (now().minusDays(3) - .isAfter(doneDate)) - return doneMentoring; + LocalDateTime doneDate = parse(mentoring.getDate(), formatter); + if (now().minusDays(3).isAfter(doneDate)) { + Senior senior = mentoring.getSenior(); + Salary salary = mentoring.getSalary(); + Payment payment = mentoring.getPayment(); + return new DoneMentoring(mentoring.getMentoringId(), senior.getSeniorId(), salary.getSalaryId(), mentoring.getDate(), payment.getPay()); + } return null; } } diff --git a/src/main/java/com/postgraduate/batch/salary/CreateSalaryJobConfig.java b/src/main/java/com/postgraduate/batch/salary/CreateSalaryJobConfig.java index 198a9894..e77f8381 100644 --- a/src/main/java/com/postgraduate/batch/salary/CreateSalaryJobConfig.java +++ b/src/main/java/com/postgraduate/batch/salary/CreateSalaryJobConfig.java @@ -3,6 +3,10 @@ import com.postgraduate.domain.salary.domain.entity.Salary; import com.postgraduate.domain.salary.domain.service.SalaryGetService; import com.postgraduate.global.slack.SlackSalaryMessage; +import com.querydsl.core.types.Projections; +import com.querydsl.jpa.JPAExpressions; +import com.querydslitemreader.core.pagingitemreader.QueryDslZeroPagingItemReader; +import jakarta.persistence.EntityManagerFactory; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.batch.core.Job; @@ -10,22 +14,19 @@ 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.item.database.JdbcPagingItemReader; -import org.springframework.batch.item.database.PagingQueryProvider; -import org.springframework.batch.item.database.builder.JdbcPagingItemReaderBuilder; -import org.springframework.batch.item.database.support.SqlPagingQueryProviderFactoryBean; import org.springframework.batch.repeat.RepeatStatus; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.transaction.PlatformTransactionManager; -import javax.sql.DataSource; import java.time.LocalDate; -import java.util.HashMap; import java.util.List; -import java.util.Map; +import static com.postgraduate.domain.account.domain.entity.QAccount.account; +import static com.postgraduate.domain.salary.domain.entity.QSalary.salary; import static com.postgraduate.domain.salary.util.SalaryUtil.getSalaryDate; +import static com.postgraduate.domain.senior.domain.entity.QSenior.senior; +import static com.postgraduate.domain.user.domain.entity.QUser.user; @Configuration @Slf4j @@ -37,12 +38,12 @@ public class CreateSalaryJobConfig { private final SalaryGetService salaryGetService; private final CreateSalaryItemWriter createSalaryItemWriter; private final CreateSalarySkipListener createSalarySkipListener; - private final DataSource dataSource; + private final EntityManagerFactory entityManagerFactory; private static final int CHUNK_SIZE = 50; @Bean(name = "salaryJob") - public Job salaryJob() throws Exception { + public Job salaryJob() { return new JobBuilder("salaryJob", jobRepository) .start(sendSlackStep()) .next(createSalaryStep()) @@ -61,7 +62,7 @@ public Step sendSlackStep() { } @Bean(name = "createSalaryStep") - public Step createSalaryStep() throws Exception { + public Step createSalaryStep() { return new StepBuilder("createSalaryStep", jobRepository) .chunk(CHUNK_SIZE, transactionManager) .reader(salaryReader()) @@ -73,38 +74,30 @@ public Step createSalaryStep() throws Exception { .build(); } - @Bean(name = "salaryReader") - public JdbcPagingItemReader salaryReader() throws Exception { - Map parameters = new HashMap<>(); - LocalDate salaryDate = getSalaryDate().plusDays(7); - parameters.put("salaryDate", salaryDate); - log.info("salaryReader salaryDate : {}", salaryDate); - return new JdbcPagingItemReaderBuilder() - .pageSize(CHUNK_SIZE) - .fetchSize(CHUNK_SIZE) - .dataSource(dataSource) - .rowMapper(new CreateSalaryRowMapper()) - .queryProvider(salaryQueryProvider()) - .parameterValues(parameters) - .name("salaryReader") - .build(); - } - - @Bean(name = "salaryQuery") - public PagingQueryProvider salaryQueryProvider() throws Exception { - SqlPagingQueryProviderFactoryBean queryProvider = new SqlPagingQueryProviderFactoryBean(); - queryProvider.setDataSource(dataSource); - queryProvider.setSelectClause("SELECT s.senior_id, a.bank, a.account_id, a.account_holder, a.account_number"); - queryProvider.setFromClause("FROM senior s\n" + - "JOIN user u ON s.user_user_id = u.user_id\n" + - "LEFT JOIN account a ON s.senior_id = a.senior_senior_id"); - queryProvider.setWhereClause("WHERE u.is_delete = false\n" + - "AND s.senior_id NOT IN (\n" + - "SELECT senior_senior_id\n" + - "FROM salary\n" + - "WHERE salary_date = :salaryDate\n" + - ")"); - queryProvider.setSortKey("senior_id"); - return queryProvider.getObject(); + @Bean + public QueryDslZeroPagingItemReader salaryReader() { + LocalDate date = getSalaryDate().plusDays(7); + return new QueryDslZeroPagingItemReader<>(entityManagerFactory, CHUNK_SIZE, queryFactory -> + queryFactory.select(Projections.constructor(CreateSalary.class, + senior.seniorId, + account.accountId, + account.bank, + account.accountNumber, + account.accountHolder)) + .distinct() + .from(senior) + .join(user) + .on(senior.user.eq(user)) + .leftJoin(account) + .on(account.senior.eq(senior)) + .where(user.isDelete.isFalse() + .and(senior.seniorId.notIn( + JPAExpressions + .select(salary.senior.seniorId) + .from(salary) + .where(salary.salaryDate.eq(date)) + ))) + .orderBy(senior.seniorId.desc()) + ); } } diff --git a/src/main/java/com/postgraduate/batch/salary/CreateSalaryRowMapper.java b/src/main/java/com/postgraduate/batch/salary/CreateSalaryRowMapper.java deleted file mode 100644 index dccd6887..00000000 --- a/src/main/java/com/postgraduate/batch/salary/CreateSalaryRowMapper.java +++ /dev/null @@ -1,19 +0,0 @@ -package com.postgraduate.batch.salary; - -import org.springframework.jdbc.core.RowMapper; - -import java.sql.ResultSet; -import java.sql.SQLException; - -public class CreateSalaryRowMapper implements RowMapper { - @Override - public CreateSalary mapRow(ResultSet rs, int rowNum) throws SQLException { - return new CreateSalary( - rs.getLong("senior_id"), - rs.getLong("account_id"), - rs.getString("bank"), - rs.getString("account_number"), - rs.getString("account_holder") - ); - } -} diff --git a/src/main/java/com/postgraduate/batch/scheduler/JobSchedulerConfig.java b/src/main/java/com/postgraduate/batch/scheduler/JobSchedulerConfig.java index 68a42e56..a2c675ac 100644 --- a/src/main/java/com/postgraduate/batch/scheduler/JobSchedulerConfig.java +++ b/src/main/java/com/postgraduate/batch/scheduler/JobSchedulerConfig.java @@ -16,8 +16,6 @@ import java.time.LocalDateTime; -import static com.postgraduate.domain.salary.util.SalaryUtil.*; - @Configuration @Slf4j @RequiredArgsConstructor @@ -49,7 +47,7 @@ public void launchDoneJob() throws JobInstanceAlreadyCompleteException, JobExecu @Scheduled(cron = "0 0 0 * * 4", zone = "Asia/Seoul") public void launchSalaryJob() throws JobInstanceAlreadyCompleteException, JobExecutionAlreadyRunningException, JobParametersInvalidException, JobRestartException { JobParameters jobParameters = new JobParametersBuilder() - .addLocalDateTime("date", LocalDateTime.from(getSalaryDate().plusDays(7))) + .addLocalDateTime("date", LocalDateTime.now()) .toJobParameters(); jobLauncher.run(salaryJob, jobParameters); }