Spring Boot動的データ・ソースの実装

6590 ワード

シーンの適用
  • アプリケーションが異なるシーンで異なるデータベースにアクセスする必要がある場合、動的データソースを使用して実装できます.
  • 実装方式:カスタム注釈+AOP
  • 複数のデータ・ソース情報の構成
    jdbc1.driverClassName=com.mysql.jdbc.Driver
    jdbc1.username=root
    jdbc1.password=123456
    jdbc1.url=jdbc:mysql://localhost:3306/db_1
    jdbc2.driverClassName=com.mysql.jdbc.Driver
    jdbc2.username=root
    jdbc2.password=654321
    jdbc2.url=jdbc:mysql://localhost:3306/db_2
    

    データ・ソース名定数列挙の定義
    public enum DataSourceEnum {
        MYSQL_DS, ORACLE_DS
    }
    

    動的データソース注記の定義
    import java.lang.annotation.ElementType;
    import java.lang.annotation.Retention;
    import java.lang.annotation.RetentionPolicy;
    import java.lang.annotation.Target;
    
    @Target({ElementType.METHOD})
    @Retention(RetentionPolicy.RUNTIME)
    public @interface DynamicDS {
        DataSourceEnum value() default DataSourceEnum.MYSQL_DS;
    }
    

    DataSourceContextHolderの定義
    public class DataSourceContextHolder {
        private static ThreadLocal context = new ThreadLocal<>();
    
        public static void setDataSourceName(DataSourceEnum dsName){
            context.set(dsName);
        }
    
        public static DataSourceEnum getDataSourceName(){
            return context.get();
        }
    
        public static void clearDS(){
            context.remove();
        }
    }
    

    pom加入AOPの依存
    
        org.springframework.boot
        spring-boot-starter-aop
    
    

    AOP実現
    import org.aspectj.lang.JoinPoint;
    import org.aspectj.lang.annotation.After;
    import org.aspectj.lang.annotation.Aspect;
    import org.aspectj.lang.annotation.Before;
    import org.aspectj.lang.reflect.MethodSignature;
    import org.springframework.stereotype.Component;
    
    import java.lang.reflect.Method;
    
    @Aspect
    @Component
    public class DynamicDataSourceAspect {
        @Before("@annotation(DynamicDS)")
        public void setDataSource(JoinPoint joinPoint)
                throws NoSuchMethodException {
            String methodName = joinPoint.getSignature().getName();
            Class> clazz = joinPoint.getTarget().getClass();
            Method method = clazz.getMethod(methodName,
                ((MethodSignature) joinPoint.getSignature()).getParameterTypes());
            if (method.isAnnotationPresent(DynamicDS.class)) {
                DataSourceContextHolder.setDataSourceName(
                        method.getAnnotation(DynamicDS.class).value());
            }
        }
    
        @After("@annotation(DynamicDS)")
        public void clearDataSource() {
            DataSourceContextHolder.clearDS();
        }
    }
    

    動的データ・ソースの定義
    import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
    
    public class DynamicDataSource extends AbstractRoutingDataSource {
        @Override
        protected Object determineCurrentLookupKey() {
            return DataSourceContextHolder.getDataSourceName();
        }
    }
    

    Mybatis構成クラス
    import com.alibaba.druid.pool.DruidDataSourceFactory;
    import org.mybatis.spring.SqlSessionFactoryBean;
    import org.mybatis.spring.annotation.MapperScan;
    import org.springframework.beans.factory.annotation.Qualifier;
    import org.springframework.context.EnvironmentAware;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Primary;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.context.annotation.PropertySource;
    import org.springframework.core.env.Environment;
    import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
    import org.springframework.core.io.support.ResourcePatternResolver;
    
    import javax.sql.DataSource;
    import java.io.IOException;
    import java.util.HashMap;
    import java.util.Map;
    import java.util.Properties;
    
    @Configuration
    @MapperScan(basePackages="com.**.dao")
    @PropertySource("classpath:datasource.properties")
    public class MybatisConfig implements EnvironmentAware {
        @Primary
        @Bean
        public DataSource mysqlDS() throws Exception {
            Properties prop = new Properties();
            prop.setProperty("username",this.env.getProperty("jdbc1.username"));
            prop.setProperty("password",this.env.getProperty("jdbc1.password"));
            prop.setProperty("url",this.env.getProperty("jdbc1.url"));
            prop.setProperty("driverClassName",this.env.getProperty("jdbc1.driverClassName"));
            return DruidDataSourceFactory.createDataSource(prop);
        }
    
        @Bean
        public DataSource oracleDS() throws Exception {
            Properties prop = new Properties();
            prop.setProperty("username",this.env.getProperty("jdbc2.username"));
            prop.setProperty("password",this.env.getProperty("jdbc2.password"));
            prop.setProperty("url",this.env.getProperty("jdbc2.url"));
            prop.setProperty("driverClassName",this.env.getProperty("jdbc2.driverClassName"));
            return DruidDataSourceFactory.createDataSource(prop);
        }
    
        @Bean
        public DataSource dynamicDS(@Qualifier("mysqlDS") DataSource mysqlDS,
                                    @Qualifier("oracleDS") DataSource oracleDS) {
            DynamicDataSource dynamicDataSource = new DynamicDataSource();
            dynamicDataSource.setDefaultTargetDataSource(mysqlDS);
            Map targetDataSources = new HashMap<>();
            targetDataSources.put(DataSourceEnum.MYSQL_DS, mysqlDS);
            targetDataSources.put(DataSourceEnum.ORACLE_DS, oracleDS);
            dynamicDataSource.setTargetDataSources(targetDataSources);
            dynamicDataSource.afterPropertiesSet();
            return dynamicDataSource;
        }
    
        @Bean
        public SqlSessionFactoryBean sqlSessionFactory(@Qualifier("dynamicDS") DataSource dynamicDS) throws IOException {
            SqlSessionFactoryBean sqlSessionFactory = new SqlSessionFactoryBean();
            sqlSessionFactory.setDataSource(dynamicDS);
            ResourcePatternResolver res = new PathMatchingResourcePatternResolver();
            sqlSessionFactory.setMapperLocations(
                    res.getResources("classpath:com/**/dao/*.xml"));
            return sqlSessionFactory;
        }
    
        private Environment env;
    
        @Override
        public void setEnvironment(Environment environment) {
            this.env = environment;
        }
    }
    

    具体的な使用
    @DynamicDS(DataSourceEnum.MYSQL_DS)
    
  • この注釈を特定のシーンサービス実装クラスに貼り付ける方法
  • かっこの値は、使用する必要がある特定のデータベースの名前(列挙で定義された定数)
  • です.