diff --git a/batch-quartz/pom.xml b/batch-quartz/pom.xml index 8354471..6750a24 100644 --- a/batch-quartz/pom.xml +++ b/batch-quartz/pom.xml @@ -5,7 +5,7 @@ org.springframework.boot spring-boot-starter-parent - 3.3.2 + 2.7.18 com.spring @@ -27,7 +27,7 @@ - 17 + 11 diff --git a/batch-quartz/src/main/java/com/spring/common/util/ApplicationUtil.java b/batch-quartz/src/main/java/com/spring/common/util/ApplicationUtil.java deleted file mode 100644 index 59f0292..0000000 --- a/batch-quartz/src/main/java/com/spring/common/util/ApplicationUtil.java +++ /dev/null @@ -1,63 +0,0 @@ -package com.spring.common.util; - -import org.springframework.web.context.ContextLoader; -import org.springframework.web.context.request.RequestAttributes; -import org.springframework.web.context.request.RequestContextHolder; -import org.springframework.web.context.request.ServletRequestAttributes; - -import jakarta.servlet.http.HttpServletRequest; -import jakarta.servlet.http.HttpServletResponse; -import jakarta.servlet.http.HttpSession; -import lombok.AccessLevel; -import lombok.NoArgsConstructor; - -@NoArgsConstructor(access = AccessLevel.PRIVATE) -public class ApplicationUtil { - - public static ServletRequestAttributes getServletRequestAttributes(){ - return (ServletRequestAttributes) RequestContextHolder.currentRequestAttributes(); - } - //Request 객체 얻기 - public static HttpServletRequest getRequest(){ - return getServletRequestAttributes().getRequest(); - } - - //Set Request Attribute - public static void setRequestAttributes(String key, Object obj){ - getServletRequestAttributes().setAttribute(key, obj, RequestAttributes.SCOPE_REQUEST); - } - //Get Request Attribute - public static Object getRequestAttributes(String key){ - return getServletRequestAttributes().getAttribute(key, RequestAttributes.SCOPE_REQUEST); - } - - //Session 객체 얻기 - public static HttpSession getSession(){ - return getRequest().getSession(); - } - - //Set Session Attributes - public static void setSessionAttributes(String key, Object obj){ - getServletRequestAttributes().setAttribute(key, obj, RequestAttributes.SCOPE_SESSION); - } - - //Get Session Attributes - public static Object getSessionAttributes(String key){ - return getServletRequestAttributes().getAttribute(key, RequestAttributes.SCOPE_SESSION); - } - - //HttpServletResponse 객체 얻기 - public static HttpServletResponse getResponse(){ - return getServletRequestAttributes().getResponse(); - } - - //beanName을 통해서 Bean을 얻을 수 있다. - public static Object getBean(String beanName){ - var context = ContextLoader.getCurrentWebApplicationContext(); - if (context == null) { - throw new IllegalStateException("WebApplicationContext를 찾을 수 없습니다."); - } - return context.getBean(beanName); - } - -} diff --git a/batch-quartz/src/main/java/com/spring/common/util/ContextUtils.java b/batch-quartz/src/main/java/com/spring/common/util/ContextUtils.java new file mode 100644 index 0000000..4d772a5 --- /dev/null +++ b/batch-quartz/src/main/java/com/spring/common/util/ContextUtils.java @@ -0,0 +1,69 @@ +package com.spring.common.util; + +import java.util.concurrent.atomic.AtomicReference; + +import org.springframework.beans.BeansException; +import org.springframework.context.ApplicationContext; +import org.springframework.context.ApplicationContextAware; +import org.springframework.lang.NonNull; +import org.springframework.stereotype.Component; + +/** + * Spring ApplicationContext에 접근하기 위한 유틸리티 클래스입니다. + * + *

이 클래스는 ApplicationContextAware를 구현하여 ApplicationContext를 저장하고, + * 애플리케이션 전역에서 Spring 빈에 접근할 수 있는 정적 메소드를 제공합니다.

+ * + * @author mindol + * @version 1.0 + */ +@Component +public class ContextUtils implements ApplicationContextAware { + + private static final AtomicReference applicationContext = new AtomicReference<>(); + + /** + * Spring에 의해 호출되어 ApplicationContext를 설정합니다. + * + * @param context 설정할 ApplicationContext + * @throws BeansException 빈 예외 발생 시 + */ + @Override + public void setApplicationContext(@NonNull ApplicationContext context) throws BeansException { + applicationContext.set(context); + } + + /** + * 지정된 이름과 타입의 빈을 반환합니다. + * + * @param 반환될 빈의 타입 + * @param beanName 찾을 빈의 이름 + * @param clazz 반환될 빈의 클래스 + * @return 지정된 이름과 타입의 빈 + * @throws IllegalStateException ApplicationContext가 설정되지 않은 경우 + */ + public static T getBean(String beanName, Class clazz) { + ApplicationContext context = applicationContext.get(); + if (context == null) { + throw new IllegalStateException("ApplicationContext가 설정되지 않았습니다."); + } + return context.getBean(beanName, clazz); + } + + /** + * 지정된 타입의 빈을 반환합니다. + * + * @param 반환될 빈의 타입 + * @param clazz 반환될 빈의 클래스 + * @return 지정된 타입의 빈 + * @throws IllegalStateException ApplicationContext가 설정되지 않은 경우 + */ + public static T getBean(Class clazz) { + ApplicationContext context = applicationContext.get(); + if (context == null) { + throw new IllegalStateException("ApplicationContext가 설정되지 않았습니다."); + } + return context.getBean(clazz); + } + +} diff --git a/batch-quartz/src/main/java/com/spring/common/util/ServletUtils.java b/batch-quartz/src/main/java/com/spring/common/util/ServletUtils.java new file mode 100644 index 0000000..dc6fb54 --- /dev/null +++ b/batch-quartz/src/main/java/com/spring/common/util/ServletUtils.java @@ -0,0 +1,117 @@ +package com.spring.common.util; + +import java.util.Optional; +import java.util.function.Function; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import javax.servlet.http.HttpSession; + +import org.springframework.web.context.request.RequestAttributes; +import org.springframework.web.context.request.RequestContextHolder; +import org.springframework.web.context.request.ServletRequestAttributes; + +import lombok.AccessLevel; +import lombok.NoArgsConstructor; + +/** + * 서블릿 관련 유틸리티 클래스입니다. + * + *

현재 요청의 HttpServletRequest, HttpServletResponse, HttpSession 등에 접근하는 메서드를 제공합니다.

+ * + * @author mindol + * @version 1.0 + */ +@NoArgsConstructor(access = AccessLevel.PRIVATE) +public class ServletUtils { + + /** + * 현재 요청의 ServletRequestAttributes를 Optional로 반환합니다. + * + * @return ServletRequestAttributes를 포함한 Optional 객체 + */ + private static Optional getAttributes() { + return Optional.ofNullable(RequestContextHolder.getRequestAttributes()) + .filter(ServletRequestAttributes.class::isInstance) + .map(ServletRequestAttributes.class::cast); + } + + /** + * ServletRequestAttributes에서 특정 속성을 추출합니다. + * + * @param getter ServletRequestAttributes에서 원하는 속성을 추출하는 함수 + * @param 반환될 속성의 타입 + * @return 추출된 속성 또는 null + */ + private static T getAttribute(Function getter) { + return getAttributes().map(getter).orElse(null); + } + + /** + * 현재 요청의 HttpServletRequest를 반환합니다. + * + * @return 현재 HttpServletRequest 또는 null + */ + public static HttpServletRequest getRequest() { + return getAttribute(ServletRequestAttributes::getRequest); + } + + /** + * 현재 요청의 속성을 설정합니다. + * + * @param key 속성의 키 + * @param obj 설정할 속성 값 + */ + public static void setRequestAttribute(String key, Object obj) { + getAttributes().ifPresent(attr -> attr.setAttribute(key, obj, RequestAttributes.SCOPE_REQUEST)); + } + + /** + * 현재 요청의 속성을 반환합니다. + * + * @param key 속성의 키 + * @return 요청 속성 값 또는 null + */ + public static Object getRequestAttribute(String key) { + return getAttribute(attr -> attr.getAttribute(key, RequestAttributes.SCOPE_REQUEST)); + } + + /** + * 현재 요청의 HttpSession을 반환합니다. + * + * @return 현재 HttpSession 또는 null + */ + public static HttpSession getSession() { + return Optional.ofNullable(getRequest()).map(HttpServletRequest::getSession).orElse(null); + } + + /** + * 현재 세션의 속성을 설정합니다. + * + * @param key 속성의 키 + * @param obj 설정할 속성 값 + */ + public static void setSessionAttribute(String key, Object obj) { + getAttributes().ifPresent(attr -> attr.setAttribute(key, obj, RequestAttributes.SCOPE_SESSION)); + } + + /** + * 현재 세션의 속성을 반환합니다. + * + * @param key 속성의 키 + * @return 세션 속성 값 또는 null + */ + public static Object getSessionAttribute(String key) { + return getAttribute(attr -> attr.getAttribute(key, RequestAttributes.SCOPE_SESSION)); + } + + /** + * 현재 요청의 HttpServletResponse를 반환합니다. + * + * @return 현재 HttpServletResponse 또는 null + */ + public static HttpServletResponse getResponse() { + return getAttribute(ServletRequestAttributes::getResponse); + } + +} diff --git a/batch-quartz/src/main/java/com/spring/domain/batch/entity/BatchJobExecution.java b/batch-quartz/src/main/java/com/spring/domain/batch/entity/BatchJobExecution.java index fc77bdd..22a94ad 100644 --- a/batch-quartz/src/main/java/com/spring/domain/batch/entity/BatchJobExecution.java +++ b/batch-quartz/src/main/java/com/spring/domain/batch/entity/BatchJobExecution.java @@ -2,15 +2,14 @@ package com.spring.domain.batch.entity; import java.sql.Timestamp; -import jakarta.persistence.Column; -import jakarta.persistence.Entity; -import jakarta.persistence.ForeignKey; -import jakarta.persistence.GeneratedValue; -import jakarta.persistence.GenerationType; -import jakarta.persistence.Id; -import jakarta.persistence.JoinColumn; -import jakarta.persistence.ManyToOne; -import jakarta.persistence.Table; +import javax.persistence.Column; +import javax.persistence.ForeignKey; +import javax.persistence.GeneratedValue; +import javax.persistence.GenerationType; +import javax.persistence.Id; +import javax.persistence.JoinColumn; +import javax.persistence.ManyToOne; + import lombok.Getter; // @Entity diff --git a/batch-quartz/src/main/java/com/spring/domain/batch/entity/BatchJobExecutionContext.java b/batch-quartz/src/main/java/com/spring/domain/batch/entity/BatchJobExecutionContext.java index 794fe9c..439921e 100644 --- a/batch-quartz/src/main/java/com/spring/domain/batch/entity/BatchJobExecutionContext.java +++ b/batch-quartz/src/main/java/com/spring/domain/batch/entity/BatchJobExecutionContext.java @@ -1,12 +1,11 @@ package com.spring.domain.batch.entity; -import jakarta.persistence.Column; -import jakarta.persistence.Entity; -import jakarta.persistence.ForeignKey; -import jakarta.persistence.Id; -import jakarta.persistence.JoinColumn; -import jakarta.persistence.OneToOne; -import jakarta.persistence.Table; +import javax.persistence.Column; +import javax.persistence.ForeignKey; +import javax.persistence.Id; +import javax.persistence.JoinColumn; +import javax.persistence.OneToOne; + import lombok.Getter; // @Entity diff --git a/batch-quartz/src/main/java/com/spring/domain/batch/entity/BatchJobExecutionParams.java b/batch-quartz/src/main/java/com/spring/domain/batch/entity/BatchJobExecutionParams.java index f60f775..4ecbf58 100644 --- a/batch-quartz/src/main/java/com/spring/domain/batch/entity/BatchJobExecutionParams.java +++ b/batch-quartz/src/main/java/com/spring/domain/batch/entity/BatchJobExecutionParams.java @@ -2,14 +2,13 @@ package com.spring.domain.batch.entity; import java.io.Serializable; -import jakarta.persistence.Column; -import jakarta.persistence.Embeddable; -import jakarta.persistence.EmbeddedId; -import jakarta.persistence.Entity; -import jakarta.persistence.ForeignKey; -import jakarta.persistence.JoinColumn; -import jakarta.persistence.ManyToOne; -import jakarta.persistence.Table; +import javax.persistence.Column; +import javax.persistence.Embeddable; +import javax.persistence.EmbeddedId; +import javax.persistence.ForeignKey; +import javax.persistence.JoinColumn; +import javax.persistence.ManyToOne; + import lombok.EqualsAndHashCode; import lombok.Getter; @@ -37,7 +36,7 @@ public class BatchJobExecutionParams { @Embeddable @Getter @EqualsAndHashCode - public class BatchJobExecutionParamsId implements Serializable { + public static class BatchJobExecutionParamsId implements Serializable { @Column(name = "JOB_EXECUTION_ID") private Long jobExecutionId; diff --git a/batch-quartz/src/main/java/com/spring/domain/batch/entity/BatchJobInstance.java b/batch-quartz/src/main/java/com/spring/domain/batch/entity/BatchJobInstance.java index 135ba06..471b77d 100644 --- a/batch-quartz/src/main/java/com/spring/domain/batch/entity/BatchJobInstance.java +++ b/batch-quartz/src/main/java/com/spring/domain/batch/entity/BatchJobInstance.java @@ -1,12 +1,10 @@ package com.spring.domain.batch.entity; -import jakarta.persistence.Column; -import jakarta.persistence.Entity; -import jakarta.persistence.GeneratedValue; -import jakarta.persistence.GenerationType; -import jakarta.persistence.Id; -import jakarta.persistence.Table; -import jakarta.persistence.UniqueConstraint; +import javax.persistence.Column; +import javax.persistence.GeneratedValue; +import javax.persistence.GenerationType; +import javax.persistence.Id; + import lombok.Getter; // @Entity diff --git a/batch-quartz/src/main/java/com/spring/domain/batch/entity/BatchStepExecution.java b/batch-quartz/src/main/java/com/spring/domain/batch/entity/BatchStepExecution.java index d700071..9ebe1c6 100644 --- a/batch-quartz/src/main/java/com/spring/domain/batch/entity/BatchStepExecution.java +++ b/batch-quartz/src/main/java/com/spring/domain/batch/entity/BatchStepExecution.java @@ -2,15 +2,14 @@ package com.spring.domain.batch.entity; import java.sql.Timestamp; -import jakarta.persistence.Column; -import jakarta.persistence.Entity; -import jakarta.persistence.ForeignKey; -import jakarta.persistence.GeneratedValue; -import jakarta.persistence.GenerationType; -import jakarta.persistence.Id; -import jakarta.persistence.JoinColumn; -import jakarta.persistence.ManyToOne; -import jakarta.persistence.Table; +import javax.persistence.Column; +import javax.persistence.ForeignKey; +import javax.persistence.GeneratedValue; +import javax.persistence.GenerationType; +import javax.persistence.Id; +import javax.persistence.JoinColumn; +import javax.persistence.ManyToOne; + import lombok.Getter; // @Entity diff --git a/batch-quartz/src/main/java/com/spring/domain/batch/entity/BatchStepExecutionContext.java b/batch-quartz/src/main/java/com/spring/domain/batch/entity/BatchStepExecutionContext.java index fdf959e..31bbf02 100644 --- a/batch-quartz/src/main/java/com/spring/domain/batch/entity/BatchStepExecutionContext.java +++ b/batch-quartz/src/main/java/com/spring/domain/batch/entity/BatchStepExecutionContext.java @@ -1,12 +1,11 @@ package com.spring.domain.batch.entity; -import jakarta.persistence.Column; -import jakarta.persistence.Entity; -import jakarta.persistence.ForeignKey; -import jakarta.persistence.Id; -import jakarta.persistence.JoinColumn; -import jakarta.persistence.OneToOne; -import jakarta.persistence.Table; +import javax.persistence.Column; +import javax.persistence.ForeignKey; +import javax.persistence.Id; +import javax.persistence.JoinColumn; +import javax.persistence.OneToOne; + import lombok.Getter; // @Entity diff --git a/batch-quartz/src/main/java/com/spring/domain/email/batch/EmailSendBatch.java b/batch-quartz/src/main/java/com/spring/domain/email/batch/EmailSendBatch.java index 8b85e15..98175ab 100644 --- a/batch-quartz/src/main/java/com/spring/domain/email/batch/EmailSendBatch.java +++ b/batch-quartz/src/main/java/com/spring/domain/email/batch/EmailSendBatch.java @@ -3,45 +3,40 @@ package com.spring.domain.email.batch; 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.batch.repeat.RepeatStatus; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; -import org.springframework.transaction.PlatformTransactionManager; import com.spring.infra.quartz.QuartzJob; -import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; @Slf4j @Configuration -@RequiredArgsConstructor public class EmailSendBatch { - private final JobRepository jobRepository; - private final PlatformTransactionManager transactionManager; - - @QuartzJob(name = "emailSendJob", cronExpression = "*/3 * * * * ?") + @QuartzJob(group = "EMAIL", name = "emailSendJob", cronExpression = "*/3 * * * * ?") + // @JobScope @Bean(name = "emailSendJob") Job emailSendJob(Step emailSendStep) { log.info(">>> emailSendJob"); - return new JobBuilder("emailSendJob", jobRepository) + return new JobBuilder("emailSendJob") .start(emailSendStep) .build(); } + // @StepScope @Bean("emailSendStep") Step emailSendStep(Tasklet emailSendTasklet) { log.info(">>> emailSendStep"); - return new StepBuilder("emailSendStep", jobRepository) - .tasklet(emailSendTasklet, transactionManager).build(); + return new StepBuilder("emailSendStep") + .tasklet(emailSendTasklet).build(); } @Bean - Tasklet emailSendTasklet(){ + Tasklet emailSendTasklet() { return ((contribution, chunkContext) -> { log.info(">>>>> emailSendTasklet"); return RepeatStatus.FINISHED; diff --git a/batch-quartz/src/main/java/com/spring/domain/email/entity/ComMailqueMst.java b/batch-quartz/src/main/java/com/spring/domain/email/entity/ComMailqueMst.java index d313b4a..323a61c 100644 --- a/batch-quartz/src/main/java/com/spring/domain/email/entity/ComMailqueMst.java +++ b/batch-quartz/src/main/java/com/spring/domain/email/entity/ComMailqueMst.java @@ -3,15 +3,16 @@ package com.spring.domain.email.entity; import java.io.Serializable; import java.time.LocalDate; -import jakarta.persistence.Column; -import jakarta.persistence.Entity; -import jakarta.persistence.GeneratedValue; -import jakarta.persistence.GenerationType; -import jakarta.persistence.Id; -import jakarta.persistence.IdClass; -import jakarta.persistence.Lob; -import jakarta.persistence.SequenceGenerator; -import jakarta.persistence.Table; +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.GenerationType; +import javax.persistence.Id; +import javax.persistence.IdClass; +import javax.persistence.Lob; +import javax.persistence.SequenceGenerator; +import javax.persistence.Table; + import lombok.EqualsAndHashCode; import lombok.Getter; diff --git a/batch-quartz/src/main/java/com/spring/domain/email/entity/ComMailrstLog.java b/batch-quartz/src/main/java/com/spring/domain/email/entity/ComMailrstLog.java index 832f3c7..99348a3 100644 --- a/batch-quartz/src/main/java/com/spring/domain/email/entity/ComMailrstLog.java +++ b/batch-quartz/src/main/java/com/spring/domain/email/entity/ComMailrstLog.java @@ -3,11 +3,12 @@ package com.spring.domain.email.entity; import java.io.Serializable; import java.time.LocalDate; -import jakarta.persistence.Column; -import jakarta.persistence.Embeddable; -import jakarta.persistence.EmbeddedId; -import jakarta.persistence.Entity; -import jakarta.persistence.Table; +import javax.persistence.Column; +import javax.persistence.Embeddable; +import javax.persistence.EmbeddedId; +import javax.persistence.Entity; +import javax.persistence.Table; + import lombok.Builder; import lombok.EqualsAndHashCode; import lombok.Getter; diff --git a/batch-quartz/src/main/java/com/spring/domain/post/entity/Post.java b/batch-quartz/src/main/java/com/spring/domain/post/entity/Post.java index cd85a56..f930ccb 100644 --- a/batch-quartz/src/main/java/com/spring/domain/post/entity/Post.java +++ b/batch-quartz/src/main/java/com/spring/domain/post/entity/Post.java @@ -1,14 +1,15 @@ package com.spring.domain.post.entity; +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.GenerationType; +import javax.persistence.Id; +import javax.persistence.Table; + import com.spring.infra.db.SecondaryDataSourceConfig; import com.spring.infra.db.orm.jpa.annotation.DatabaseSelector; -import jakarta.persistence.Column; -import jakarta.persistence.Entity; -import jakarta.persistence.GeneratedValue; -import jakarta.persistence.GenerationType; -import jakarta.persistence.Id; -import jakarta.persistence.Table; import lombok.Getter; @DatabaseSelector(SecondaryDataSourceConfig.DATABASE) diff --git a/batch-quartz/src/main/java/com/spring/domain/quartz/entity/QrtzBlobTriggers.java b/batch-quartz/src/main/java/com/spring/domain/quartz/entity/QrtzBlobTriggers.java index a00c2fc..32fba82 100644 --- a/batch-quartz/src/main/java/com/spring/domain/quartz/entity/QrtzBlobTriggers.java +++ b/batch-quartz/src/main/java/com/spring/domain/quartz/entity/QrtzBlobTriggers.java @@ -2,13 +2,12 @@ package com.spring.domain.quartz.entity; import java.io.Serializable; -import jakarta.persistence.Column; -import jakarta.persistence.Embeddable; -import jakarta.persistence.EmbeddedId; -import jakarta.persistence.Entity; -import jakarta.persistence.JoinColumn; -import jakarta.persistence.ManyToOne; -import jakarta.persistence.Table; +import javax.persistence.Column; +import javax.persistence.Embeddable; +import javax.persistence.EmbeddedId; +import javax.persistence.JoinColumn; +import javax.persistence.ManyToOne; + import lombok.EqualsAndHashCode; import lombok.Getter; diff --git a/batch-quartz/src/main/java/com/spring/domain/quartz/entity/QrtzCalendars.java b/batch-quartz/src/main/java/com/spring/domain/quartz/entity/QrtzCalendars.java index db6af16..8ce4cbb 100644 --- a/batch-quartz/src/main/java/com/spring/domain/quartz/entity/QrtzCalendars.java +++ b/batch-quartz/src/main/java/com/spring/domain/quartz/entity/QrtzCalendars.java @@ -2,11 +2,10 @@ package com.spring.domain.quartz.entity; import java.io.Serializable; -import jakarta.persistence.Column; -import jakarta.persistence.Embeddable; -import jakarta.persistence.EmbeddedId; -import jakarta.persistence.Entity; -import jakarta.persistence.Table; +import javax.persistence.Column; +import javax.persistence.Embeddable; +import javax.persistence.EmbeddedId; + import lombok.EqualsAndHashCode; import lombok.Getter; diff --git a/batch-quartz/src/main/java/com/spring/domain/quartz/entity/QrtzCronTriggers.java b/batch-quartz/src/main/java/com/spring/domain/quartz/entity/QrtzCronTriggers.java index 4fdc724..ae2ae10 100644 --- a/batch-quartz/src/main/java/com/spring/domain/quartz/entity/QrtzCronTriggers.java +++ b/batch-quartz/src/main/java/com/spring/domain/quartz/entity/QrtzCronTriggers.java @@ -2,15 +2,14 @@ package com.spring.domain.quartz.entity; import java.io.Serializable; -import jakarta.persistence.Column; -import jakarta.persistence.Embeddable; -import jakarta.persistence.EmbeddedId; -import jakarta.persistence.Entity; -import jakarta.persistence.ForeignKey; -import jakarta.persistence.JoinColumn; -import jakarta.persistence.JoinColumns; -import jakarta.persistence.ManyToOne; -import jakarta.persistence.Table; +import javax.persistence.Column; +import javax.persistence.Embeddable; +import javax.persistence.EmbeddedId; +import javax.persistence.ForeignKey; +import javax.persistence.JoinColumn; +import javax.persistence.JoinColumns; +import javax.persistence.ManyToOne; + import lombok.EqualsAndHashCode; import lombok.Getter; diff --git a/batch-quartz/src/main/java/com/spring/domain/quartz/entity/QrtzFiredTriggers.java b/batch-quartz/src/main/java/com/spring/domain/quartz/entity/QrtzFiredTriggers.java index 957d761..abfdedb 100644 --- a/batch-quartz/src/main/java/com/spring/domain/quartz/entity/QrtzFiredTriggers.java +++ b/batch-quartz/src/main/java/com/spring/domain/quartz/entity/QrtzFiredTriggers.java @@ -2,11 +2,10 @@ package com.spring.domain.quartz.entity; import java.io.Serializable; -import jakarta.persistence.Column; -import jakarta.persistence.Embeddable; -import jakarta.persistence.EmbeddedId; -import jakarta.persistence.Entity; -import jakarta.persistence.Table; +import javax.persistence.Column; +import javax.persistence.Embeddable; +import javax.persistence.EmbeddedId; + import lombok.EqualsAndHashCode; import lombok.Getter; diff --git a/batch-quartz/src/main/java/com/spring/domain/quartz/entity/QrtzJobDetails.java b/batch-quartz/src/main/java/com/spring/domain/quartz/entity/QrtzJobDetails.java index b59ba79..aa8ab67 100644 --- a/batch-quartz/src/main/java/com/spring/domain/quartz/entity/QrtzJobDetails.java +++ b/batch-quartz/src/main/java/com/spring/domain/quartz/entity/QrtzJobDetails.java @@ -2,11 +2,10 @@ package com.spring.domain.quartz.entity; import java.io.Serializable; -import jakarta.persistence.Column; -import jakarta.persistence.Embeddable; -import jakarta.persistence.EmbeddedId; -import jakarta.persistence.Entity; -import jakarta.persistence.Table; +import javax.persistence.Column; +import javax.persistence.Embeddable; +import javax.persistence.EmbeddedId; + import lombok.EqualsAndHashCode; import lombok.Getter; diff --git a/batch-quartz/src/main/java/com/spring/domain/quartz/entity/QrtzLocks.java b/batch-quartz/src/main/java/com/spring/domain/quartz/entity/QrtzLocks.java index 3ae8de4..5994a61 100644 --- a/batch-quartz/src/main/java/com/spring/domain/quartz/entity/QrtzLocks.java +++ b/batch-quartz/src/main/java/com/spring/domain/quartz/entity/QrtzLocks.java @@ -2,11 +2,10 @@ package com.spring.domain.quartz.entity; import java.io.Serializable; -import jakarta.persistence.Column; -import jakarta.persistence.Embeddable; -import jakarta.persistence.EmbeddedId; -import jakarta.persistence.Entity; -import jakarta.persistence.Table; +import javax.persistence.Column; +import javax.persistence.Embeddable; +import javax.persistence.EmbeddedId; + import lombok.EqualsAndHashCode; import lombok.Getter; diff --git a/batch-quartz/src/main/java/com/spring/domain/quartz/entity/QrtzPausedTriggerGrps.java b/batch-quartz/src/main/java/com/spring/domain/quartz/entity/QrtzPausedTriggerGrps.java index 6a4e7df..a420928 100644 --- a/batch-quartz/src/main/java/com/spring/domain/quartz/entity/QrtzPausedTriggerGrps.java +++ b/batch-quartz/src/main/java/com/spring/domain/quartz/entity/QrtzPausedTriggerGrps.java @@ -2,11 +2,10 @@ package com.spring.domain.quartz.entity; import java.io.Serializable; -import jakarta.persistence.Column; -import jakarta.persistence.Embeddable; -import jakarta.persistence.EmbeddedId; -import jakarta.persistence.Entity; -import jakarta.persistence.Table; +import javax.persistence.Column; +import javax.persistence.Embeddable; +import javax.persistence.EmbeddedId; + import lombok.EqualsAndHashCode; import lombok.Getter; diff --git a/batch-quartz/src/main/java/com/spring/domain/quartz/entity/QrtzSchedulerState.java b/batch-quartz/src/main/java/com/spring/domain/quartz/entity/QrtzSchedulerState.java index 8d8392e..5288eae 100644 --- a/batch-quartz/src/main/java/com/spring/domain/quartz/entity/QrtzSchedulerState.java +++ b/batch-quartz/src/main/java/com/spring/domain/quartz/entity/QrtzSchedulerState.java @@ -2,11 +2,10 @@ package com.spring.domain.quartz.entity; import java.io.Serializable; -import jakarta.persistence.Column; -import jakarta.persistence.Embeddable; -import jakarta.persistence.EmbeddedId; -import jakarta.persistence.Entity; -import jakarta.persistence.Table; +import javax.persistence.Column; +import javax.persistence.Embeddable; +import javax.persistence.EmbeddedId; + import lombok.EqualsAndHashCode; import lombok.Getter; diff --git a/batch-quartz/src/main/java/com/spring/domain/quartz/entity/QrtzSimpleTriggers.java b/batch-quartz/src/main/java/com/spring/domain/quartz/entity/QrtzSimpleTriggers.java index f259417..1732114 100644 --- a/batch-quartz/src/main/java/com/spring/domain/quartz/entity/QrtzSimpleTriggers.java +++ b/batch-quartz/src/main/java/com/spring/domain/quartz/entity/QrtzSimpleTriggers.java @@ -2,15 +2,14 @@ package com.spring.domain.quartz.entity; import java.io.Serializable; -import jakarta.persistence.Column; -import jakarta.persistence.Embeddable; -import jakarta.persistence.EmbeddedId; -import jakarta.persistence.Entity; -import jakarta.persistence.ForeignKey; -import jakarta.persistence.JoinColumn; -import jakarta.persistence.JoinColumns; -import jakarta.persistence.ManyToOne; -import jakarta.persistence.Table; +import javax.persistence.Column; +import javax.persistence.Embeddable; +import javax.persistence.EmbeddedId; +import javax.persistence.ForeignKey; +import javax.persistence.JoinColumn; +import javax.persistence.JoinColumns; +import javax.persistence.ManyToOne; + import lombok.EqualsAndHashCode; import lombok.Getter; diff --git a/batch-quartz/src/main/java/com/spring/domain/quartz/entity/QrtzSimpropTriggers.java b/batch-quartz/src/main/java/com/spring/domain/quartz/entity/QrtzSimpropTriggers.java index c112e58..720a8ca 100644 --- a/batch-quartz/src/main/java/com/spring/domain/quartz/entity/QrtzSimpropTriggers.java +++ b/batch-quartz/src/main/java/com/spring/domain/quartz/entity/QrtzSimpropTriggers.java @@ -3,15 +3,14 @@ package com.spring.domain.quartz.entity; import java.io.Serializable; import java.math.BigDecimal; -import jakarta.persistence.Column; -import jakarta.persistence.Embeddable; -import jakarta.persistence.EmbeddedId; -import jakarta.persistence.Entity; -import jakarta.persistence.ForeignKey; -import jakarta.persistence.JoinColumn; -import jakarta.persistence.JoinColumns; -import jakarta.persistence.ManyToOne; -import jakarta.persistence.Table; +import javax.persistence.Column; +import javax.persistence.Embeddable; +import javax.persistence.EmbeddedId; +import javax.persistence.ForeignKey; +import javax.persistence.JoinColumn; +import javax.persistence.JoinColumns; +import javax.persistence.ManyToOne; + import lombok.EqualsAndHashCode; import lombok.Getter; diff --git a/batch-quartz/src/main/java/com/spring/domain/quartz/entity/QrtzTriggers.java b/batch-quartz/src/main/java/com/spring/domain/quartz/entity/QrtzTriggers.java index 2f9bfb3..cb308ea 100644 --- a/batch-quartz/src/main/java/com/spring/domain/quartz/entity/QrtzTriggers.java +++ b/batch-quartz/src/main/java/com/spring/domain/quartz/entity/QrtzTriggers.java @@ -2,15 +2,14 @@ package com.spring.domain.quartz.entity; import java.io.Serializable; -import jakarta.persistence.Column; -import jakarta.persistence.Embeddable; -import jakarta.persistence.EmbeddedId; -import jakarta.persistence.Entity; -import jakarta.persistence.ForeignKey; -import jakarta.persistence.JoinColumn; -import jakarta.persistence.JoinColumns; -import jakarta.persistence.ManyToOne; -import jakarta.persistence.Table; +import javax.persistence.Column; +import javax.persistence.Embeddable; +import javax.persistence.EmbeddedId; +import javax.persistence.ForeignKey; +import javax.persistence.JoinColumn; +import javax.persistence.JoinColumns; +import javax.persistence.ManyToOne; + import lombok.EqualsAndHashCode; import lombok.Getter; diff --git a/batch-quartz/src/main/java/com/spring/domain/user/api/AuthController.java b/batch-quartz/src/main/java/com/spring/domain/user/api/AuthController.java index 395def2..5bafd12 100644 --- a/batch-quartz/src/main/java/com/spring/domain/user/api/AuthController.java +++ b/batch-quartz/src/main/java/com/spring/domain/user/api/AuthController.java @@ -1,5 +1,7 @@ package com.spring.domain.user.api; +import javax.servlet.http.HttpServletResponse; + import org.springframework.http.ResponseEntity; import org.springframework.security.core.Authentication; import org.springframework.web.bind.annotation.PostMapping; @@ -11,7 +13,6 @@ import com.spring.domain.user.dto.SignInRequest; import com.spring.domain.user.service.AuthService; import com.spring.infra.security.jwt.JwtTokenService; -import jakarta.servlet.http.HttpServletResponse; import lombok.RequiredArgsConstructor; @RestController diff --git a/batch-quartz/src/main/java/com/spring/domain/user/entity/AppUser.java b/batch-quartz/src/main/java/com/spring/domain/user/entity/AppUser.java index 3f1470a..d172c86 100644 --- a/batch-quartz/src/main/java/com/spring/domain/user/entity/AppUser.java +++ b/batch-quartz/src/main/java/com/spring/domain/user/entity/AppUser.java @@ -2,15 +2,16 @@ package com.spring.domain.user.entity; import java.util.Set; -import jakarta.persistence.CascadeType; -import jakarta.persistence.Column; -import jakarta.persistence.Entity; -import jakarta.persistence.FetchType; -import jakarta.persistence.GeneratedValue; -import jakarta.persistence.GenerationType; -import jakarta.persistence.Id; -import jakarta.persistence.OneToMany; -import jakarta.persistence.Table; +import javax.persistence.CascadeType; +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.FetchType; +import javax.persistence.GeneratedValue; +import javax.persistence.GenerationType; +import javax.persistence.Id; +import javax.persistence.OneToMany; +import javax.persistence.Table; + import lombok.Getter; @Entity diff --git a/batch-quartz/src/main/java/com/spring/domain/user/entity/AppUserRole.java b/batch-quartz/src/main/java/com/spring/domain/user/entity/AppUserRole.java index c14f251..977e3c5 100644 --- a/batch-quartz/src/main/java/com/spring/domain/user/entity/AppUserRole.java +++ b/batch-quartz/src/main/java/com/spring/domain/user/entity/AppUserRole.java @@ -2,15 +2,16 @@ package com.spring.domain.user.entity; import java.util.Set; -import jakarta.persistence.CascadeType; -import jakarta.persistence.Column; -import jakarta.persistence.Entity; -import jakarta.persistence.FetchType; -import jakarta.persistence.GeneratedValue; -import jakarta.persistence.GenerationType; -import jakarta.persistence.Id; -import jakarta.persistence.OneToMany; -import jakarta.persistence.Table; +import javax.persistence.CascadeType; +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.FetchType; +import javax.persistence.GeneratedValue; +import javax.persistence.GenerationType; +import javax.persistence.Id; +import javax.persistence.OneToMany; +import javax.persistence.Table; + import lombok.Getter; @Entity diff --git a/batch-quartz/src/main/java/com/spring/domain/user/entity/AppUserRoleMap.java b/batch-quartz/src/main/java/com/spring/domain/user/entity/AppUserRoleMap.java index f445e15..104a97b 100644 --- a/batch-quartz/src/main/java/com/spring/domain/user/entity/AppUserRoleMap.java +++ b/batch-quartz/src/main/java/com/spring/domain/user/entity/AppUserRoleMap.java @@ -2,14 +2,15 @@ package com.spring.domain.user.entity; import java.io.Serializable; -import jakarta.persistence.Column; -import jakarta.persistence.Embeddable; -import jakarta.persistence.EmbeddedId; -import jakarta.persistence.Entity; -import jakarta.persistence.JoinColumn; -import jakarta.persistence.ManyToOne; -import jakarta.persistence.MapsId; -import jakarta.persistence.Table; +import javax.persistence.Column; +import javax.persistence.Embeddable; +import javax.persistence.EmbeddedId; +import javax.persistence.Entity; +import javax.persistence.JoinColumn; +import javax.persistence.ManyToOne; +import javax.persistence.MapsId; +import javax.persistence.Table; + import lombok.EqualsAndHashCode; import lombok.Getter; diff --git a/batch-quartz/src/main/java/com/spring/infra/batch/BatchConfig.java b/batch-quartz/src/main/java/com/spring/infra/batch/BatchConfig.java new file mode 100644 index 0000000..9ee0e0b --- /dev/null +++ b/batch-quartz/src/main/java/com/spring/infra/batch/BatchConfig.java @@ -0,0 +1,6 @@ +package com.spring.infra.batch; + +public class BatchConfig { + + +} diff --git a/batch-quartz/src/main/java/com/spring/infra/db/PrimaryDataSourceConfig.java b/batch-quartz/src/main/java/com/spring/infra/db/PrimaryDataSourceConfig.java index 8b95889..d44e479 100644 --- a/batch-quartz/src/main/java/com/spring/infra/db/PrimaryDataSourceConfig.java +++ b/batch-quartz/src/main/java/com/spring/infra/db/PrimaryDataSourceConfig.java @@ -11,6 +11,18 @@ import org.springframework.context.annotation.Primary; import com.zaxxer.hikari.HikariDataSource; +/** + * 주 데이터 소스 설정을 위한 구성 클래스입니다. + * + *

이 클래스는 애플리케이션의 주 데이터 소스를 설정하며, 다음과 같은 기능을 제공합니다:

+ *
    + *
  • 주 데이터 소스 속성 설정
  • + *
  • HikariCP를 사용한 주 데이터 소스 생성
  • + *
+ * + * @author mindol + * @version 1.0 + */ @Configuration public class PrimaryDataSourceConfig { @@ -19,6 +31,14 @@ public class PrimaryDataSourceConfig { private static final String DATASOURCE_PROPERTIES = "primaryDataSourceProperties"; private static final String DATASOURCE_PROPERTIES_PREFIX = "spring.datasource.primary"; + /** + * 주 데이터 소스의 속성을 설정합니다. + * + *

이 메소드는 'spring.datasource.primary' 접두사로 시작하는 설정을 읽어 + * DataSourceProperties 객체를 생성합니다.

+ * + * @return 설정된 DataSourceProperties 객체 + */ @Primary @Bean(name = DATASOURCE_PROPERTIES) @ConfigurationProperties(prefix = DATASOURCE_PROPERTIES_PREFIX) @@ -26,6 +46,14 @@ public class PrimaryDataSourceConfig { return new DataSourceProperties(); } + /** + * 주 데이터 소스를 생성합니다. + * + *

이 메소드는 설정된 DataSourceProperties를 사용하여 HikariCP 데이터 소스를 생성합니다.

+ * + * @param properties 데이터 소스 속성 + * @return 생성된 DataSource 객체 + */ @Primary @Bean(name = DATASOURCE) DataSource dataSource(@Qualifier(DATASOURCE_PROPERTIES) DataSourceProperties properties) { diff --git a/batch-quartz/src/main/java/com/spring/infra/db/SecondaryDataSourceConfig.java b/batch-quartz/src/main/java/com/spring/infra/db/SecondaryDataSourceConfig.java index 8da340c..808948f 100644 --- a/batch-quartz/src/main/java/com/spring/infra/db/SecondaryDataSourceConfig.java +++ b/batch-quartz/src/main/java/com/spring/infra/db/SecondaryDataSourceConfig.java @@ -10,6 +10,18 @@ import org.springframework.context.annotation.Configuration; import com.zaxxer.hikari.HikariDataSource; +/** + * 보조 데이터 소스 설정을 위한 구성 클래스입니다. + * + *

이 클래스는 애플리케이션의 보조 데이터 소스를 설정하며, 다음과 같은 기능을 제공합니다:

+ *
    + *
  • 보조 데이터 소스 속성 설정
  • + *
  • HikariCP를 사용한 보조 데이터 소스 생성
  • + *
+ * + * @author mindol + * @version 1.0 + */ @Configuration public class SecondaryDataSourceConfig { @@ -18,12 +30,28 @@ public class SecondaryDataSourceConfig { private static final String DATASOURCE_PROPERTIES = "secondaryDataSourceProperties"; private static final String DATASOURCE_PROPERTIES_PREFIX = "spring.datasource.secondary"; + /** + * 보조 데이터 소스의 속성을 설정합니다. + * + *

이 메소드는 'spring.datasource.secondary' 접두사로 시작하는 설정을 읽어 + * DataSourceProperties 객체를 생성합니다.

+ * + * @return 설정된 DataSourceProperties 객체 + */ @Bean(name = DATASOURCE_PROPERTIES) @ConfigurationProperties(prefix = DATASOURCE_PROPERTIES_PREFIX) DataSourceProperties dataSourceProperties() { return new DataSourceProperties(); } + /** + * 보조 데이터 소스를 생성합니다. + * + *

이 메소드는 설정된 DataSourceProperties를 사용하여 HikariCP 데이터 소스를 생성합니다.

+ * + * @param properties 데이터 소스 속성 + * @return 생성된 DataSource 객체 + */ @Bean(name = DATASOURCE) DataSource dataSource(@Qualifier(DATASOURCE_PROPERTIES) DataSourceProperties properties) { return properties.initializeDataSourceBuilder().type(HikariDataSource.class).build(); diff --git a/batch-quartz/src/main/java/com/spring/infra/db/orm/jpa/PrimaryJpaConfig.java b/batch-quartz/src/main/java/com/spring/infra/db/orm/jpa/PrimaryJpaConfig.java index 5c39df8..7ee587b 100644 --- a/batch-quartz/src/main/java/com/spring/infra/db/orm/jpa/PrimaryJpaConfig.java +++ b/batch-quartz/src/main/java/com/spring/infra/db/orm/jpa/PrimaryJpaConfig.java @@ -1,5 +1,6 @@ package com.spring.infra.db.orm.jpa; +import javax.persistence.EntityManagerFactory; import javax.sql.DataSource; import org.springframework.beans.factory.annotation.Qualifier; @@ -25,9 +26,21 @@ import com.spring.infra.db.PrimaryDataSourceConfig; import com.spring.infra.db.orm.jpa.annotation.DatabaseSelector; import com.spring.infra.db.orm.jpa.util.EntityScanner; -import jakarta.persistence.EntityManagerFactory; import lombok.RequiredArgsConstructor; +/** + * 주 데이터베이스에 대한 JPA 설정을 담당하는 구성 클래스입니다. + * + *

이 클래스는 다음과 같은 주요 기능을 제공합니다:

+ *
    + *
  • 주 데이터베이스용 EntityManagerFactory 설정
  • + *
  • 주 데이터베이스용 TransactionManager 설정
  • + *
  • JPA 리포지토리 활성화 및 필터링
  • + *
+ * + * @author mindol + * @version 1.0 + */ @Configuration @EnableJpaRepositories( basePackages = PrimaryJpaConfig.BASE_PACKAGE, @@ -46,6 +59,13 @@ public class PrimaryJpaConfig { private final JpaProperties jpaProperties; private final HibernateProperties hibernateProperties; + /** + * 주 데이터베이스용 EntityManagerFactory를 생성합니다. + * + * @param builder EntityManagerFactory 빌더 + * @param dataSource 주 데이터 소스 + * @return 구성된 LocalContainerEntityManagerFactoryBean + */ @Primary @Bean(name = ENTITY_MANAGER_FACTORY) LocalContainerEntityManagerFactoryBean entityManagerFactory( @@ -62,12 +82,23 @@ public class PrimaryJpaConfig { .build(); } + /** + * 주 데이터베이스용 TransactionManager를 생성합니다. + * + * @param factory 주 EntityManagerFactory + * @return 구성된 PlatformTransactionManager + */ @Primary @Bean(TRANSACTION_MANAGER) PlatformTransactionManager transactionManager(@Qualifier(ENTITY_MANAGER_FACTORY) EntityManagerFactory factory) { return new JpaTransactionManager(factory); } + /** + * 데이터베이스 선택을 위한 커스텀 필터 클래스입니다. + * + *

이 필터는 DatabaseSelector 어노테이션이 없는 엔티티만 선택합니다.

+ */ public static class DatabaseFilter implements TypeFilter { @Override public boolean match(@NonNull MetadataReader reader, @NonNull MetadataReaderFactory factory) { diff --git a/batch-quartz/src/main/java/com/spring/infra/db/orm/jpa/SecondaryJpaConfig.java b/batch-quartz/src/main/java/com/spring/infra/db/orm/jpa/SecondaryJpaConfig.java index a60c022..9e211d6 100644 --- a/batch-quartz/src/main/java/com/spring/infra/db/orm/jpa/SecondaryJpaConfig.java +++ b/batch-quartz/src/main/java/com/spring/infra/db/orm/jpa/SecondaryJpaConfig.java @@ -2,6 +2,7 @@ package com.spring.infra.db.orm.jpa; import java.util.Optional; +import javax.persistence.EntityManagerFactory; import javax.sql.DataSource; import org.springframework.beans.factory.annotation.Qualifier; @@ -26,9 +27,21 @@ import com.spring.infra.db.SecondaryDataSourceConfig; import com.spring.infra.db.orm.jpa.annotation.DatabaseSelector; import com.spring.infra.db.orm.jpa.util.EntityScanner; -import jakarta.persistence.EntityManagerFactory; import lombok.RequiredArgsConstructor; +/** + * 보조 데이터베이스에 대한 JPA 설정을 담당하는 구성 클래스입니다. + * + *

이 클래스는 다음과 같은 주요 기능을 제공합니다:

+ *
    + *
  • 보조 데이터베이스용 EntityManagerFactory 설정
  • + *
  • 보조 데이터베이스용 TransactionManager 설정
  • + *
  • JPA 리포지토리 활성화 및 필터링
  • + *
+ * + * @author mindol + * @version 1.0 + */ @Configuration @EnableJpaRepositories( basePackages = SecondaryJpaConfig.BASE_PACKAGE, @@ -47,6 +60,13 @@ public class SecondaryJpaConfig { private final JpaProperties jpaProperties; private final HibernateProperties hibernateProperties; + /** + * 보조 데이터베이스용 EntityManagerFactory를 생성합니다. + * + * @param builder EntityManagerFactory 빌더 + * @param dataSource 보조 데이터 소스 + * @return 구성된 LocalContainerEntityManagerFactoryBean + */ @Bean(name = ENTITY_MANAGER_FACTORY) LocalContainerEntityManagerFactoryBean entityManagerFactory( EntityManagerFactoryBuilder builder, @@ -62,11 +82,22 @@ public class SecondaryJpaConfig { .build(); } + /** + * 보조 데이터베이스용 TransactionManager를 생성합니다. + * + * @param factory 보조 EntityManagerFactory + * @return 구성된 PlatformTransactionManager + */ @Bean(TRANSACTION_MANAGER) PlatformTransactionManager transactionManager(@Qualifier(ENTITY_MANAGER_FACTORY) EntityManagerFactory factory) { return new JpaTransactionManager(factory); } + /** + * 데이터베이스 선택을 위한 커스텀 필터 클래스입니다. + * + *

이 필터는 DatabaseSelector 어노테이션이 있고, 그 값이 보조 데이터베이스와 일치하는 엔티티만 선택합니다.

+ */ public static class DatabaseFilter implements TypeFilter { @Override public boolean match(@NonNull MetadataReader reader, @NonNull MetadataReaderFactory factory) { diff --git a/batch-quartz/src/main/java/com/spring/infra/db/orm/jpa/annotation/DatabaseSelector.java b/batch-quartz/src/main/java/com/spring/infra/db/orm/jpa/annotation/DatabaseSelector.java index fb0ca72..79b801c 100644 --- a/batch-quartz/src/main/java/com/spring/infra/db/orm/jpa/annotation/DatabaseSelector.java +++ b/batch-quartz/src/main/java/com/spring/infra/db/orm/jpa/annotation/DatabaseSelector.java @@ -5,8 +5,34 @@ import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; +/** + * 엔티티 클래스가 사용할 데이터베이스를 지정하는 어노테이션입니다. + * + *

이 어노테이션은 JPA 엔티티 클래스에 적용되며, 해당 엔티티가 어떤 데이터베이스에서 + * 사용될지를 지정합니다. 주로 다중 데이터베이스 환경에서 엔티티를 특정 데이터베이스에 + * 매핑하는 데 사용됩니다.

+ * + *

사용 예:

+ *
+ * {@code
+ * @Entity
+ * @DatabaseSelector("secondary")
+ * public class MyEntity {
+ *     // 엔티티 필드 및 메소드
+ * }
+ * }
+ * 
+ * + * @author mindol + * @version 1.0 + */ @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) public @interface DatabaseSelector { + /** + * 엔티티가 사용할 데이터베이스의 이름을 지정합니다. + * + * @return 데이터베이스 이름 + */ String value(); } diff --git a/batch-quartz/src/main/java/com/spring/infra/db/orm/jpa/util/EntityScanner.java b/batch-quartz/src/main/java/com/spring/infra/db/orm/jpa/util/EntityScanner.java index 1d39e4c..ca04a13 100644 --- a/batch-quartz/src/main/java/com/spring/infra/db/orm/jpa/util/EntityScanner.java +++ b/batch-quartz/src/main/java/com/spring/infra/db/orm/jpa/util/EntityScanner.java @@ -1,5 +1,7 @@ package com.spring.infra.db.orm.jpa.util; +import javax.persistence.Entity; + import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider; import org.springframework.core.type.classreading.MetadataReader; @@ -8,17 +10,38 @@ import org.springframework.util.StringUtils; import com.spring.infra.db.orm.jpa.annotation.DatabaseSelector; -import jakarta.persistence.Entity; import lombok.AccessLevel; import lombok.NoArgsConstructor; +/** + * JPA 엔티티 클래스를 스캔하는 유틸리티 클래스입니다. + * + *

이 클래스는 지정된 패키지 내의 엔티티 클래스를 스캔하고, + * 선택적으로 특정 데이터베이스에 해당하는 엔티티만 필터링할 수 있습니다.

+ * + * @author mindol + * @version 1.0 + */ @NoArgsConstructor(access = AccessLevel.PRIVATE) public class EntityScanner { + /** + * 지정된 기본 패키지 내의 모든 엔티티를 스캔합니다. + * + * @param basePackage 스캔할 기본 패키지 + * @return 스캔된 엔티티 클래스의 패키지 이름 배열 + */ public static String[] scanEntities(String basePackage) { return scanEntities(basePackage, null); } + /** + * 지정된 기본 패키지 내의 엔티티를 스캔하고, 선택적으로 특정 데이터베이스에 해당하는 엔티티만 필터링합니다. + * + * @param basePackage 스캔할 기본 패키지 + * @param dbName 필터링할 데이터베이스 이름 (null이면 모든 엔티티 반환) + * @return 스캔된 엔티티 클래스의 패키지 이름 배열 + */ public static String[] scanEntities(String basePackage, String dbName) { var scanner = new ClassPathScanningCandidateComponentProvider(false); scanner.addIncludeFilter((MetadataReader reader, MetadataReaderFactory factory) -> { diff --git a/batch-quartz/src/main/java/com/spring/infra/db/orm/mybatis/PrimaryMybatisConfig.java b/batch-quartz/src/main/java/com/spring/infra/db/orm/mybatis/PrimaryMybatisConfig.java index 979e922..a8e5262 100644 --- a/batch-quartz/src/main/java/com/spring/infra/db/orm/mybatis/PrimaryMybatisConfig.java +++ b/batch-quartz/src/main/java/com/spring/infra/db/orm/mybatis/PrimaryMybatisConfig.java @@ -16,6 +16,19 @@ import org.springframework.core.io.support.PathMatchingResourcePatternResolver; import com.spring.infra.db.PrimaryDataSourceConfig; import com.spring.infra.db.orm.mybatis.annotation.PrimaryMapper; +/** + * 주 데이터베이스에 대한 MyBatis 설정을 담당하는 구성 클래스입니다. + * + *

이 클래스는 다음과 같은 주요 기능을 제공합니다:

+ *
    + *
  • 주 데이터베이스용 SqlSessionFactory 설정
  • + *
  • MyBatis 매퍼 스캔 설정
  • + *
  • MyBatis 관련 설정 (카멜 케이스 변환, null 처리 등)
  • + *
+ * + * @author mindol + * @version 1.0 + */ @Configuration @MapperScan( basePackages = {PrimaryMybatisConfig.BASE_PACKAGE}, @@ -29,6 +42,13 @@ public class PrimaryMybatisConfig { private static final String MAPPER_RESOURCES = "classpath:mapper/**/*.xml"; private static final String SESSION_FACTORY = "primarySqlSessionFactory"; + /** + * 주 데이터베이스용 SqlSessionFactory를 생성합니다. + * + * @param dataSource 주 데이터 소스 + * @return 구성된 SqlSessionFactory 객체 + * @throws Exception SqlSessionFactory 생성 중 발생할 수 있는 예외 + */ @Primary @Bean(name = SESSION_FACTORY) SqlSessionFactory sqlSessionFactory(@Qualifier(PrimaryDataSourceConfig.DATASOURCE) DataSource dataSource) throws Exception { @@ -41,6 +61,11 @@ public class PrimaryMybatisConfig { return sessionFactory.getObject(); } + /** + * MyBatis 설정을 구성합니다. + * + * @return 구성된 MyBatis Configuration 객체 + */ private org.apache.ibatis.session.Configuration mybatisConfiguration() { var configuration = new org.apache.ibatis.session.Configuration(); configuration.setMapUnderscoreToCamelCase(true); diff --git a/batch-quartz/src/main/java/com/spring/infra/db/orm/mybatis/SecondaryMybatisConfig.java b/batch-quartz/src/main/java/com/spring/infra/db/orm/mybatis/SecondaryMybatisConfig.java index 6138f9c..d064fdd 100644 --- a/batch-quartz/src/main/java/com/spring/infra/db/orm/mybatis/SecondaryMybatisConfig.java +++ b/batch-quartz/src/main/java/com/spring/infra/db/orm/mybatis/SecondaryMybatisConfig.java @@ -15,6 +15,19 @@ import org.springframework.core.io.support.PathMatchingResourcePatternResolver; import com.spring.infra.db.SecondaryDataSourceConfig; import com.spring.infra.db.orm.mybatis.annotation.SecondaryMapper; +/** + * 보조 데이터베이스에 대한 MyBatis 설정을 담당하는 구성 클래스입니다. + * + *

이 클래스는 다음과 같은 주요 기능을 제공합니다:

+ *
    + *
  • 보조 데이터베이스용 SqlSessionFactory 설정
  • + *
  • MyBatis 매퍼 스캔 설정
  • + *
  • MyBatis 관련 설정 (카멜 케이스 변환, null 처리 등)
  • + *
+ * + * @author mindol + * @version 1.0 + */ @Configuration @MapperScan( basePackages = {SecondaryMybatisConfig.BASE_PACKAGE}, @@ -28,6 +41,13 @@ public class SecondaryMybatisConfig { private static final String MAPPER_RESOURCES = "classpath:mapper/**/*.xml"; private static final String SESSION_FACTORY = "secondarySqlSessionFactory"; + /** + * 보조 데이터베이스용 SqlSessionFactory를 생성합니다. + * + * @param dataSource 보조 데이터 소스 + * @return 구성된 SqlSessionFactory 객체 + * @throws Exception SqlSessionFactory 생성 중 발생할 수 있는 예외 + */ @Bean(name = SESSION_FACTORY) SqlSessionFactory sqlSessionFactory(@Qualifier(SecondaryDataSourceConfig.DATASOURCE) DataSource dataSource) throws Exception { var sessionFactory = new SqlSessionFactoryBean(); @@ -39,6 +59,11 @@ public class SecondaryMybatisConfig { return sessionFactory.getObject(); } + /** + * MyBatis 설정을 구성합니다. + * + * @return 구성된 MyBatis Configuration 객체 + */ private org.apache.ibatis.session.Configuration mybatisConfiguration() { var configuration = new org.apache.ibatis.session.Configuration(); configuration.setMapUnderscoreToCamelCase(true); diff --git a/batch-quartz/src/main/java/com/spring/infra/db/orm/mybatis/annotation/PrimaryMapper.java b/batch-quartz/src/main/java/com/spring/infra/db/orm/mybatis/annotation/PrimaryMapper.java index 3173d33..4a85aaf 100644 --- a/batch-quartz/src/main/java/com/spring/infra/db/orm/mybatis/annotation/PrimaryMapper.java +++ b/batch-quartz/src/main/java/com/spring/infra/db/orm/mybatis/annotation/PrimaryMapper.java @@ -5,6 +5,26 @@ import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; +/** + * 주 데이터베이스용 MyBatis 매퍼를 지정하는 어노테이션입니다. + * + *

이 어노테이션은 인터페이스 레벨에서 사용되며, 해당 인터페이스가 주 데이터베이스와 + * 연결된 MyBatis 매퍼임을 나타냅니다. 주로 다중 데이터베이스 환경에서 매퍼 인터페이스를 + * 특정 데이터베이스에 연결하는 데 사용됩니다.

+ * + *

사용 예:

+ *
+ * {@code
+ * @PrimaryMapper
+ * public interface UserMapper {
+ *     // 매퍼 메소드 정의
+ * }
+ * }
+ * 
+ * + * @author mindol + * @version 1.0 + */ @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) public @interface PrimaryMapper { diff --git a/batch-quartz/src/main/java/com/spring/infra/db/orm/mybatis/annotation/SecondaryMapper.java b/batch-quartz/src/main/java/com/spring/infra/db/orm/mybatis/annotation/SecondaryMapper.java index 66dc5c0..d689746 100644 --- a/batch-quartz/src/main/java/com/spring/infra/db/orm/mybatis/annotation/SecondaryMapper.java +++ b/batch-quartz/src/main/java/com/spring/infra/db/orm/mybatis/annotation/SecondaryMapper.java @@ -5,6 +5,26 @@ import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; +/** + * 보조 데이터베이스용 MyBatis 매퍼를 지정하는 어노테이션입니다. + * + *

이 어노테이션은 인터페이스 레벨에서 사용되며, 해당 인터페이스가 보조 데이터베이스와 + * 연결된 MyBatis 매퍼임을 나타냅니다. 주로 다중 데이터베이스 환경에서 매퍼 인터페이스를 + * 특정 데이터베이스에 연결하는 데 사용됩니다.

+ * + *

사용 예:

+ *
+ * {@code
+ * @SecondaryMapper
+ * public interface UserMapper {
+ *     // 매퍼 메소드 정의
+ * }
+ * }
+ * 
+ * + * @author mindol + * @version 1.0 + */ @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) public @interface SecondaryMapper { diff --git a/batch-quartz/src/main/java/com/spring/infra/quartz/QuartzConfig.java b/batch-quartz/src/main/java/com/spring/infra/quartz/QuartzConfig.java index 2dd76c3..74a7fef 100644 --- a/batch-quartz/src/main/java/com/spring/infra/quartz/QuartzConfig.java +++ b/batch-quartz/src/main/java/com/spring/infra/quartz/QuartzConfig.java @@ -14,6 +14,19 @@ import org.springframework.transaction.PlatformTransactionManager; import lombok.RequiredArgsConstructor; +/** + * Quartz 스케줄러 설정을 위한 구성 클래스입니다. + * + *

이 클래스는 Quartz 스케줄러의 기본 설정을 제공하며, 다음과 같은 기능을 수행합니다:

+ *
    + *
  • JobFactory 빈 생성 및 설정
  • + *
  • SchedulerFactoryBean 생성 및 설정
  • + *
  • Quartz 작업에 대한 의존성 주입 지원
  • + *
+ * + * @author mindol + * @version 1.0 + */ @Configuration @RequiredArgsConstructor public class QuartzConfig { @@ -23,10 +36,13 @@ public class QuartzConfig { private final PlatformTransactionManager transactionManager; /** - * Quartz Schedule Job 에 의존성 주입 + * Quartz Schedule Job에 의존성을 주입하기 위한 JobFactory를 생성합니다. * - * @param beanFactory application context beanFactory - * @return the job factory + *

이 메소드는 Spring의 AutowireCapableBeanFactory를 사용하여 + * Quartz Job 인스턴스에 자동으로 의존성을 주입합니다.

+ * + * @param beanFactory Spring의 AutowireCapableBeanFactory + * @return 생성된 JobFactory 인스턴스 */ @Bean JobFactory jobFactory(AutowireCapableBeanFactory beanFactory) { @@ -38,10 +54,19 @@ public class QuartzConfig { } /** - * Scheduler 전체를 관리하는 Manager. + * Quartz Scheduler를 생성하고 설정하는 SchedulerFactoryBean을 구성합니다. + * + *

이 메소드는 다음과 같은 설정을 수행합니다:

+ *
    + *
  • 스케줄러 이름 설정
  • + *
  • Quartz 속성 설정
  • + *
  • 데이터 소스 및 트랜잭션 매니저 설정
  • + *
  • JobFactory 설정
  • + *
  • 자동 시작 및 종료 시 작업 완료 대기 설정
  • + *
* - * @param jobFactory job factory - * @return the scheduler factory bean + * @param jobFactory 사용할 JobFactory 인스턴스 + * @return 구성된 SchedulerFactoryBean 인스턴스 */ @Bean SchedulerFactoryBean schedulerFactoryBean(JobFactory jobFactory) { diff --git a/batch-quartz/src/main/java/com/spring/infra/quartz/QuartzJob.java b/batch-quartz/src/main/java/com/spring/infra/quartz/QuartzJob.java index eb7cb31..2ba225e 100644 --- a/batch-quartz/src/main/java/com/spring/infra/quartz/QuartzJob.java +++ b/batch-quartz/src/main/java/com/spring/infra/quartz/QuartzJob.java @@ -5,9 +5,50 @@ import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; +/** + * Quartz 스케줄러를 통해 실행될 작업을 정의하는 어노테이션입니다. + * + *

이 어노테이션은 메소드 레벨에서 사용되며, 해당 메소드를 Quartz 작업으로 등록합니다.

+ * + *

주요 기능:

+ *
    + *
  • 작업의 이름 지정
  • + *
  • 작업의 실행 주기를 Cron 표현식으로 정의
  • + *
+ * + *

사용 예:

+ *
+ * {@code
+ * @QuartzJob(name = "myJob", cronExpression = "0 0 12 * * ?")
+ * public void myScheduledJob() {
+ *     // 작업 내용
+ * }
+ * }
+ * 
+ * + * @author mindol + * @version 1.0 + */ @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.METHOD) public @interface QuartzJob { + /** + * Quartz 작업의 그룹을 지정합니다. + * + * @return 그룹의 이름 + */ + String group() default "DEFAULT"; + /** + * Quartz 작업의 이름을 지정합니다. + * + * @return 작업의 이름 + */ String name(); + + /** + * 작업의 실행 주기를 Cron 표현식으로 지정합니다. + * + * @return Cron 표현식 + */ String cronExpression(); } diff --git a/batch-quartz/src/main/java/com/spring/infra/quartz/QuartzJobLauncher.java b/batch-quartz/src/main/java/com/spring/infra/quartz/QuartzJobLauncher.java index 57e866a..6eb6f2e 100644 --- a/batch-quartz/src/main/java/com/spring/infra/quartz/QuartzJobLauncher.java +++ b/batch-quartz/src/main/java/com/spring/infra/quartz/QuartzJobLauncher.java @@ -13,19 +13,49 @@ import org.springframework.stereotype.Component; import lombok.RequiredArgsConstructor; +/** + * Quartz 작업을 실행하는 Spring Batch Job 실행기 클래스입니다. + * + *

이 클래스는 Quartz 스케줄러에 의해 호출되며, 지정된 Spring Batch Job을 실행합니다.

+ * + *

주요 기능:

+ *
    + *
  • JobRegistry에서 지정된 이름의 Job을 조회
  • + *
  • Job 실행을 위한 JobParameters 생성
  • + *
  • JobLauncher를 통한 Job 실행
  • + *
+ * + * @author mindol + * @version 1.0 + * @see QuartzJobBean + * @see JobLauncher + * @see JobRegistry + */ @Component @RequiredArgsConstructor public class QuartzJobLauncher extends QuartzJobBean { private final JobLauncher jobLauncher; private final JobRegistry jobRegistry; - + private String jobName; public void setJobName(String jobName) { this.jobName = jobName; } + /** + * Quartz 스케줄러에 의해 호출되는 메소드로, 실제 Job을 실행합니다. + * + *

이 메소드는 다음과 같은 작업을 수행합니다:

+ *
    + *
  1. JobRegistry에서 지정된 이름의 Job을 조회
  2. + *
  3. 현재 시간을 기반으로 한 고유한 JobParameters 생성
  4. + *
  5. JobLauncher를 사용하여 Job 실행
  6. + *
+ * + * @param context Quartz JobExecutionContext 객체 + */ @Override protected void executeInternal(@NonNull JobExecutionContext context) throws JobExecutionException { try { diff --git a/batch-quartz/src/main/java/com/spring/infra/quartz/QuartzJobRegistrar.java b/batch-quartz/src/main/java/com/spring/infra/quartz/QuartzJobRegistrar.java index a56abf3..c351a9c 100644 --- a/batch-quartz/src/main/java/com/spring/infra/quartz/QuartzJobRegistrar.java +++ b/batch-quartz/src/main/java/com/spring/infra/quartz/QuartzJobRegistrar.java @@ -19,6 +19,29 @@ import org.springframework.stereotype.Component; import lombok.RequiredArgsConstructor; +/** + * Quartz 작업 등록기 클래스입니다. + * + *

이 클래스는 애플리케이션 컨텍스트가 리프레시될 때 실행되며, + * {@link QuartzJob} 어노테이션이 붙은 모든 메소드를 찾아 Quartz 스케줄러에 등록합니다.

+ * + *

주요 기능:

+ *
    + *
  • 애플리케이션 컨텍스트 내의 모든 빈을 검사
  • + *
  • QuartzJob 어노테이션이 붙은 메소드 식별
  • + *
  • 식별된 메소드를 Quartz 작업으로 등록
  • + *
  • 각 작업에 대한 JobDetail 및 Trigger 생성
  • + *
+ * + *

이 클래스는 {@link ApplicationListener}를 구현하여 + * {@link ContextRefreshedEvent}가 발생할 때 자동으로 실행됩니다.

+ * + * @author mindol + * @version 1.0 + * @see QuartzJob + * @see ApplicationListener + * @see ContextRefreshedEvent + */ @Component @RequiredArgsConstructor public class QuartzJobRegistrar implements ApplicationListener { @@ -26,6 +49,12 @@ public class QuartzJobRegistrar implements ApplicationListener이 클래스는 application.properties 또는 application.yml 파일에서 + * "spring.quartz.properties.org.quartz" 접두사로 시작하는 설정을 읽어옵니다.

+ * + *

주요 기능:

+ *
    + *
  • JobStore, Scheduler, ThreadPool 관련 설정 관리
  • + *
  • 설정값을 Properties 객체로 변환
  • + *
+ * + * @author mindol + * @version 1.0 + */ @Slf4j @Getter @RequiredArgsConstructor @@ -45,18 +60,38 @@ public class QuartzProperties { private final int threadPriority; } + /** + * 현재 객체의 모든 속성을 Properties 객체로 변환합니다. + * + * @return 변환된 Properties 객체 + */ public Properties toProperties() { Properties properties = new Properties(); addProperties(PREFIX, this, properties); return properties; } + /** + * 주어진 객체의 모든 필드를 재귀적으로 순회하며 Properties 객체에 추가합니다. + * + * @param prefix 속성 키의 접두사 + * @param object 속성을 추출할 객체 + * @param properties 속성을 저장할 Properties 객체 + */ private void addProperties(String prefix, Object object, Properties properties) { Arrays.stream(object.getClass().getDeclaredFields()) .filter(field -> !Modifier.isStatic(field.getModifiers())) .forEach(field -> setProperties(prefix, object, properties, field)); } + /** + * 주어진 필드의 값을 Properties 객체에 추가합니다. + * + * @param prefix 속성 키의 접두사 + * @param object 속성을 추출할 객체 + * @param properties 속성을 저장할 Properties 객체 + * @param field 처리할 필드 + */ private void setProperties(String prefix, Object object, Properties properties, Field field) { try { Object value = field.get(object); @@ -74,6 +109,12 @@ public class QuartzProperties { } } + /** + * 주어진 타입이 단순 타입(primitive, String, Number, Boolean, Character)인지 확인합니다. + * + * @param type 확인할 클래스 타입 + * @return 단순 타입이면 true, 그렇지 않으면 false + */ private boolean isSimpleType(Class type) { return type.isPrimitive() || String.class == type diff --git a/batch-quartz/src/main/java/com/spring/infra/security/config/SecurityConfig.java b/batch-quartz/src/main/java/com/spring/infra/security/config/SecurityConfig.java index 4c3e4d5..48867b2 100644 --- a/batch-quartz/src/main/java/com/spring/infra/security/config/SecurityConfig.java +++ b/batch-quartz/src/main/java/com/spring/infra/security/config/SecurityConfig.java @@ -23,16 +23,30 @@ import com.spring.infra.security.handler.JwtAccessDeniedHandler; import com.spring.infra.security.handler.JwtAuthenticationEntryPoint; import com.spring.infra.security.jwt.JwtTokenService; -import lombok.RequiredArgsConstructor; - +/** + * 애플리케이션의 보안 설정을 담당하는 구성 클래스입니다. + * + *

이 클래스는 Spring Security를 구성하고 JWT 기반의 인증을 설정합니다.

+ * + * @author mindol + * @version 1.0 + */ @Configuration @EnableWebSecurity @EnableMethodSecurity -@RequiredArgsConstructor public class SecurityConfig { private static final String[] PERMITTED_URI = {"/favicon.ico", "/api/auth/**", "/signIn", "/h2-console/**"}; + /** + * Spring Security의 필터 체인을 구성합니다. + * + * @param http HttpSecurity 객체 + * @param tokenService JWT 토큰 서비스 + * @param authenticationEntryPoint JWT 인증 진입점 + * @param accessDeniedHandler JWT 접근 거부 핸들러 + * @return 구성된 SecurityFilterChain + */ @Bean SecurityFilterChain securityFilterChain( HttpSecurity http, @@ -46,7 +60,7 @@ public class SecurityConfig { .httpBasic(HttpBasicConfigurer::disable) .formLogin(FormLoginConfigurer::disable) .authorizeHttpRequests(auth -> auth - .requestMatchers(PERMITTED_URI).permitAll() + .antMatchers(PERMITTED_URI).permitAll() .anyRequest().authenticated() ) .logout(logout -> logout @@ -63,11 +77,21 @@ public class SecurityConfig { return http.build(); } + /** + * 특정 요청에 대해 보안 검사를 무시하도록 설정합니다. + * + * @return WebSecurityCustomizer 객체 + */ @Bean WebSecurityCustomizer ignoringCustomizer() { - return web -> web.ignoring().requestMatchers("/h2-console/**"); + return web -> web.ignoring().antMatchers("/h2-console/**"); } + /** + * 비밀번호 인코더를 구성합니다. + * + * @return PasswordEncoder 객체 + */ @Bean PasswordEncoder passwordEncoder() { return PasswordEncoderFactories.createDelegatingPasswordEncoder(); diff --git a/batch-quartz/src/main/java/com/spring/infra/security/domain/UserPrincipal.java b/batch-quartz/src/main/java/com/spring/infra/security/domain/UserPrincipal.java index feab821..3c04ff2 100644 --- a/batch-quartz/src/main/java/com/spring/infra/security/domain/UserPrincipal.java +++ b/batch-quartz/src/main/java/com/spring/infra/security/domain/UserPrincipal.java @@ -12,16 +12,34 @@ import com.spring.domain.user.entity.AppUser; import lombok.Getter; import lombok.RequiredArgsConstructor; +/** + * Spring Security의 UserDetails 인터페이스를 구현한 사용자 주체(Principal) 클래스입니다. + *

애플리케이션의 사용자 정보를 Spring Security에서 사용할 수 있는 형태로 변환합니다.

+ * + * @author mindol + * @version 1.0 + */ @Getter @RequiredArgsConstructor public final class UserPrincipal implements UserDetails { - private final AppUser appUser; + private final transient AppUser appUser; + /** + * AppUser 객체로부터 UserPrincipal 객체를 생성합니다. + * + * @param appUser 변환할 AppUser 객체 + * @return 생성된 UserPrincipal 객체 + */ public static UserPrincipal valueOf(AppUser appUser) { return new UserPrincipal(appUser); } + /** + * 사용자의 권한 목록을 반환합니다. + * + * @return 사용자의 GrantedAuthority 컬렉션 + */ @Override public Collection getAuthorities() { return appUser.getAppUserRoleMap().stream() @@ -30,31 +48,62 @@ public final class UserPrincipal implements UserDetails { .collect(Collectors.toList()); } + /** + * 계정이 만료되지 않았는지 확인합니다. + * + * @return 계정 만료 여부 (true: 만료되지 않음) + */ @Override public boolean isAccountNonExpired() { return true; } + /** + * 계정이 잠기지 않았는지 확인합니다. + * + * @return 계정 잠금 여부 (true: 잠기지 않음) + */ @Override public boolean isAccountNonLocked() { return true; } + /** + * 자격 증명(비밀번호)이 만료되지 않았는지 확인합니다. + * + * @return 자격 증명 만료 여부 (true: 만료되지 않음) + */ @Override public boolean isCredentialsNonExpired() { return true; } + /** + * 계정이 활성화되어 있는지 확인합니다. + * + * @return 계정 활성화 여부 (true: 활성화됨) + */ @Override public boolean isEnabled() { return true; } + /** + * 사용자의 비밀번호를 반환합니다. + * 이 구현에서는 null을 반환합니다. + * + * @return 사용자 비밀번호 (null) + */ @Override public String getPassword() { return null; } + /** + * 사용자의 로그인 ID를 반환합니다. + * + * @return 사용자 로그인 ID + */ @Override public String getUsername() { return appUser.getLoginId(); diff --git a/batch-quartz/src/main/java/com/spring/infra/security/filter/JwtAuthenticationFilter.java b/batch-quartz/src/main/java/com/spring/infra/security/filter/JwtAuthenticationFilter.java index 19deef0..205c603 100644 --- a/batch-quartz/src/main/java/com/spring/infra/security/filter/JwtAuthenticationFilter.java +++ b/batch-quartz/src/main/java/com/spring/infra/security/filter/JwtAuthenticationFilter.java @@ -3,6 +3,11 @@ package com.spring.infra.security.filter; import java.io.IOException; import java.util.List; +import javax.servlet.FilterChain; +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + import org.springframework.lang.NonNull; import org.springframework.security.core.Authentication; import org.springframework.security.core.context.SecurityContextHolder; @@ -11,21 +16,39 @@ import org.springframework.web.filter.OncePerRequestFilter; import com.spring.infra.security.jwt.JwtTokenRule; import com.spring.infra.security.jwt.JwtTokenService; -import jakarta.servlet.FilterChain; -import jakarta.servlet.ServletException; -import jakarta.servlet.http.HttpServletRequest; -import jakarta.servlet.http.HttpServletResponse; import lombok.RequiredArgsConstructor; +/** + * JWT 인증을 처리하는 필터 클래스입니다. + * + *

이 필터는 요청마다 JWT 토큰을 검증하고, 필요한 경우 토큰을 갱신합니다.

+ * + * @author mindol + * @version 1.0 + */ @RequiredArgsConstructor public final class JwtAuthenticationFilter extends OncePerRequestFilter { private final JwtTokenService jwtTokenService; private final List permitAllUrls; + /** + * 요청마다 실행되는 필터 메소드입니다. + * + *

JWT 토큰을 검증하고, 필요한 경우 토큰을 갱신합니다.

+ * + * @param request HTTP 요청 객체 + * @param response HTTP 응답 객체 + * @param filterChain 필터 체인 + * @throws ServletException 서블릿 예외 + * @throws IOException 입출력 예외 + */ @Override - protected void doFilterInternal(@NonNull HttpServletRequest request, @NonNull HttpServletResponse response, @NonNull FilterChain filterChain) - throws ServletException, IOException { + protected void doFilterInternal( + @NonNull HttpServletRequest request, + @NonNull HttpServletResponse response, + @NonNull FilterChain filterChain + ) throws ServletException, IOException { String requestURI = request.getRequestURI(); if (permitAllUrls.stream().anyMatch(requestURI::startsWith)) { @@ -51,11 +74,15 @@ public final class JwtAuthenticationFilter extends OncePerRequestFilter { } jwtTokenService.deleteCookie(response); - } - private void setAuthenticationToContext(String accessToken) { - Authentication authentication = jwtTokenService.getAuthentication(accessToken); + /** + * 인증 정보를 SecurityContext에 설정합니다. + * + * @param token 토큰 + */ + private void setAuthenticationToContext(String token) { + Authentication authentication = jwtTokenService.getAuthentication(token); SecurityContextHolder.getContext().setAuthentication(authentication); } diff --git a/batch-quartz/src/main/java/com/spring/infra/security/handler/JwtAccessDeniedHandler.java b/batch-quartz/src/main/java/com/spring/infra/security/handler/JwtAccessDeniedHandler.java index 06a04a6..199e21b 100644 --- a/batch-quartz/src/main/java/com/spring/infra/security/handler/JwtAccessDeniedHandler.java +++ b/batch-quartz/src/main/java/com/spring/infra/security/handler/JwtAccessDeniedHandler.java @@ -2,20 +2,38 @@ package com.spring.infra.security.handler; import java.io.IOException; +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + import org.springframework.security.access.AccessDeniedException; import org.springframework.security.web.access.AccessDeniedHandler; import org.springframework.stereotype.Component; -import jakarta.servlet.ServletException; -import jakarta.servlet.http.HttpServletRequest; -import jakarta.servlet.http.HttpServletResponse; - /** - * 유저 정보는 있으나 자원에 접근할 수 있는 권한이 없는 경우 : SC_FORBIDDEN (403) 응답 + * JWT 인증에서 접근 거부 상황을 처리하는 핸들러 클래스입니다. + * + *

이 클래스는 사용자가 인증은 되었지만 특정 리소스에 대한 접근 권한이 없는 경우를 처리합니다. + * 이런 경우 SC_FORBIDDEN (403) 응답을 반환합니다.

+ * + * @author mindol + * @version 1.0 */ @Component public class JwtAccessDeniedHandler implements AccessDeniedHandler { + /** + * 접근 거부 상황을 처리합니다. + * + *

사용자가 접근 권한이 없는 리소스에 접근을 시도할 때 호출됩니다. + * 이 메소드는 SC_FORBIDDEN (403) 상태 코드를 응답으로 전송합니다.

+ * + * @param request 현재 HTTP 요청 + * @param response 현재 HTTP 응답 + * @param accessDeniedException 발생한 접근 거부 예외 + * @throws IOException 입출력 예외 발생 시 + * @throws ServletException 서블릿 예외 발생 시 + */ @Override public void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException accessDeniedException) throws IOException, ServletException { diff --git a/batch-quartz/src/main/java/com/spring/infra/security/handler/JwtAuthenticationEntryPoint.java b/batch-quartz/src/main/java/com/spring/infra/security/handler/JwtAuthenticationEntryPoint.java index f687b75..aaa70d0 100644 --- a/batch-quartz/src/main/java/com/spring/infra/security/handler/JwtAuthenticationEntryPoint.java +++ b/batch-quartz/src/main/java/com/spring/infra/security/handler/JwtAuthenticationEntryPoint.java @@ -2,20 +2,38 @@ package com.spring.infra.security.handler; import java.io.IOException; +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + import org.springframework.security.core.AuthenticationException; import org.springframework.security.web.AuthenticationEntryPoint; import org.springframework.stereotype.Component; -import jakarta.servlet.ServletException; -import jakarta.servlet.http.HttpServletRequest; -import jakarta.servlet.http.HttpServletResponse; - /** - * 유저 정보 없이 접근한 경우 : SC_UNAUTHORIZED (401) 응답 + * JWT 인증에서 인증되지 않은 접근을 처리하는 진입점 클래스입니다. + * + *

이 클래스는 사용자가 인증되지 않은 상태에서 보호된 리소스에 접근을 시도할 때 호출됩니다. + * 이런 경우 SC_UNAUTHORIZED (401) 응답을 반환합니다.

+ * + * @author mindol + * @version 1.0 */ @Component public class JwtAuthenticationEntryPoint implements AuthenticationEntryPoint { + /** + * 인증되지 않은 접근을 처리합니다. + * + *

인증되지 않은 사용자가 보호된 리소스에 접근을 시도할 때 호출됩니다. + * 이 메소드는 SC_UNAUTHORIZED (401) 상태 코드를 응답으로 전송합니다.

+ * + * @param request 현재 HTTP 요청 + * @param response 현재 HTTP 응답 + * @param authException 발생한 인증 예외 + * @throws IOException 입출력 예외 발생 시 + * @throws ServletException 서블릿 예외 발생 시 + */ @Override public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException, ServletException { diff --git a/batch-quartz/src/main/java/com/spring/infra/security/jwt/JwtProperties.java b/batch-quartz/src/main/java/com/spring/infra/security/jwt/JwtProperties.java index 93adcc1..b1f66b5 100644 --- a/batch-quartz/src/main/java/com/spring/infra/security/jwt/JwtProperties.java +++ b/batch-quartz/src/main/java/com/spring/infra/security/jwt/JwtProperties.java @@ -5,6 +5,12 @@ import org.springframework.boot.context.properties.ConfigurationProperties; import lombok.Getter; import lombok.RequiredArgsConstructor; +/** + * JWT(JSON Web Token) 관련 설정 속성을 관리하는 클래스입니다. + * 'jwt' 접두사로 시작하는 설정 속성을 바인딩합니다. + * @author mindol + * @version 1.0 + */ @Getter @ConfigurationProperties(prefix = "jwt") @RequiredArgsConstructor diff --git a/batch-quartz/src/main/java/com/spring/infra/security/jwt/JwtTokenGenerator.java b/batch-quartz/src/main/java/com/spring/infra/security/jwt/JwtTokenGenerator.java index 07b2fd6..0cb4ae4 100644 --- a/batch-quartz/src/main/java/com/spring/infra/security/jwt/JwtTokenGenerator.java +++ b/batch-quartz/src/main/java/com/spring/infra/security/jwt/JwtTokenGenerator.java @@ -17,12 +17,26 @@ import io.jsonwebtoken.Jwts; import io.jsonwebtoken.SignatureAlgorithm; import lombok.RequiredArgsConstructor; +/** + * JWT 토큰을 생성하는 클래스입니다. + * + *

이 클래스는 액세스 토큰과 리프레시 토큰을 생성하는 기능을 제공합니다.

+ * + * @author mindol + * @version 1.0 + */ @Component @RequiredArgsConstructor public class JwtTokenGenerator { private final JwtProperties jwtProperties; + /** + * 액세스 토큰을 생성합니다. + * + * @param authentication 인증 정보 + * @return 생성된 액세스 토큰 + */ public String generateAccessToken(Authentication authentication) { return Jwts.builder() .setHeader(createHeader()) @@ -33,6 +47,12 @@ public class JwtTokenGenerator { .compact(); } + /** + * 리프레시 토큰을 생성합니다. + * + * @param authentication 인증 정보 + * @return 생성된 리프레시 토큰 + */ public String generateRefreshToken(Authentication authentication) { return Jwts.builder() .setHeader(createHeader()) @@ -42,6 +62,11 @@ public class JwtTokenGenerator { .compact(); } + /** + * JWT 헤더를 생성합니다. + * + * @return JWT 헤더 맵 + */ private Map createHeader() { Map header = new HashMap<>(); header.put("typ", "JWT"); @@ -49,6 +74,12 @@ public class JwtTokenGenerator { return header; } + /** + * JWT 클레임을 생성합니다. + * + * @param authentication 인증 정보 + * @return JWT 클레임 맵 + */ private Map createClaims(Authentication authentication) { Map claims = new HashMap<>(); claims.put(JwtTokenRule.AUTHORITIES_KEY.getValue(), authentication.getAuthorities().stream() diff --git a/batch-quartz/src/main/java/com/spring/infra/security/jwt/JwtTokenRule.java b/batch-quartz/src/main/java/com/spring/infra/security/jwt/JwtTokenRule.java index a5e1ef7..33c382a 100644 --- a/batch-quartz/src/main/java/com/spring/infra/security/jwt/JwtTokenRule.java +++ b/batch-quartz/src/main/java/com/spring/infra/security/jwt/JwtTokenRule.java @@ -3,15 +3,46 @@ package com.spring.infra.security.jwt; import lombok.Getter; import lombok.RequiredArgsConstructor; +/** + * JWT 토큰 관련 규칙을 정의하는 열거형입니다. + * + *

이 열거형은 JWT 토큰 처리에 필요한 다양한 상수 값들을 정의합니다.

+ * + * @author mindol + * @version 1.0 + */ @RequiredArgsConstructor @Getter public enum JwtTokenRule { + /** + * JWT 토큰 발급 시 사용되는 HTTP 헤더 이름입니다. + */ JWT_ISSUE_HEADER("Set-Cookie"), + + /** + * JWT 토큰 해석 시 사용되는 HTTP 헤더 이름입니다. + */ JWT_RESOLVE_HEADER("Cookie"), + + /** + * 액세스 토큰의 접두사입니다. + */ ACCESS_PREFIX("access"), + + /** + * 리프레시 토큰의 접두사입니다. + */ REFRESH_PREFIX("refresh"), + + /** + * Bearer 인증 스키마의 접두사입니다. + */ BEARER_PREFIX("Bearer "), + + /** + * JWT 클레임에서 권한 정보를 나타내는 키입니다. + */ AUTHORITIES_KEY("auth"); private final String value; diff --git a/batch-quartz/src/main/java/com/spring/infra/security/jwt/JwtTokenService.java b/batch-quartz/src/main/java/com/spring/infra/security/jwt/JwtTokenService.java index 8d4ca25..ae43a43 100644 --- a/batch-quartz/src/main/java/com/spring/infra/security/jwt/JwtTokenService.java +++ b/batch-quartz/src/main/java/com/spring/infra/security/jwt/JwtTokenService.java @@ -2,6 +2,10 @@ package com.spring.infra.security.jwt; import java.security.Key; +import javax.servlet.http.Cookie; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + import org.springframework.http.ResponseCookie; import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; import org.springframework.security.core.Authentication; @@ -11,10 +15,15 @@ import org.springframework.stereotype.Service; import com.spring.infra.security.service.UserPrincipalService; import io.jsonwebtoken.Jwts; -import jakarta.servlet.http.Cookie; -import jakarta.servlet.http.HttpServletRequest; -import jakarta.servlet.http.HttpServletResponse; +/** + * JWT 토큰 관련 서비스를 제공하는 클래스입니다. + * + *

이 클래스는 JWT 토큰의 생성, 검증, 해석 등 다양한 토큰 관련 기능을 제공합니다.

+ * + * @author mindol + * @version 1.0 + */ @Service public class JwtTokenService { @@ -41,6 +50,13 @@ public class JwtTokenService { this.refreshExpiration = jwtProperties.getRefreshToken().getExpiration(); } + /** + * 액세스 토큰을 생성하고 응답 헤더에 설정합니다. + * + * @param response HTTP 응답 객체 + * @param authentication 인증 정보 + * @return 생성된 액세스 토큰 + */ public String generateAccessToken(HttpServletResponse response, Authentication authentication) { String accessToken = jwtTokenGenerator.generateAccessToken(authentication); ResponseCookie cookie = setTokenToCookie(JwtTokenRule.ACCESS_PREFIX.getValue(), accessToken, accessExpiration / 1000); @@ -48,6 +64,13 @@ public class JwtTokenService { return accessToken; } + /** + * 리프레시 토큰을 생성하고 응답 헤더에 설정합니다. + * + * @param response HTTP 응답 객체 + * @param authentication 인증 정보 + * @return 생성된 리프레시 토큰 + */ public String generateRefreshToken(HttpServletResponse response, Authentication authentication) { String refreshToken = jwtTokenGenerator.generateRefreshToken(authentication); ResponseCookie cookie = setTokenToCookie(JwtTokenRule.REFRESH_PREFIX.getValue(), refreshToken, refreshExpiration / 1000); @@ -55,6 +78,14 @@ public class JwtTokenService { return refreshToken; } + /** + * 토큰을 쿠키로 설정합니다. + * + * @param tokenPrefix 토큰 접두사 + * @param token 토큰 값 + * @param maxAgeSeconds 쿠키 유효 시간(초) + * @return 생성된 ResponseCookie 객체 + */ private ResponseCookie setTokenToCookie(String tokenPrefix, String token, long maxAgeSeconds) { return ResponseCookie.from(tokenPrefix, token) .path("/") @@ -65,14 +96,34 @@ public class JwtTokenService { .build(); } + /** + * 액세스 토큰의 유효성을 검증합니다. + * + * @param token 검증할 토큰 + * @return 토큰의 유효성 여부 + */ public boolean validateAccessToken(String token) { return jwtTokenUtil.getTokenStatus(token, accessSecretKey) == JwtTokenStatus.AUTHENTICATED; } + /** + * 리프레시 토큰의 유효성을 검증합니다. + * + * @param token 검증할 토큰 + * @return 토큰의 유효성 여부 + */ public boolean validateRefreshToken(String token) { return jwtTokenUtil.getTokenStatus(token, refreshSecretKey) == JwtTokenStatus.AUTHENTICATED; } + /** + * 쿠키에서 토큰을 추출합니다. + * + * @param request HTTP 요청 객체 + * @param tokenPrefix 토큰 접두사 + * @return 추출된 토큰 + * @throws IllegalStateException 토큰이 없을 경우 발생 + */ public String resolveTokenFromCookie(HttpServletRequest request, JwtTokenRule tokenPrefix) { Cookie[] cookies = request.getCookies(); if (cookies == null) { @@ -81,11 +132,24 @@ public class JwtTokenService { return jwtTokenUtil.resolveTokenFromCookie(cookies, tokenPrefix); } + /** + * 토큰으로부터 인증 정보를 생성합니다. + * + * @param token JWT 토큰 + * @return 생성된 Authentication 객체 + */ public Authentication getAuthentication(String token) { UserDetails principal = userPrincipalService.loadUserByUsername(getUserPk(token, accessSecretKey)); return new UsernamePasswordAuthenticationToken(principal, "", principal.getAuthorities()); } + /** + * 토큰에서 사용자 식별자를 추출합니다. + * + * @param token JWT 토큰 + * @param secretKey 비밀 키 + * @return 추출된 사용자 식별자 + */ private String getUserPk(String token, Key secretKey) { return Jwts.parserBuilder() .setSigningKey(secretKey) @@ -95,6 +159,11 @@ public class JwtTokenService { .getSubject(); } + /** + * 액세스 토큰과 리프레시 토큰 쿠키를 삭제합니다. + * + * @param response HTTP 응답 객체 + */ public void deleteCookie(HttpServletResponse response) { Cookie accessCookie = jwtTokenUtil.resetToken(JwtTokenRule.ACCESS_PREFIX); Cookie refreshCookie = jwtTokenUtil.resetToken(JwtTokenRule.REFRESH_PREFIX); diff --git a/batch-quartz/src/main/java/com/spring/infra/security/jwt/JwtTokenStatus.java b/batch-quartz/src/main/java/com/spring/infra/security/jwt/JwtTokenStatus.java index cd4cb81..646b67b 100644 --- a/batch-quartz/src/main/java/com/spring/infra/security/jwt/JwtTokenStatus.java +++ b/batch-quartz/src/main/java/com/spring/infra/security/jwt/JwtTokenStatus.java @@ -3,10 +3,29 @@ package com.spring.infra.security.jwt; import lombok.Getter; import lombok.RequiredArgsConstructor; +/** + * JWT 토큰의 상태를 나타내는 열거형입니다. + * + *

이 열거형은 JWT 토큰의 유효성 검증 결과를 표현하는 데 사용됩니다.

+ * + * @author mindol + * @version 1.0 + */ @RequiredArgsConstructor @Getter public enum JwtTokenStatus { + /** + * 토큰이 유효하고 인증된 상태를 나타냅니다. + */ AUTHENTICATED, + + /** + * 토큰이 만료된 상태를 나타냅니다. + */ EXPIRED, + + /** + * 토큰이 유효하지 않은 상태를 나타냅니다. + */ INVALID } diff --git a/batch-quartz/src/main/java/com/spring/infra/security/jwt/JwtTokenUtil.java b/batch-quartz/src/main/java/com/spring/infra/security/jwt/JwtTokenUtil.java index 24fe419..9a49a78 100644 --- a/batch-quartz/src/main/java/com/spring/infra/security/jwt/JwtTokenUtil.java +++ b/batch-quartz/src/main/java/com/spring/infra/security/jwt/JwtTokenUtil.java @@ -5,19 +5,35 @@ import java.security.Key; import java.util.Arrays; import java.util.Base64; +import javax.servlet.http.Cookie; + import org.springframework.stereotype.Component; import io.jsonwebtoken.ExpiredJwtException; import io.jsonwebtoken.JwtException; import io.jsonwebtoken.Jwts; import io.jsonwebtoken.security.Keys; -import jakarta.servlet.http.Cookie; import lombok.extern.slf4j.Slf4j; +/** + * JWT 토큰 관련 유틸리티 기능을 제공하는 클래스입니다. + * + *

이 클래스는 JWT 토큰의 상태 확인, 쿠키에서 토큰 추출, 서명 키 생성 등의 기능을 제공합니다.

+ * + * @author mindol + * @version 1.0 + */ @Slf4j @Component public class JwtTokenUtil { + /** + * JWT 토큰의 상태를 확인합니다. + * + * @param token 검증할 JWT 토큰 + * @param secretKey 토큰 검증에 사용할 비밀 키 + * @return 토큰의 상태 (AUTHENTICATED, EXPIRED, INVALID) + */ public JwtTokenStatus getTokenStatus(String token, Key secretKey) { try { Jwts.parserBuilder() @@ -34,6 +50,13 @@ public class JwtTokenUtil { } } + /** + * 쿠키에서 특정 접두사를 가진 토큰을 추출합니다. + * + * @param cookies 쿠키 배열 + * @param tokenPrefix 토큰 접두사 + * @return 추출된 토큰 값 (없으면 빈 문자열) + */ public String resolveTokenFromCookie(Cookie[] cookies, JwtTokenRule tokenPrefix) { return Arrays.stream(cookies) .filter(cookie -> cookie.getName().equals(tokenPrefix.getValue())) @@ -42,15 +65,33 @@ public class JwtTokenUtil { .orElse(""); } + /** + * 주어진 비밀 키로 서명 키를 생성합니다. + * + * @param secretKey 비밀 키 문자열 + * @return 생성된 서명 키 + */ public Key getSigningKey(String secretKey) { String encodedKey = encodeToBase64(secretKey); return Keys.hmacShaKeyFor(encodedKey.getBytes(StandardCharsets.UTF_8)); } + /** + * 문자열을 Base64로 인코딩합니다. + * + * @param secretKey 인코딩할 문자열 + * @return Base64로 인코딩된 문자열 + */ private String encodeToBase64(String secretKey) { return Base64.getEncoder().encodeToString(secretKey.getBytes()); } + /** + * 토큰을 리셋하는 쿠키를 생성합니다. + * + * @param tokenPrefix 토큰 접두사 + * @return 생성된 쿠키 객체 + */ public Cookie resetToken(JwtTokenRule tokenPrefix) { Cookie cookie = new Cookie(tokenPrefix.getValue(), null); cookie.setMaxAge(0); diff --git a/batch-quartz/src/main/resources/application.yml b/batch-quartz/src/main/resources/application.yml index df03105..47fc998 100644 --- a/batch-quartz/src/main/resources/application.yml +++ b/batch-quartz/src/main/resources/application.yml @@ -44,6 +44,8 @@ spring: show-sql: true batch: + job: + enabled: true jdbc: initialize-schema: always