commit
This commit is contained in:
@@ -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;
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
package com.spring.domain.post.mapper;
|
||||
|
||||
import com.spring.infra.db.orm.mybatis.annotation.SecondaryMapper;
|
||||
|
||||
@SecondaryMapper
|
||||
public interface PostMapper {
|
||||
|
||||
}
|
||||
@@ -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<Post, Long> {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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> appUserRoleMap;
|
||||
private Set<AppUserRoleMap> appUserRoleMap;
|
||||
|
||||
}
|
||||
|
||||
@@ -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> appUserRoleMap;
|
||||
private Set<AppUserRoleMap> appUserRoleMap;
|
||||
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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<AppUser> findByLoginId(String loginId);
|
||||
}
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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<BeanDefinition> 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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
}
|
||||
@@ -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 {
|
||||
|
||||
}
|
||||
@@ -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("");
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
||||
|
||||
<mapper namespace="com.spring.domain.user.mapper.AppUserMaper">
|
||||
<select id="findByLoginId" resultType="com.spring.domain.user.entity.AppUser">
|
||||
select *
|
||||
from app_user
|
||||
</select>
|
||||
</mapper>
|
||||
Reference in New Issue
Block a user