动态数据源

Springboot整合Mybatis实现多数据源动态切换
原文链接:https://blog.csdn.net/honger_hua/article/details/102463401?utm_medium=distribute.pc_relevant.none-task-blog-baidujs_title-0&spm=1001.2101.3001.4242

1.pom文件配置
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.0.6.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.xin</groupId>
    <artifactId>tinaye</artifactId>
    <version>1.0.0</version>
    <name>deviceInformation</name>
    <description>Demo project for Spring Boot</description>
    <properties>
        <java.version>1.8</java.version>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
            <exclusions>
                <exclusion>
                    <artifactId>spring-boot-autoconfigure</artifactId>
                    <groupId>org.springframework.boot</groupId>
                </exclusion>
            </exclusions>
        </dependency>
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>1.2.51</version>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>${mysql.version}</version>
        </dependency>
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid-spring-boot-starter</artifactId>
            <version>1.1.14</version>
        </dependency>
        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>2.0.1</version>
            <exclusions>
                <exclusion>
                    <artifactId>mybatis-spring</artifactId>
                    <groupId>org.mybatis</groupId>
                </exclusion>
            </exclusions>
        </dependency>
        <dependency>
            <groupId>tk.mybatis</groupId>
            <artifactId>mapper-spring-boot-starter</artifactId>
            <version>2.0.0</version>
        </dependency>
        <dependency>
            <groupId>com.github.pagehelper</groupId>
            <artifactId>pagehelper-spring-boot-starter</artifactId>
            <version>1.2.10</version>
            <exclusions>
                <exclusion>
                    <artifactId>spring-boot-autoconfigure</artifactId>
                    <groupId>org.springframework.boot</groupId>
                </exclusion>
            </exclusions>
        </dependency>

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
        </dependency>

        <dependency>
            <groupId>commons-httpclient</groupId>
            <artifactId>commons-httpclient</artifactId>
            <version>3.1</version>
        </dependency>

        <dependency>
            <groupId>org.apache.httpcomponents</groupId>
            <artifactId>httpclient</artifactId>
            <version>4.5.1</version>
        </dependency>

        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-lang3</artifactId>
            <version>3.7</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-autoconfigure</artifactId>
            <version>2.0.6.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjtools</artifactId>
            <version>1.8.13</version>
        </dependency>
        <!-- https://mvnrepository.com/artifact/org.springframework/org.springframework.asm -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>org.springframework.asm</artifactId>
            <version>3.1.4.RELEASE</version>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>

            <plugin>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.7.0</version>
                <configuration>
                    <source>1.8</source>
                    <target>1.8</target>
                </configuration>
            </plugin>

            <plugin>
                <artifactId>maven-assembly-plugin</artifactId>
                <configuration>
                    <descriptorRefs>
                        <descriptorRef>jar-with-dependencies</descriptorRef>
                    </descriptorRefs>
                    <archive>
                        <manifest>
                            <mainClass>com.xin.tianye.util.DecryptUtil</mainClass>
                        </manifest>
                    </archive>
                </configuration>
                <executions>
                    <execution>
                        <id>make-assembly</id>
                        <phase>package</phase>
                        <goals>
                            <goal>single</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>

</project>
2.application.yml文件
spring:
  datasource:
    jzys:
      type: com.alibaba.druid.pool.DruidDataSource
      driver-class-name: com.mysql.jdbc.Driver
      jdbc-url: *****
      username: ***
      password: ***
    sxxatest:
      type: com.alibaba.druid.pool.DruidDataSource
      driver-class-name: com.mysql.jdbc.Driver
      jdbc-url: *****
      username: ***
      password: ***
2.application.properties文件
spring.application.name=springboot-datasource
server.port=8081
## mysql database
spring.datasource.local.url=****
spring.datasource.local.username=****
spring.datasource.local.password=****
spring.datasource.local.driver-class-name=com.mysql.jdbc.Driver
## oracle database
spring.datasource.oracle.url=****
spring.datasource.oracle.username=****
spring.datasource.oracle.password****
spring.datasource.oracle.driver-class-name=****
3.数据源配置类 MultipleDataSourceConfig .java
import java.util.HashMap;

import javax.sql.DataSource;

import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.SqlSessionTemplate;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.jdbc.DataSourceBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.transaction.PlatformTransactionManager;

@Configuration
public class MultipleDataSourceConfig {

    @Bean(name = "jzysDatasource")
    @Qualifier(value = "jzysDatasource")
    @ConfigurationProperties(prefix = "spring.datasource.jzys")
    public DataSource localDatasource() {
        return org.springframework.boot.jdbc.DataSourceBuilder.create().build();
    }

    @Bean(name = "sxxaDatasource")
    @Qualifier(value = "sxxaDatasource")
    @ConfigurationProperties(prefix = "spring.datasource.sxxatest")
    public DataSource oracleDatasource() {
        return DataSourceBuilder.create().build();
    }

