> Java > java지도 시간 > 본문

SpringBoot 다중 데이터 소스 전환을 구현하는 방법

王林
풀어 주다: 2023-05-22 18:22:06
앞으로
1539명이 탐색했습니다.

    구성 파일(YML)

    spring:
      datasource:
        default-db-key: voidme
        multi-db:
          - voidme:
              driver-class-name: com.mysql.cj.jdbc.Driver
              username: root
              password: root
              url: jdbc:mysql://192.168.42.153:3306/voidme?characterEncoding=utf8&autoReconnect=true&failOverReadOnly=false&maxReconnects=10&useSSL=false
          - xcdef:
              driver-class-name: com.mysql.cj.jdbc.Driver
              username: root
              password: root
              url: jdbc:mysql://192.168.42.153:3306/xcdef?characterEncoding=utf8&autoReconnect=true&failOverReadOnly=false&maxReconnects=10&useSSL=false
    
    
    mybatis:
      #1.classpath:只会到你的classes路径中查找找文件。
      #2.classpath*:不仅会到classes路径,还包括jar文件中(classes路径)进行查找。
      mapper-locations: classpath*:/mapper/**/*Mapper.xml    # mapper映射文件位置
      type-aliases-package: com.**.entity    # 实体类所在的位置
      configuration:
        log-impl: org.apache.ibatis.logging.stdout.StdOutImpl   #用于控制台打印sql语句
        map-underscore-to-camel-case: true #开启将带有下划线的表字段 映射为驼峰格式的实体类属性
    로그인 후 복사

    Core code

    SpringBoot 다중 데이터 소스 전환을 구현하는 방법

    DynamicDataSource

    이 클래스는 데이터 소스의 (코어)를 얻기 위해 사용됩니다

    package com.dynamicdatadource.dynamic;
    
    import org.springframework.beans.factory.annotation.Value;
    import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
    
    public class DynamicDataSource extends AbstractRoutingDataSource {
    
        @Value("${spring.datasource.default-db-key}")
        private String defaultDbKey;
    
        @Override
        protected Object determineCurrentLookupKey() {
            String currentDb = DynamicDataSourceService.currentDb();
            if (currentDb == null) {
                return defaultDbKey;
            }
            return currentDb;
        }
    }
    로그인 후 복사

    DynamicDataSourceService

    이 클래스는 데이터 소스 전환입니다 스레드가 격리되어 있으므로 멀티 스레드 데이터 소스의 혼동을 걱정할 필요가 없습니다

    package com.dynamicdatadource.dynamic;
    
    import com.application.ApplicationContextProvider;
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    import org.springframework.boot.jdbc.DataSourceBuilder;
    
    import javax.sql.DataSource;
    import java.util.HashMap;
    import java.util.Map;
    
    public class DynamicDataSourceService  {
        private static final Logger log = LoggerFactory.getLogger(DynamicDataSourceService.class);
    
        private static final Map<Object, Object> dataSources = new HashMap<>();
        private static final ThreadLocal<String> dbKeys = ThreadLocal.withInitial(() -> null);
    
        /**
         * 动态添加一个数据源
         *
         * @param name       数据源的key
         * @param dataSource 数据源对象
         */
        public static void addDataSource(String name, DataSource dataSource) {
            DynamicDataSource dynamicDataSource = ApplicationContextProvider.getApplicationContext().getBean(DynamicDataSource.class);
            dataSources.put(name, dataSource);
            dynamicDataSource.setTargetDataSources(dataSources);
            dynamicDataSource.afterPropertiesSet();
            log.info("添加了数据源:{}",name);
        }
    
        /**
         * @param name   数据源的key
         * @param driverClassName  驱动
         * @param url     数据库连接地址
         * @param username   数据库账户
         * @param password   数据库密码
         */
        public static void addDataSource(String name, String driverClassName,String url,String username,String password) {
            DataSourceBuilder<?> builder = DataSourceBuilder.create();
            builder.driverClassName(driverClassName);
            builder.username(username);
            builder.password(password);
            builder.url(url);
            addDataSource(name,builder.build());
            log.info("添加了数据源:{}",name);
        }
        /**
         * 切换数据源
         */
        public static void switchDb(String dbKey) {
            dbKeys.set(dbKey);
        }
    
        /**
         * 重置数据源(切换为默认的数据源)
         */
        public static void resetDb() {
            dbKeys.remove();
        }
    
        /**
         * 获取当前数据源的key
         */
        public static String currentDb() {
            return dbKeys.get();
        }
    }
    로그인 후 복사

    DynamicDataSourceConfig

    데이터 소스를 springboot로 구성하고 Mybaitis 구성을 초기화합니다

    package com.dynamicdatadource.dynamic;
    
    import lombok.Data;
    import org.apache.ibatis.logging.Log;
    import org.mybatis.spring.SqlSessionFactoryBean;
    import org.springframework.boot.context.properties.ConfigurationProperties;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
    import org.springframework.jdbc.datasource.DataSourceTransactionManager;
    import org.springframework.transaction.PlatformTransactionManager;
    
    import java.io.IOException;
    import java.util.HashMap;
    import java.util.Map;
    
    @Configuration
    @ConfigurationProperties(prefix = "mybatis")
    @Data
    public class DynamicDataSourceConfig {
    
        private String mapperLocations;
        private String typeAliasesPackage;
        @Data
        public class MybatisConfiguration{
            private String logImpl;
            private boolean mapUnderscoreToCamelCase;
        }
        private  MybatisConfiguration configuration=new MybatisConfiguration();
    
    
        /**
         * 动态数据源
         */
        @Bean
        public DynamicDataSource dynamicDataSource() {
            DynamicDataSource dataSource = new DynamicDataSource();
            Map<Object, Object> targetDataSources = new HashMap<>();
            dataSource.setTargetDataSources(targetDataSources);
            return dataSource;
        }
    
        /**
         * 会话工厂Mybaitis
         */
        @Bean
        public SqlSessionFactoryBean sqlSessionFactoryBean() throws IOException, ClassNotFoundException {
            org.apache.ibatis.session.Configuration configuration = new org.apache.ibatis.session.Configuration();
            configuration.setMapUnderscoreToCamelCase(this.configuration.isMapUnderscoreToCamelCase()); //开启驼峰命名
            configuration.setLogImpl((Class<? extends Log>) Class.forName(this.configuration.getLogImpl())); //控制台打印sql日志
            SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
            sqlSessionFactoryBean.setDataSource(dynamicDataSource());
            sqlSessionFactoryBean.setConfiguration(configuration);
            PathMatchingResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
            sqlSessionFactoryBean.setMapperLocations(resolver.getResources(mapperLocations));
            sqlSessionFactoryBean.setTypeAliasesPackage(typeAliasesPackage);
            return sqlSessionFactoryBean;
        }
    
        /**
         * 事务管理器
         */
        @Bean
        public PlatformTransactionManager transactionManager() {
            return new DataSourceTransactionManager(dynamicDataSource());
        }
    }
    로그인 후 복사

    YML 데이터베이스 로드 구성 클래스

    package com.dynamicdatadource.config;
    
    import com.dynamicdatadource.dynamic.DynamicDataSourceService;
    import lombok.Data;
    import org.springframework.boot.autoconfigure.jdbc.DataSourceProperties;
    import org.springframework.boot.context.properties.ConfigurationProperties;
    import org.springframework.boot.jdbc.DataSourceBuilder;
    import org.springframework.stereotype.Component;
    
    import javax.annotation.PostConstruct;
    import javax.sql.DataSource;
    import java.util.List;
    import java.util.Map;
    import java.util.Set;
    
    @Component
    @Data
    @ConfigurationProperties(prefix = "spring.datasource")
    public class YmlDataSourceProvider  {
    
        private List<Map<String, DataSourceProperties>> multiDb;
    
        private DataSource buildDataSource(DataSourceProperties prop) {
            DataSourceBuilder<?> builder = DataSourceBuilder.create();
            builder.driverClassName(prop.getDriverClassName());
            builder.username(prop.getUsername());
            builder.password(prop.getPassword());
            builder.url(prop.getUrl());
            return builder.build();
        }
    
        public void initDataSource() {
            multiDb.forEach(map -> {
                Set<String> keys = map.keySet();
                keys.forEach(key -> {
                    DataSourceProperties properties = map.get(key);
                    DataSource dataSource = buildDataSource(properties);
                    DynamicDataSourceService.addDataSource(key, dataSource);
                });
            });
        }
    
        //在构造函数之后执行
        @PostConstruct
        public void init() {
            initDataSource();
        }
    }
    로그인 후 복사

    aop 스위칭

    package com.dynamicdatadource.aop;
    
    import java.lang.annotation.ElementType;
    import java.lang.annotation.Retention;
    import java.lang.annotation.RetentionPolicy;
    import java.lang.annotation.Target;
    
    @Target({ElementType.METHOD,ElementType.TYPE})//作用:方法和类
    @Retention(RetentionPolicy.RUNTIME)
    public @interface DynamicDataSourceAnno {
        String key() default "";
    }
    로그인 후 복사
    package com.dynamicdatadource.aop;
    import com.dynamicdatadource.dynamic.DynamicDataSourceService;
    import org.apache.commons.lang.StringUtils;
    import org.aspectj.lang.ProceedingJoinPoint;
    import org.aspectj.lang.annotation.Around;
    import org.aspectj.lang.annotation.Aspect;
    import org.aspectj.lang.annotation.Pointcut;
    import org.aspectj.lang.reflect.MethodSignature;
    import org.springframework.stereotype.Component;
    
    // 用于单独的请求或者类进行切换数据库
    @Aspect
    @Component
    public class DynamicDataSourceAspect {
        @Pointcut("@annotation(com.dynamicdatadource.aop.DynamicDataSourceAnno)")
        public void dynamicDataSourceAnno() {
        }
    
        @Around("dynamicDataSourceAnno()")
        public Object DynamicDataSourceAspectAroundAnno(ProceedingJoinPoint joinPoint) {
            Object object = null;
            try {
                MethodSignature signature = (MethodSignature)joinPoint.getSignature();
                DynamicDataSourceAnno dynamicDataSourceAnno  = signature.getMethod().getAnnotation(DynamicDataSourceAnno.class);
                String key = dynamicDataSourceAnno.key();
                if (StringUtils.isNotBlank(key)) {
                    //切换为指定数据库
                    DynamicDataSourceService.switchDb(key);
                }
                object = joinPoint.proceed();
            } catch (Throwable e) {
                e.printStackTrace();
            }finally {
                //还原为默认配置
                DynamicDataSourceService.resetDb();
            }
            return object;
        }
        // 还可以扩展包路径切换
    }
    로그인 후 복사

    Effect

    프로그램을 실행하면 데이터소스가 데이터소스 목록에 추가됩니다

    SpringBoot 다중 데이터 소스 전환을 구현하는 방법

    Extension

    MysqlDataSourceInitialize

    데이터베이스에서 구성 정보를 쿼리한 후 동적으로 데이터 소스 목록에 추가하세요

    package com.dao.config;
    
    import com.dao.DatasourceDao;
    import com.dynamicdatadource.aop.DynamicDataSourceAnno;
    import com.dynamicdatadource.dynamic.DynamicDataSourceService;
    import com.entity.DataSourceEneity;
    import org.springframework.beans.factory.InitializingBean;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.boot.ApplicationArguments;
    import org.springframework.boot.ApplicationRunner;
    import org.springframework.stereotype.Component;
    
    import javax.annotation.PostConstruct;
    import javax.sql.DataSource;
    import java.util.List;
    
    //从数据库中查询出全部的数据源,添加到数据源容器中
    
    /**
     * 表结构如下:
     *
     * CREATE TABLE `t_datasource` (
     *   `id` int(11) NOT NULL,
     *   `key` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT &#39;绑定的key,用于数据源的切换&#39;,
     *   `url` text CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT &#39;数据库连接地址&#39;,
     *   `username` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT &#39;数据库用户名&#39;,
     *   `password` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT &#39;数据库密码&#39;,
     *   `driverClassName` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT &#39;数据库驱动&#39;,
     *   `type` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT &#39;数据库类型:  mysql ,oracle,..&#39;,
     *   `state` int(2) NOT NULL COMMENT &#39;是否可用: 1可用 ,2不可用&#39;,
     *   PRIMARY KEY (`id`),
     *   UNIQUE KEY `key` (`key`)
     * ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
     *
     * 上表要放入到默认数据源中的数据库里才行
     */
    @Component
    public class MysqlDataSourceInitialize implements ApplicationRunner  {
    
        @Autowired
        private DatasourceDao datasourceDao;
    
        //项目启动后执行初始化数据源
        @Override
        public void run(ApplicationArguments args) throws Exception {
            try {
                List<DataSourceEneity> dataSources = datasourceDao.getDataSources();
                for (DataSourceEneity dataSource : dataSources) {
                    DynamicDataSourceService.addDataSource(dataSource.getKey(),dataSource.getDataSource());
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
    로그인 후 복사

    DataSourceEneity 엔터티 클래스

    @Data
    public class DataSourceEneity {
        private int id;
        private String key;
        private String url;
        private String username;
        private String password;
        private String driverClassName;
        private String type;
        private int state;
    
        public  DataSource getDataSource() {
            DataSourceBuilder<?> builder = DataSourceBuilder.create();
            builder.driverClassName(driverClassName);
            builder.username(username);
            builder.password(password);
            builder.url(url);
            return  builder.build();
        }
    }
    로그인 후 복사

    위 내용은 SpringBoot 다중 데이터 소스 전환을 구현하는 방법의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

    관련 라벨:
    원천:yisu.com
    본 웹사이트의 성명
    본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.
    인기 튜토리얼
    더>
    최신 다운로드
    더>
    웹 효과
    웹사이트 소스 코드
    웹사이트 자료
    프론트엔드 템플릿