From ade2aa0733c8d2b3730eb2ed060e7155de56c0a3 Mon Sep 17 00:00:00 2001 From: mindol1004 Date: Fri, 23 Aug 2024 16:46:23 +0900 Subject: [PATCH] commit --- .../com/spring/domain/post/entity/Post.java | 10 ++-- .../spring/domain/post/mapper/PostMapper.java | 8 +++ .../post/repository/PostRepository.java | 2 +- .../quartz/entity/QrtzBlobTriggers.java | 9 ++-- .../spring/domain/user/entity/AppUser.java | 12 ++--- .../domain/user/entity/AppUserRole.java | 8 ++- .../domain/user/entity/AppUserRoleMap.java | 15 +++--- .../domain/user/mapper/AppUserMapper.java | 11 ++++ .../infra/db/orm/jpa/PrimaryJpaConfig.java | 47 ++++++++++------- .../infra/db/orm/jpa/SecondaryJpaConfig.java | 50 ++++++++++++------- .../jpa}/annotation/DatabaseSelector.java | 2 +- .../orm/jpa/util/DatabaseSelectorFilter.java | 33 ------------ .../infra/db/orm/jpa/util/EntityScanner.java | 42 ++++++++++++++++ .../orm/jpa/util/EntityScannerConfigurer.java | 36 ------------- .../db/orm/mybatis/PrimaryMybatisConfig.java | 38 ++++++-------- .../orm/mybatis/SecondaryMybatisConfig.java | 36 ++++++------- .../orm/mybatis/annotation/PrimaryMapper.java | 12 +++++ .../mybatis/annotation/SecondaryMapper.java | 12 +++++ .../util/MybatisBeanNameGenerator.java | 41 --------------- .../com/spring/infra/quartz/QuartzConfig.java | 33 ++++++------ .../infra/quartz/QuartzJobRegistrar.java | 36 ++++++------- .../resources/mapper/user/AppUserMapper.xml | 10 ++++ 22 files changed, 240 insertions(+), 263 deletions(-) create mode 100644 batch-quartz/src/main/java/com/spring/domain/post/mapper/PostMapper.java create mode 100644 batch-quartz/src/main/java/com/spring/domain/user/mapper/AppUserMapper.java rename batch-quartz/src/main/java/com/spring/infra/db/{ => orm/jpa}/annotation/DatabaseSelector.java (85%) delete mode 100644 batch-quartz/src/main/java/com/spring/infra/db/orm/jpa/util/DatabaseSelectorFilter.java create mode 100644 batch-quartz/src/main/java/com/spring/infra/db/orm/jpa/util/EntityScanner.java delete mode 100644 batch-quartz/src/main/java/com/spring/infra/db/orm/jpa/util/EntityScannerConfigurer.java create mode 100644 batch-quartz/src/main/java/com/spring/infra/db/orm/mybatis/annotation/PrimaryMapper.java create mode 100644 batch-quartz/src/main/java/com/spring/infra/db/orm/mybatis/annotation/SecondaryMapper.java delete mode 100644 batch-quartz/src/main/java/com/spring/infra/db/orm/mybatis/util/MybatisBeanNameGenerator.java create mode 100644 batch-quartz/src/main/resources/mapper/user/AppUserMapper.xml 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 d549f3f..cd85a56 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,7 +1,7 @@ package com.spring.domain.post.entity; import com.spring.infra.db.SecondaryDataSourceConfig; -import com.spring.infra.db.annotation.DatabaseSelector; +import com.spring.infra.db.orm.jpa.annotation.DatabaseSelector; import jakarta.persistence.Column; import jakarta.persistence.Entity; @@ -10,24 +10,22 @@ import jakarta.persistence.GenerationType; import jakarta.persistence.Id; import jakarta.persistence.Table; import lombok.Getter; -import lombok.RequiredArgsConstructor; @DatabaseSelector(SecondaryDataSourceConfig.DATABASE) @Entity @Table(name = "APP_POST") @Getter -@RequiredArgsConstructor public class Post { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) @Column(name = "POST_ID", nullable = false) - private final Long postId; + private Long postId; @Column(name = "TITLE", nullable = false, length = 100) - private final String title; + private String title; @Column(name = "CONTENT", nullable = false, length = 2000) - private final String content; + private String content; } diff --git a/batch-quartz/src/main/java/com/spring/domain/post/mapper/PostMapper.java b/batch-quartz/src/main/java/com/spring/domain/post/mapper/PostMapper.java new file mode 100644 index 0000000..f0bc30f --- /dev/null +++ b/batch-quartz/src/main/java/com/spring/domain/post/mapper/PostMapper.java @@ -0,0 +1,8 @@ +package com.spring.domain.post.mapper; + +import com.spring.infra.db.orm.mybatis.annotation.SecondaryMapper; + +@SecondaryMapper +public interface PostMapper { + +} diff --git a/batch-quartz/src/main/java/com/spring/domain/post/repository/PostRepository.java b/batch-quartz/src/main/java/com/spring/domain/post/repository/PostRepository.java index ff66d2f..e1e19d4 100644 --- a/batch-quartz/src/main/java/com/spring/domain/post/repository/PostRepository.java +++ b/batch-quartz/src/main/java/com/spring/domain/post/repository/PostRepository.java @@ -4,7 +4,7 @@ import org.springframework.data.jpa.repository.JpaRepository; import com.spring.domain.post.entity.Post; import com.spring.infra.db.SecondaryDataSourceConfig; -import com.spring.infra.db.annotation.DatabaseSelector; +import com.spring.infra.db.orm.jpa.annotation.DatabaseSelector; @DatabaseSelector(SecondaryDataSourceConfig.DATABASE) public interface PostRepository extends JpaRepository { 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 7c0fbde..3afd7f9 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 @@ -7,7 +7,6 @@ import jakarta.persistence.Embeddable; import jakarta.persistence.EmbeddedId; import jakarta.persistence.Entity; import jakarta.persistence.JoinColumn; -import jakarta.persistence.JoinColumns; import jakarta.persistence.ManyToOne; import jakarta.persistence.Table; import lombok.EqualsAndHashCode; @@ -25,11 +24,9 @@ public class QrtzBlobTriggers { private byte[] blobData; @ManyToOne - @JoinColumns({ - @JoinColumn(name = "SCHED_NAME", referencedColumnName = "SCHED_NAME", insertable = false, updatable = false), - @JoinColumn(name = "TRIGGER_NAME", referencedColumnName = "TRIGGER_NAME", insertable = false, updatable = false), - @JoinColumn(name = "TRIGGER_GROUP", referencedColumnName = "TRIGGER_GROUP", insertable = false, updatable = false) - }) + @JoinColumn(name = "SCHED_NAME", referencedColumnName = "SCHED_NAME", insertable = false, updatable = false) + @JoinColumn(name = "TRIGGER_NAME", referencedColumnName = "TRIGGER_NAME", insertable = false, updatable = false) + @JoinColumn(name = "TRIGGER_GROUP", referencedColumnName = "TRIGGER_GROUP", insertable = false, updatable = false) private QrtzTriggers qrtzTriggers; @Embeddable 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 b9df1d7..3f1470a 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 @@ -12,29 +12,27 @@ import jakarta.persistence.Id; import jakarta.persistence.OneToMany; import jakarta.persistence.Table; import lombok.Getter; -import lombok.RequiredArgsConstructor; @Entity @Table(name = "APP_USER") @Getter -@RequiredArgsConstructor public final class AppUser { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) @Column(name = "USER_ID", nullable = false) - private final Long userId; + private Long userId; @Column(name = "LOGIN_ID", nullable = false, length = 50) - private final String loginId; + private String loginId; @Column(name = "PASSWORD", nullable = false, length = 128) - private final String password; + private String password; @Column(name = "USER_NAME", nullable = false, length = 50) - private final String userName; + private String userName; @OneToMany(mappedBy = "appUser", cascade = CascadeType.ALL, fetch = FetchType.LAZY) - private final Set appUserRoleMap; + private Set appUserRoleMap; } 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 f2605ea..c14f251 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 @@ -12,23 +12,21 @@ import jakarta.persistence.Id; import jakarta.persistence.OneToMany; import jakarta.persistence.Table; import lombok.Getter; -import lombok.RequiredArgsConstructor; @Entity @Table(name = "APP_USER_ROLE") @Getter -@RequiredArgsConstructor public final class AppUserRole { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) @Column(name = "ROLE_ID", nullable = false) - private final Long roleId; + private Long roleId; @Column(name = "ROLE_TYPE", nullable = false, length = 10) - private final String roleType; + private String roleType; @OneToMany(mappedBy = "appUserRole", cascade = CascadeType.ALL, fetch = FetchType.LAZY) - private final Set appUserRoleMap; + private Set appUserRoleMap; } 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 b6b69fd..f445e15 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 @@ -12,37 +12,34 @@ import jakarta.persistence.MapsId; import jakarta.persistence.Table; import lombok.EqualsAndHashCode; import lombok.Getter; -import lombok.RequiredArgsConstructor; @Entity @Table(name = "APP_USER_ROLE_MAP") @Getter -@RequiredArgsConstructor public final class AppUserRoleMap { @EmbeddedId - private final AppUserRoleMapId id; + private AppUserRoleMapId id; @ManyToOne @MapsId("roleId") @JoinColumn(name = "ROLE_ID") - private final AppUserRole appUserRole; + private AppUserRole appUserRole; @ManyToOne @MapsId("userId") @JoinColumn(name = "USER_ID") - private final AppUser appUser; + private AppUser appUser; @Embeddable @EqualsAndHashCode @Getter - @RequiredArgsConstructor - public static final class AppUserRoleMapId implements Serializable { + public static class AppUserRoleMapId implements Serializable { @Column(name = "ROLE_ID", nullable = false) - private final Long roleId; + private Long roleId; @Column(name = "USER_ID", nullable = false) - private final Long userId; + private Long userId; } } diff --git a/batch-quartz/src/main/java/com/spring/domain/user/mapper/AppUserMapper.java b/batch-quartz/src/main/java/com/spring/domain/user/mapper/AppUserMapper.java new file mode 100644 index 0000000..e13168c --- /dev/null +++ b/batch-quartz/src/main/java/com/spring/domain/user/mapper/AppUserMapper.java @@ -0,0 +1,11 @@ +package com.spring.domain.user.mapper; + +import java.util.Optional; + +import com.spring.domain.user.entity.AppUser; +import com.spring.infra.db.orm.mybatis.annotation.PrimaryMapper; + +@PrimaryMapper +public interface AppUserMapper { + Optional findByLoginId(String loginId); +} 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 0625912..5c39df8 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 @@ -3,46 +3,48 @@ package com.spring.infra.db.orm.jpa; import javax.sql.DataSource; import org.springframework.beans.factory.annotation.Qualifier; -import org.springframework.beans.factory.config.BeanDefinition; +import org.springframework.boot.autoconfigure.orm.jpa.HibernateProperties; +import org.springframework.boot.autoconfigure.orm.jpa.HibernateSettings; +import org.springframework.boot.autoconfigure.orm.jpa.JpaProperties; import org.springframework.boot.orm.jpa.EntityManagerFactoryBuilder; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.ComponentScan.Filter; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.FilterType; import org.springframework.context.annotation.Primary; +import org.springframework.core.type.classreading.MetadataReader; +import org.springframework.core.type.classreading.MetadataReaderFactory; +import org.springframework.core.type.filter.TypeFilter; import org.springframework.data.jpa.repository.config.EnableJpaRepositories; +import org.springframework.lang.NonNull; import org.springframework.orm.jpa.JpaTransactionManager; import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean; import org.springframework.transaction.PlatformTransactionManager; import com.spring.infra.db.PrimaryDataSourceConfig; -import com.spring.infra.db.orm.jpa.util.DatabaseSelectorFilter; -import com.spring.infra.db.orm.jpa.util.EntityScannerConfigurer; +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; @Configuration @EnableJpaRepositories( - basePackages = {PrimaryJpaConfig.BASE_PACKAGE}, + basePackages = PrimaryJpaConfig.BASE_PACKAGE, entityManagerFactoryRef = PrimaryJpaConfig.ENTITY_MANAGER_FACTORY, transactionManagerRef = PrimaryJpaConfig.TRANSACTION_MANAGER, - includeFilters = @Filter(type = FilterType.CUSTOM, classes = DatabaseSelectorFilter.class) + includeFilters = @Filter(type = FilterType.CUSTOM, classes = PrimaryJpaConfig.DatabaseFilter.class) ) +@RequiredArgsConstructor public class PrimaryJpaConfig { public static final String TRANSACTION_MANAGER = "primaryTransactionManager"; - private static final String DATABASE_FILTER = "primaryDatabaseFilter"; private static final String BASE_PACKAGE = "com.spring.domain"; private static final String ENTITY_MANAGER_FACTORY = "primaryEntityManagerFactory"; private static final String PERSISTENCE_UNIT = "primaryPersistenceUnit"; - @Primary - @Bean(name = DATABASE_FILTER) - DatabaseSelectorFilter databaseSelectorFilter() { - var filter = new DatabaseSelectorFilter(); - filter.setDbName(PrimaryDataSourceConfig.DATABASE); - return filter; - } + private final JpaProperties jpaProperties; + private final HibernateProperties hibernateProperties; @Primary @Bean(name = ENTITY_MANAGER_FACTORY) @@ -50,20 +52,27 @@ public class PrimaryJpaConfig { EntityManagerFactoryBuilder builder, @Qualifier(PrimaryDataSourceConfig.DATASOURCE) DataSource dataSource ) { - var entities = EntityScannerConfigurer.scanEntities(BASE_PACKAGE, PrimaryDataSourceConfig.DATABASE); + var properties = hibernateProperties.determineHibernateProperties(jpaProperties.getProperties(), new HibernateSettings()); + var entities = EntityScanner.scanEntities(BASE_PACKAGE); return builder .dataSource(dataSource) - .packages(entities.stream().map(BeanDefinition::getBeanClassName).toArray(String[]::new)) + .packages(entities) .persistenceUnit(PERSISTENCE_UNIT) + .properties(properties) .build(); } @Primary @Bean(TRANSACTION_MANAGER) - PlatformTransactionManager transactionManager( - @Qualifier(ENTITY_MANAGER_FACTORY) EntityManagerFactory entityManagerFactory - ) { - return new JpaTransactionManager(entityManagerFactory); + PlatformTransactionManager transactionManager(@Qualifier(ENTITY_MANAGER_FACTORY) EntityManagerFactory factory) { + return new JpaTransactionManager(factory); + } + + public static class DatabaseFilter implements TypeFilter { + @Override + public boolean match(@NonNull MetadataReader reader, @NonNull MetadataReaderFactory factory) { + return !reader.getAnnotationMetadata().hasAnnotation(DatabaseSelector.class.getName()); + } } } 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 5186d4c..a60c022 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 @@ -1,65 +1,79 @@ package com.spring.infra.db.orm.jpa; +import java.util.Optional; + import javax.sql.DataSource; import org.springframework.beans.factory.annotation.Qualifier; -import org.springframework.beans.factory.config.BeanDefinition; +import org.springframework.boot.autoconfigure.orm.jpa.HibernateProperties; +import org.springframework.boot.autoconfigure.orm.jpa.HibernateSettings; +import org.springframework.boot.autoconfigure.orm.jpa.JpaProperties; import org.springframework.boot.orm.jpa.EntityManagerFactoryBuilder; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.ComponentScan.Filter; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.FilterType; +import org.springframework.core.type.classreading.MetadataReader; +import org.springframework.core.type.classreading.MetadataReaderFactory; +import org.springframework.core.type.filter.TypeFilter; import org.springframework.data.jpa.repository.config.EnableJpaRepositories; +import org.springframework.lang.NonNull; import org.springframework.orm.jpa.JpaTransactionManager; import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean; import org.springframework.transaction.PlatformTransactionManager; import com.spring.infra.db.SecondaryDataSourceConfig; -import com.spring.infra.db.orm.jpa.util.DatabaseSelectorFilter; -import com.spring.infra.db.orm.jpa.util.EntityScannerConfigurer; +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; @Configuration @EnableJpaRepositories( - basePackages = {SecondaryJpaConfig.BASE_PACKAGE}, + basePackages = SecondaryJpaConfig.BASE_PACKAGE, entityManagerFactoryRef = SecondaryJpaConfig.ENTITY_MANAGER_FACTORY, transactionManagerRef = SecondaryJpaConfig.TRANSACTION_MANAGER, - includeFilters = @Filter(type = FilterType.CUSTOM, classes = DatabaseSelectorFilter.class) + includeFilters = @Filter(type = FilterType.CUSTOM, classes = SecondaryJpaConfig.DatabaseFilter.class) ) +@RequiredArgsConstructor public class SecondaryJpaConfig { public static final String TRANSACTION_MANAGER = "secondaryTransactionManager"; - private static final String DATABASE_FILTER = "secondaryDatabaseFilter"; private static final String BASE_PACKAGE = "com.spring.domain"; private static final String ENTITY_MANAGER_FACTORY = "secondaryEntityManagerFactory"; private static final String PERSISTENCE_UNIT = "secondaryPersistenceUnit"; - @Bean(name = DATABASE_FILTER) - DatabaseSelectorFilter databaseSelectorFilter() { - var filter = new DatabaseSelectorFilter(); - filter.setDbName(SecondaryDataSourceConfig.DATABASE); - return filter; - } + private final JpaProperties jpaProperties; + private final HibernateProperties hibernateProperties; @Bean(name = ENTITY_MANAGER_FACTORY) LocalContainerEntityManagerFactoryBean entityManagerFactory( EntityManagerFactoryBuilder builder, @Qualifier(SecondaryDataSourceConfig.DATASOURCE) DataSource dataSource ) { - var entities = EntityScannerConfigurer.scanEntities(BASE_PACKAGE, SecondaryDataSourceConfig.DATABASE); + var properties = hibernateProperties.determineHibernateProperties(jpaProperties.getProperties(), new HibernateSettings()); + var entities = EntityScanner.scanEntities(BASE_PACKAGE, SecondaryDataSourceConfig.DATABASE); return builder .dataSource(dataSource) - .packages(entities.stream().map(BeanDefinition::getBeanClassName).toArray(String[]::new)) + .packages(entities) .persistenceUnit(PERSISTENCE_UNIT) + .properties(properties) .build(); } @Bean(TRANSACTION_MANAGER) - PlatformTransactionManager transactionManager( - @Qualifier(ENTITY_MANAGER_FACTORY) EntityManagerFactory entityManagerFactory - ) { - return new JpaTransactionManager(entityManagerFactory); + PlatformTransactionManager transactionManager(@Qualifier(ENTITY_MANAGER_FACTORY) EntityManagerFactory factory) { + return new JpaTransactionManager(factory); + } + + public static class DatabaseFilter implements TypeFilter { + @Override + public boolean match(@NonNull MetadataReader reader, @NonNull MetadataReaderFactory factory) { + return Optional.ofNullable(reader.getAnnotationMetadata().getAnnotationAttributes(DatabaseSelector.class.getName())) + .map(attributes -> SecondaryDataSourceConfig.DATABASE.equals(attributes.get("value"))) + .orElse(false); + } } } diff --git a/batch-quartz/src/main/java/com/spring/infra/db/annotation/DatabaseSelector.java b/batch-quartz/src/main/java/com/spring/infra/db/orm/jpa/annotation/DatabaseSelector.java similarity index 85% rename from batch-quartz/src/main/java/com/spring/infra/db/annotation/DatabaseSelector.java rename to batch-quartz/src/main/java/com/spring/infra/db/orm/jpa/annotation/DatabaseSelector.java index a28570f..fb0ca72 100644 --- a/batch-quartz/src/main/java/com/spring/infra/db/annotation/DatabaseSelector.java +++ b/batch-quartz/src/main/java/com/spring/infra/db/orm/jpa/annotation/DatabaseSelector.java @@ -1,4 +1,4 @@ -package com.spring.infra.db.annotation; +package com.spring.infra.db.orm.jpa.annotation; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; diff --git a/batch-quartz/src/main/java/com/spring/infra/db/orm/jpa/util/DatabaseSelectorFilter.java b/batch-quartz/src/main/java/com/spring/infra/db/orm/jpa/util/DatabaseSelectorFilter.java deleted file mode 100644 index 4514fc9..0000000 --- a/batch-quartz/src/main/java/com/spring/infra/db/orm/jpa/util/DatabaseSelectorFilter.java +++ /dev/null @@ -1,33 +0,0 @@ -package com.spring.infra.db.orm.jpa.util; - -import java.io.IOException; -import java.util.Optional; - -import org.springframework.core.type.classreading.MetadataReader; -import org.springframework.core.type.classreading.MetadataReaderFactory; -import org.springframework.core.type.filter.TypeFilter; -import org.springframework.lang.NonNull; - -import com.spring.infra.db.annotation.DatabaseSelector; - -import lombok.Setter; - -public class DatabaseSelectorFilter implements TypeFilter { - - @Setter private String dbName; - - @Override - public boolean match(@NonNull MetadataReader metadataReader, @NonNull MetadataReaderFactory metadataReaderFactory) - throws IOException { - boolean hasAnnotation = metadataReader.getAnnotationMetadata().hasAnnotation(DatabaseSelector.class.getName()); - if (!hasAnnotation) { - return true; - } else { - return Optional.ofNullable(metadataReader.getAnnotationMetadata() - .getAnnotationAttributes(DatabaseSelector.class.getName())) - .map(attributes -> dbName.equals(attributes.get("value"))) - .orElse(false); - } - } - -} 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 new file mode 100644 index 0000000..1d39e4c --- /dev/null +++ b/batch-quartz/src/main/java/com/spring/infra/db/orm/jpa/util/EntityScanner.java @@ -0,0 +1,42 @@ +package com.spring.infra.db.orm.jpa.util; + +import org.springframework.beans.factory.config.BeanDefinition; +import org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider; +import org.springframework.core.type.classreading.MetadataReader; +import org.springframework.core.type.classreading.MetadataReaderFactory; +import org.springframework.util.StringUtils; + +import com.spring.infra.db.orm.jpa.annotation.DatabaseSelector; + +import jakarta.persistence.Entity; +import lombok.AccessLevel; +import lombok.NoArgsConstructor; + +@NoArgsConstructor(access = AccessLevel.PRIVATE) +public class EntityScanner { + + public static String[] scanEntities(String basePackage) { + return scanEntities(basePackage, null); + } + + public static String[] scanEntities(String basePackage, String dbName) { + var scanner = new ClassPathScanningCandidateComponentProvider(false); + scanner.addIncludeFilter((MetadataReader reader, MetadataReaderFactory factory) -> { + if (!reader.getAnnotationMetadata().hasAnnotation(Entity.class.getName())) { + return false; + } + if (StringUtils.hasText(dbName)) { + var attributes = reader.getAnnotationMetadata().getAnnotationAttributes(DatabaseSelector.class.getName()); + return attributes != null && dbName.equals(attributes.get("value")); + } + return !reader.getAnnotationMetadata().hasAnnotation(DatabaseSelector.class.getName()); + }); + + return scanner.findCandidateComponents(basePackage).stream() + .map(BeanDefinition::getBeanClassName) + .map(className -> className.substring(0, className.lastIndexOf('.'))) + .distinct() + .toArray(String[]::new); + } + +} diff --git a/batch-quartz/src/main/java/com/spring/infra/db/orm/jpa/util/EntityScannerConfigurer.java b/batch-quartz/src/main/java/com/spring/infra/db/orm/jpa/util/EntityScannerConfigurer.java deleted file mode 100644 index a53e0ca..0000000 --- a/batch-quartz/src/main/java/com/spring/infra/db/orm/jpa/util/EntityScannerConfigurer.java +++ /dev/null @@ -1,36 +0,0 @@ -package com.spring.infra.db.orm.jpa.util; - -import java.util.Optional; -import java.util.Set; - -import org.springframework.beans.factory.config.BeanDefinition; -import org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider; -import org.springframework.core.type.classreading.MetadataReader; -import org.springframework.core.type.classreading.MetadataReaderFactory; -import org.springframework.core.type.filter.TypeFilter; -import org.springframework.lang.NonNull; - -import com.spring.infra.db.annotation.DatabaseSelector; - -public class EntityScannerConfigurer { - - public static Set scanEntities(String basePackage, String dbName) { - var scanner = new ClassPathScanningCandidateComponentProvider(false); - scanner.addIncludeFilter(new TypeFilter() { - @Override - public boolean match(@NonNull MetadataReader metadataReader, @NonNull MetadataReaderFactory metadataReaderFactory) { - boolean hasAnnotation = metadataReader.getAnnotationMetadata().hasAnnotation(DatabaseSelector.class.getName()); - if (!hasAnnotation) { - return true; - } else { - return Optional.ofNullable(metadataReader.getAnnotationMetadata() - .getAnnotationAttributes(DatabaseSelector.class.getName())) - .map(attributes -> dbName.equals(attributes.get("value"))) - .orElse(false); - } - } - }); - return scanner.findCandidateComponents(basePackage); - } - -} 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 d402e3a..979e922 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 @@ -14,40 +14,21 @@ import org.springframework.context.annotation.Primary; import org.springframework.core.io.support.PathMatchingResourcePatternResolver; import com.spring.infra.db.PrimaryDataSourceConfig; -import com.spring.infra.db.orm.mybatis.util.MybatisBeanNameGenerator; +import com.spring.infra.db.orm.mybatis.annotation.PrimaryMapper; @Configuration @MapperScan( - basePackages = PrimaryMybatisConfig.BASE_PACKAGE, + basePackages = {PrimaryMybatisConfig.BASE_PACKAGE}, sqlSessionFactoryRef = PrimaryMybatisConfig.SESSION_FACTORY, - nameGenerator = MybatisBeanNameGenerator.class + annotationClass = PrimaryMapper.class ) public class PrimaryMybatisConfig { - private static final String BEAN_NAME_GENERATOR = "primaryNameGenerator"; - private static final String BASE_PACKAGE = "com.spring.domain"; - private static final String ALIASES_PACKAGE = "com.spring.domain.*.*.dto"; + private static final String BASE_PACKAGE = "com.spring.domain.*.mapper"; + private static final String ALIASES_PACKAGE = "com.spring.domain.*.dto"; private static final String MAPPER_RESOURCES = "classpath:mapper/**/*.xml"; - private static final String MYBATIS_CONFIG = "primaryMybatisConfig"; private static final String SESSION_FACTORY = "primarySqlSessionFactory"; - @Primary - @Bean(name = BEAN_NAME_GENERATOR) - MybatisBeanNameGenerator mybatisBeanNameGenerator() { - return new MybatisBeanNameGenerator(PrimaryDataSourceConfig.DATABASE); - } - - @Primary - @Bean(name = MYBATIS_CONFIG) - org.apache.ibatis.session.Configuration mybatisConfiguration() { - var configuration = new org.apache.ibatis.session.Configuration(); - configuration.setMapUnderscoreToCamelCase(true); - configuration.setCallSettersOnNulls(true); - configuration.setReturnInstanceForEmptyRow(true); - configuration.setJdbcTypeForNull(JdbcType.NULL); - return configuration; - } - @Primary @Bean(name = SESSION_FACTORY) SqlSessionFactory sqlSessionFactory(@Qualifier(PrimaryDataSourceConfig.DATASOURCE) DataSource dataSource) throws Exception { @@ -60,4 +41,13 @@ public class PrimaryMybatisConfig { return sessionFactory.getObject(); } + private org.apache.ibatis.session.Configuration mybatisConfiguration() { + var configuration = new org.apache.ibatis.session.Configuration(); + configuration.setMapUnderscoreToCamelCase(true); + configuration.setCallSettersOnNulls(true); + configuration.setReturnInstanceForEmptyRow(true); + configuration.setJdbcTypeForNull(JdbcType.NULL); + return configuration; + } + } 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 4402545..6138f9c 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 @@ -13,38 +13,21 @@ import org.springframework.context.annotation.Configuration; import org.springframework.core.io.support.PathMatchingResourcePatternResolver; import com.spring.infra.db.SecondaryDataSourceConfig; -import com.spring.infra.db.orm.mybatis.util.MybatisBeanNameGenerator; +import com.spring.infra.db.orm.mybatis.annotation.SecondaryMapper; @Configuration @MapperScan( - basePackages = SecondaryMybatisConfig.BASE_PACKAGE, + basePackages = {SecondaryMybatisConfig.BASE_PACKAGE}, sqlSessionFactoryRef = SecondaryMybatisConfig.SESSION_FACTORY, - nameGenerator = MybatisBeanNameGenerator.class + annotationClass = SecondaryMapper.class ) public class SecondaryMybatisConfig { - private static final String BEAN_NAME_GENERATOR = "secondaryNameGenerator"; - private static final String BASE_PACKAGE = "com.spring.domain"; - private static final String ALIASES_PACKAGE = "com.spring.domain.*.*.dto"; + private static final String BASE_PACKAGE = "com.spring.domain.*.mapper"; + private static final String ALIASES_PACKAGE = "com.spring.domain.*.dto"; private static final String MAPPER_RESOURCES = "classpath:mapper/**/*.xml"; - private static final String MYBATIS_CONFIG = "secondaryMybatisConfig"; private static final String SESSION_FACTORY = "secondarySqlSessionFactory"; - @Bean(name = BEAN_NAME_GENERATOR) - MybatisBeanNameGenerator mybatisBeanNameGenerator() { - return new MybatisBeanNameGenerator(SecondaryDataSourceConfig.DATABASE); - } - - @Bean(name = MYBATIS_CONFIG) - org.apache.ibatis.session.Configuration mybatisConfiguration() { - var configuration = new org.apache.ibatis.session.Configuration(); - configuration.setMapUnderscoreToCamelCase(true); - configuration.setCallSettersOnNulls(true); - configuration.setReturnInstanceForEmptyRow(true); - configuration.setJdbcTypeForNull(JdbcType.NULL); - return configuration; - } - @Bean(name = SESSION_FACTORY) SqlSessionFactory sqlSessionFactory(@Qualifier(SecondaryDataSourceConfig.DATASOURCE) DataSource dataSource) throws Exception { var sessionFactory = new SqlSessionFactoryBean(); @@ -56,4 +39,13 @@ public class SecondaryMybatisConfig { return sessionFactory.getObject(); } + private org.apache.ibatis.session.Configuration mybatisConfiguration() { + var configuration = new org.apache.ibatis.session.Configuration(); + configuration.setMapUnderscoreToCamelCase(true); + configuration.setCallSettersOnNulls(true); + configuration.setReturnInstanceForEmptyRow(true); + configuration.setJdbcTypeForNull(JdbcType.NULL); + return configuration; + } + } 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 new file mode 100644 index 0000000..3173d33 --- /dev/null +++ b/batch-quartz/src/main/java/com/spring/infra/db/orm/mybatis/annotation/PrimaryMapper.java @@ -0,0 +1,12 @@ +package com.spring.infra.db.orm.mybatis.annotation; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@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 new file mode 100644 index 0000000..66dc5c0 --- /dev/null +++ b/batch-quartz/src/main/java/com/spring/infra/db/orm/mybatis/annotation/SecondaryMapper.java @@ -0,0 +1,12 @@ +package com.spring.infra.db.orm.mybatis.annotation; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Target(ElementType.TYPE) +@Retention(RetentionPolicy.RUNTIME) +public @interface SecondaryMapper { + +} diff --git a/batch-quartz/src/main/java/com/spring/infra/db/orm/mybatis/util/MybatisBeanNameGenerator.java b/batch-quartz/src/main/java/com/spring/infra/db/orm/mybatis/util/MybatisBeanNameGenerator.java deleted file mode 100644 index 9ff9fd7..0000000 --- a/batch-quartz/src/main/java/com/spring/infra/db/orm/mybatis/util/MybatisBeanNameGenerator.java +++ /dev/null @@ -1,41 +0,0 @@ -package com.spring.infra.db.orm.mybatis.util; - -import java.util.Optional; - -import org.springframework.beans.factory.config.BeanDefinition; -import org.springframework.beans.factory.support.BeanDefinitionRegistry; -import org.springframework.context.annotation.AnnotationBeanNameGenerator; -import org.springframework.lang.NonNull; - -import com.spring.infra.db.annotation.DatabaseSelector; - -import lombok.RequiredArgsConstructor; - -@RequiredArgsConstructor -public class MybatisBeanNameGenerator extends AnnotationBeanNameGenerator { - - private final String dbName; - - @Override - @NonNull - public String generateBeanName(@NonNull BeanDefinition definition, @NonNull BeanDefinitionRegistry registry) { - return Optional.ofNullable(definition.getBeanClassName()) - .flatMap(className -> { - try { - var beanClass = Class.forName(className); - var annotation = beanClass.getAnnotation(DatabaseSelector.class); - if (annotation == null) { - return Optional.of(super.generateBeanName(definition, registry)); - } - if (dbName.equals(annotation.value())) { - return Optional.of(super.generateBeanName(definition, registry)); - } - return Optional.empty(); - } catch (ClassNotFoundException e) { - return Optional.empty(); - } - }) - .orElse(""); - } - -} 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 1d8bce9..012b807 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 @@ -2,6 +2,8 @@ package com.spring.infra.quartz; import javax.sql.DataSource; +import org.quartz.Job; +import org.quartz.Scheduler; import org.quartz.spi.JobFactory; import org.quartz.spi.TriggerFiredBundle; import org.springframework.batch.core.configuration.JobRegistry; @@ -9,9 +11,7 @@ import org.springframework.batch.core.configuration.support.JobRegistryBeanPostP import org.springframework.beans.factory.config.AutowireCapableBeanFactory; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; -import org.springframework.lang.NonNull; import org.springframework.scheduling.quartz.SchedulerFactoryBean; -import org.springframework.scheduling.quartz.SpringBeanJobFactory; import org.springframework.transaction.PlatformTransactionManager; import lombok.RequiredArgsConstructor; @@ -30,12 +30,12 @@ public class QuartzConfig { * @param jobRegistry ths Spring Batch Job Registry * @return JobRegistry BeanPostProcessor */ - // @Bean - // JobRegistryBeanPostProcessor jobRegistryBeanPostProcessor(JobRegistry jobRegistry) { - // var jobRegistryBeanPostProcessor = new JobRegistryBeanPostProcessor(); - // jobRegistryBeanPostProcessor.setJobRegistry(jobRegistry); - // return jobRegistryBeanPostProcessor; - // } + @Bean + JobRegistryBeanPostProcessor jobRegistryBeanPostProcessor(JobRegistry jobRegistry) { + var jobRegistryBeanPostProcessor = new JobRegistryBeanPostProcessor(); + jobRegistryBeanPostProcessor.setJobRegistry(jobRegistry); + return jobRegistryBeanPostProcessor; + } /** * Quartz Schedule Job 에 의존성 주입 @@ -45,13 +45,10 @@ public class QuartzConfig { */ @Bean JobFactory jobFactory(AutowireCapableBeanFactory beanFactory) { - return new SpringBeanJobFactory(){ - @Override - protected @NonNull Object createJobInstance(@NonNull final TriggerFiredBundle bundle) throws Exception { - var job = super.createJobInstance(bundle); - beanFactory.autowireBean(job); - return job; - } + return (TriggerFiredBundle bundle, Scheduler scheduler) -> { + Job job = beanFactory.createBean(bundle.getJobDetail().getJobClass()); + beanFactory.autowireBean(job); + return job; }; } @@ -64,12 +61,12 @@ public class QuartzConfig { * @throws Exception the exception */ @Bean - SchedulerFactoryBean schedulerFactoryBean(JobFactory jobFactory) throws Exception { + SchedulerFactoryBean schedulerFactoryBean(JobFactory jobFactory) { var factory = new SchedulerFactoryBean(); factory.setSchedulerName("SampleProject-0.0.1"); factory.setQuartzProperties(quartzProperties.toProperties()); - factory.setOverwriteExistingJobs(true); //Job Detail 데이터 Overwrite 유무 - factory.setDataSource(dataSource); //Schedule 관리를 Spring Datasource 에 위임 + factory.setOverwriteExistingJobs(true); + factory.setDataSource(dataSource); factory.setTransactionManager(transactionManager); factory.setJobFactory(jobFactory); factory.setAutoStartup(true); 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 2c19350..1098b03 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 @@ -10,18 +10,16 @@ import org.quartz.Scheduler; import org.quartz.SchedulerException; import org.quartz.Trigger; import org.quartz.TriggerBuilder; -import org.springframework.batch.core.configuration.JobLocator; -import org.springframework.batch.core.launch.JobLauncher; import org.springframework.beans.BeansException; -import org.springframework.beans.factory.config.BeanPostProcessor; +import org.springframework.beans.factory.InitializingBean; import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContextAware; import org.springframework.lang.NonNull; import org.springframework.stereotype.Component; @Component -public class QuartzJobRegistrar implements BeanPostProcessor, ApplicationContextAware { - +public class QuartzJobRegistrar implements ApplicationContextAware, InitializingBean { + private ApplicationContext applicationContext; private Scheduler scheduler; @@ -35,15 +33,20 @@ public class QuartzJobRegistrar implements BeanPostProcessor, ApplicationContext } @Override - public Object postProcessAfterInitialization(@NonNull Object bean, @NonNull String beanName) throws BeansException { - Class beanClass = bean.getClass(); - for (Method method : beanClass.getMethods()) { - QuartzJob quartzJobAnnotation = method.getAnnotation(QuartzJob.class); - if (quartzJobAnnotation != null) { - registerQuartzJob(quartzJobAnnotation, method.getName()); + public void afterPropertiesSet() { + registerQuartzJobs(); + } + + private void registerQuartzJobs() { + applicationContext.getBeansWithAnnotation(QuartzJob.class).forEach((beanName, bean) -> { + Class beanClass = bean.getClass(); + for (Method method : beanClass.getMethods()) { + QuartzJob quartzJobAnnotation = method.getAnnotation(QuartzJob.class); + if (quartzJobAnnotation != null) { + registerQuartzJob(quartzJobAnnotation, method.getName()); + } } - } - return bean; + }); } private void registerQuartzJob(QuartzJob quartzJobAnnotation, String jobName) { @@ -52,16 +55,15 @@ public class QuartzJobRegistrar implements BeanPostProcessor, ApplicationContext Trigger trigger = createTrigger(quartzJobAnnotation, jobDetail); scheduler.scheduleJob(jobDetail, trigger); } catch (SchedulerException e) { - throw new RuntimeException("Error scheduling Quartz job", e); + throw new IllegalStateException("Error scheduling Quartz job", e); } } private JobDetail createJobDetail(QuartzJob quartzJobAnnotation, String jobName) { JobDataMap jobDataMap = new JobDataMap(); jobDataMap.put("jobName", jobName); - jobDataMap.put("jobLauncher", applicationContext.getBean(JobLauncher.class)); - jobDataMap.put("jobLocator", applicationContext.getBean(JobLocator.class)); - + jobDataMap.put("jobLauncher", applicationContext.getBean("jobLauncher")); + jobDataMap.put("jobLocator", applicationContext.getBean("jobLocator")); return JobBuilder.newJob(QuartzJobLauncher.class) .withIdentity(quartzJobAnnotation.name()) .setJobData(jobDataMap) diff --git a/batch-quartz/src/main/resources/mapper/user/AppUserMapper.xml b/batch-quartz/src/main/resources/mapper/user/AppUserMapper.xml new file mode 100644 index 0000000..1b6c79b --- /dev/null +++ b/batch-quartz/src/main/resources/mapper/user/AppUserMapper.xml @@ -0,0 +1,10 @@ + + + + + + + \ No newline at end of file