    @Bean(name = "sqlSessionFactory")
    // 表示这个数据源是默认数据源
    // @Qualifier表示查找Spring容器中名字为test1DataSource的对象
    public SqlSessionFactory sqlSessionFactory()
            throws Exception {
        SqlSessionFactoryBean bean = new SqlSessionFactoryBean();
        bean.setDataSource(dynamicDataSource());
        bean.setMapperLocations(
                // 设置mybatis的xml所在位置
                new PathMatchingResourcePatternResolver().getResources("classpath:*/*.xml"));
        return bean.getObject();
    }
    @Bean("sqlSessionTemplate")
    // 表示这个数据源是默认数据源
    public SqlSessionTemplate sqlsessiontemplate(
            @Qualifier("sqlSessionFactory") SqlSessionFactory sessionfactory) {
        return new SqlSessionTemplate(sessionfactory);
    }

    @Primary
    @Bean(name = "dynamicDataSource")
    public DataSource dynamicDataSource() {
        DynamicDataSource dynamicDataSource = new DynamicDataSource();
        //配置默认数据源
        dynamicDataSource.setDefaultTargetDataSource(localDatasource());
        //配置多数据源
        HashMap<Object, Object> dataSourceMap = new HashMap<>();
        dataSourceMap.put(ContextConst.DataSourceType.PRIMARY.name(), localDatasource());
        dataSourceMap.put(ContextConst.DataSourceType.LOCAL.name(), oracleDatasource());
        dynamicDataSource.setTargetDataSources(dataSourceMap); // 该方法是AbstractRoutingDataSource的方法
        return dynamicDataSource;
    }

    @Bean
    public PlatformTransactionManager bfTransactionManager() {
        return new DataSourceTransactionManager(dynamicDataSource());
    }

//	    @Bean
//	    public PlatformTransactionManager bfscrmTransactionManager(@Qualifier("oracleDatasource")DataSource oracleDatasource) {
//	     return new DataSourceTransactionManager(oracleDatasource);
//	    }

}
4. 数据源路由实现类 DynamicDataSource.java
import org.slf4j.*;
import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;

/**
 * 数据源路由实现类
 */
public class DynamicDataSource extends AbstractRoutingDataSource {

    private static final Logger logger = LoggerFactory.getLogger(DynamicDataSource.class);

    @Override
    protected Object determineCurrentLookupKey() {
        String dataSource = DataSourceContextHolder.getDataSource();
        if (dataSource == null) {
            logger.info("当前数据源为[primary]");
        } else {
            logger.info("当前数据源为{}", dataSource);
        }
        return dataSource;
    }

}
5.自定义切换数据源的注解
import java.lang.annotation.*;

/**
 * 切换数据源的注解
 */
@Target({ElementType.TYPE,ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface DataSource {
    ContextConst.DataSourceType value() default ContextConst.DataSourceType.PRIMARY;
}
6.数据源枚举类
/**
 * 上下文常量
 */
public interface ContextConst {

    /**
     * 数据源枚举
     */
    enum DataSourceType {
        PRIMARY, LOCAL, PROD, TEST
    }
}
7.定义切换数据源的切面,为注解服务
import java.lang.reflect.Method;

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.core.annotation.Order;
import org.springframework.stereotype.Component;

/**
 * 切换数据源的切面
 */
@Component
@Aspect
@Order(1) //这是关键,要让该切面调用先于AbstractRoutingDataSource的determineCurrentLookupKey()
public class DynamicDataSourceAspect {

    @Before("execution(* com.xin.tianye.*.service..*.*(..))")
    public void before(JoinPoint point) {
        try {
            DataSource annotationOfClass = point.getTarget().getClass().getAnnotation(DataSource.class);
            String methodName = point.getSignature().getName();
            Class[] parameterTypes = ((MethodSignature) point.getSignature()).getParameterTypes();
            Method method = point.getTarget().getClass().getMethod(methodName, parameterTypes);
            DataSource methodAnnotation = method.getAnnotation(DataSource.class);
            methodAnnotation = methodAnnotation == null ? annotationOfClass : methodAnnotation;
            ContextConst.DataSourceType dataSourceType = methodAnnotation != null
                    && methodAnnotation.value() != null ? methodAnnotation.value() : ContextConst.DataSourceType.PRIMARY;

            DataSourceContextHolder.setDataSource(dataSourceType.name());
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        }
    }

    @After("execution(* com.xin.tianye.*.service..*.*(..))")
    public void after(JoinPoint point) {
        DataSourceContextHolder.clearDataSource();
    }
}

8.修改启动类

@SpringBootApplication(exclude = {DataSourceAutoConfiguration.class})

9.使用

使用
在方法上通过注解@DataSource指定该方法所用的数据源,如果没有使用注解指定则使用默认数据源

package com.lb.api.service;
 
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
 
import com.lb.api.config.ContextConst;
import com.lb.api.config.DataSource;
import com.lb.api.dao.LivesubscriptionMapper;
import com.lb.api.dao.TOperHisMapper;
@Service
public class DataSourceService {
	
	@Autowired
	private LivesubscriptionMapper livesubscriptionMapper;
	
	@Autowired
	private TOperHisMapper tOperHisMapper;
	
	  @DataSource(ContextConst.DataSourceType.LOCAL) // 指定该方法使用prod数据源
	  public void test01() {
 		DataSourceContextHolder.setDataSource("PRIMARY");//可手动注入
		  System.out.println(livesubscriptionMapper.selectByExample().size());
	  }
	  
	  @DataSource(ContextConst.DataSourceType.PRIMARY) // 指定该方法使用prod数据源
	  public void test02() {
		  System.out.println(tOperHisMapper.selectByExample().size());
	  }
	
 
}
avatar

取消