Spring Boot + Mybatis多數據源和動態數據源配置

參考原創地址:https://blog.csdn.net/neosmith/article/details/61202084java

說明:使用動態數據源的初衷,是能在應用層作到讀寫分離,即在程序代碼中控制不一樣的查詢方法去鏈接不一樣的庫mysql

1.第一步 關閉spring boot的單數據源:spring

/**
 * 經過配置多數據源,實現動態數據源切換
 * 1.禁用掉spring boot自帶的數據源處理類DataSourceAutoConfiguration.class。
 * 2.在配置文件application.yml中配置鏈接多個數據操做
 */
@SpringBootApplication(exclude = {DataSourceAutoConfiguration.class})
@MapperScan("com.example.server.dao")
public class ServerApplication {

   public static void main(String[] args) {
      SpringApplication.run(ServerApplication.class, args);
   }

2,第二步 定義一個ContextHolder, 用於保存當前線程使用的數據源名sql

/**
 * 使用動態數據源的初衷,是能在應用層作到讀寫分離,即在程序代碼中控制不一樣的查詢方法去鏈接不一樣的庫。除了這種方法之外,
 * 數據庫中間件也是個不錯的選擇,它的優勢是數據庫集羣對應用來講只暴露爲單庫,不須要切換數據源的代碼邏輯。
 * 咱們經過自定義註解 + AOP的方式實現數據源動態切換。
 * 首先定義一個ContextHolder, 用於保存當前線程使用的數據源名
 * @author liuqiuping 
 * @date 2018-4-21
 */
public class DataSourceContextHolder {
    public static final Logger log = LoggerFactory.getLogger(DataSourceContextHolder.class);

    /**
     * 默認數據源
     */
    public static final String DEFAULT_DS = "Master";

    private static final ThreadLocal<String> contextHolder = new ThreadLocal<>();

    // 設置數據源名
    public static void setDB(String dbType) {
        log.debug("切換到{}數據源", dbType);
        contextHolder.set(dbType);
    }

    // 獲取數據源名
    public static String getDB() {
        return (contextHolder.get());
    }

    // 清除數據源名
    public static void clearDB() {
        contextHolder.remove();
    }
}

3 第三步 自定義一個javax.sql.DataSource接口的實現,這裏只須要繼承Spring爲咱們預先實現好的父類AbstractRoutingDataSource數據庫

package com.example.server.config;

import com.alibaba.druid.pool.DruidDataSource;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.bind.RelaxedPropertyResolver;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
import org.springframework.transaction.annotation.EnableTransactionManagement;

import javax.sql.DataSource;
import java.util.HashMap;
import java.util.Map;

/**
 * 動態數據源配置,
 *自定義一個javax.sql.DataSource接口的實現,這裏只須要繼承Spring爲咱們預先實現好的父類AbstractRoutingDataSource
 * @author liuqiuping
 * @date  2018-4-21
 */
public class DynamicDataSource extends AbstractRoutingDataSource {
    private static final Logger log = LoggerFactory.getLogger(DynamicDataSource.class);


    @Override
    protected Object determineCurrentLookupKey() {
        log.debug("數據源爲{}", DataSourceContextHolder.getDB());

        return DataSourceContextHolder.getDB();
    }

}

4 第四步 建立自定義sqlSessionFactoryapache

import com.alibaba.druid.pool.DruidDataSource;
import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.SqlSessionTemplate;
import org.mybatis.spring.annotation.MapperScan;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.autoconfigure.jdbc.DataSourceBuilder;
import org.springframework.boot.bind.RelaxedPropertyResolver;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;

import javax.annotation.Resource;
import javax.sql.DataSource;
import java.util.HashMap;
import java.util.Map;

/**
 * 建立自定義sqlSessionFactory,實現多個數據源動態注入sqlSessionFactory
 * @author liuqiuping
 * @date 2018-4-21
 */
@Configuration
@MapperScan(basePackages = {"com.example.server.dao"}, sqlSessionFactoryRef = "sqlSessionFactory")
public class MybatisDbAConfig {
    private static final Logger logger = LoggerFactory.getLogger(DynamicDataSource.class);

    @Bean(name = "Master")
    @ConfigurationProperties(prefix = "spring.datasource.master") // application.properteis中對應屬性的前綴
    public DataSource MasterDataSource() {
        logger.info("注入 druid MasterDataSource!!!");
//        DruidDataSource datasource = new DruidDataSource();
////        datasource.setUrl(propertyResolver.getProperty("url"));
////        datasource.setDriverClassName(propertyResolver.getProperty("driver-class-name"));
////        datasource.setUsername(propertyResolver.getProperty("username"));
////        datasource.setPassword(propertyResolver.getProperty("password"));
////        datasource.setInitialSize(Integer.valueOf(propertyResolver.getProperty("initialSize")));
////        datasource.setMinIdle(Integer.valueOf(propertyResolver.getProperty("minIdle")));
////        datasource.setMaxWait(Long.valueOf(propertyResolver.getProperty("maxWait")));
////        datasource.setMaxActive(Integer.valueOf(propertyResolver.getProperty("maxActive")));
////        datasource.setMinEvictableIdleTimeMillis(Long.valueOf(propertyResolver.getProperty("minEvictableIdleTimeMillis")));
////        return datasource;

        return DataSourceBuilder.create().build();
    }

    @Bean(name = "Cluster")
    @ConfigurationProperties(prefix = "spring.datasource.cluster") // application.properteis中對應屬性的前綴
    public DataSource clusterDataSource() {
        logger.info("注入 druid MasterDataSource!!!");
//        DruidDataSource datasource = new DruidDataSource();
//        datasource.setUrl(propertyResolver.getProperty("url"));
//        datasource.setDriverClassName(propertyResolver.getProperty("driver-class-name"));
//        datasource.setUsername(propertyResolver.getProperty("username"));
//        datasource.setPassword(propertyResolver.getProperty("password"));
//        datasource.setInitialSize(Integer.valueOf(propertyResolver.getProperty("initialSize")));
//        datasource.setMinIdle(Integer.valueOf(propertyResolver.getProperty("minIdle")));
//        datasource.setMaxWait(Long.valueOf(propertyResolver.getProperty("maxWait")));
//        datasource.setMaxActive(Integer.valueOf(propertyResolver.getProperty("maxActive")));
//        datasource.setMinEvictableIdleTimeMillis(Long.valueOf(propertyResolver.getProperty("minEvictableIdleTimeMillis")));
//        return datasource;
        return DataSourceBuilder.create().build();
    }

    @Bean(name="dynamicDataSource")
    @Primary    //優先使用,多數據源
    public DataSource dataSource() {
        DynamicDataSource dynamicDataSource = new DynamicDataSource();
        DataSource master = MasterDataSource();
        DataSource slave = clusterDataSource();
        //設置默認數據源
        dynamicDataSource.setDefaultTargetDataSource(master);
        //配置多數據源
        Map<Object,Object> map = new HashMap<>();
        map.put("Master", master);   //key須要跟ThreadLocal中的值對應
        map.put("Cluster", slave);
        dynamicDataSource.setTargetDataSources(map);
        return dynamicDataSource;
    }


}

 

5 第五步,編寫AOP切面,實現切換邏輯,根據具體方法調用不用的數據源session

package com.example.server.config;

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;

/**
 * 編寫AOP切面,實現切換邏輯:
 * @author liuqiuping
 * @date 2018-4-21
 */
@Aspect
@Component
public class DynamicDataSourceAspect {

    @Before("@annotation(DS)")
    public void beforeSwitchDS(JoinPoint point){

        //得到當前訪問的class
        Class<?> className = point.getTarget().getClass();

        //得到訪問的方法名
        String methodName = point.getSignature().getName();
        //獲得方法的參數的類型
        Class[] argClass = ((MethodSignature)point.getSignature()).getParameterTypes();
        String dataSource = DataSourceContextHolder.DEFAULT_DS;
        try {
            // 獲得訪問的方法對象
            Method method = className.getMethod(methodName, argClass);

            // 判斷是否存在@DS註解
            if (method.isAnnotationPresent(DS.class)) {
                DS annotation = method.getAnnotation(DS.class);
                // 取出註解中的數據源名
                dataSource = annotation.value();
            }
        } catch (Exception e) {
            e.printStackTrace();
        }

        // 切換數據源
        DataSourceContextHolder.setDB(dataSource);

    }


    @After("@annotation(DS)")
    public void afterSwitchDS(JoinPoint point){

        DataSourceContextHolder.clearDB();

    }
}

 

6 第六步 自定義DS註解mybatis

/**
 * 自定義DS註解
 * @author  liuqiuping
 * @date  2018-4-21
 * 
 */
@Retention(RetentionPolicy.RUNTIME)
@Target({
        ElementType.METHOD
})
public @interface DS {
    String value() default "master";
}

 到此完成多數據源的配置app

接下來配置兩個不一樣數據數據源的配置鏈接ide

spring:
  application:
    name: sso-server
##  選擇 數據源
  datasource:
    master:
      url: jdbc:mysql://127.0.0.1:3306/sso?&useUnicode=true&characterEncoding=utf8 
      username: root                                                             
      password: 123                                                               
      driver-class-name: com.mysql.jdbc.Driver
      initialSize: 5
      minIdle: 5
      maxActive: 20
      maxWait: 60000
      timeBetweenEvictionRunsMillis: 60000
      minEvictableIdleTimeMillis: 300000
    cluster:
      url: jdbc:mysql://127.0.0.1:3306/ssoCluster?&useUnicode=true&characterEncoding=utf8
      username: root                                                             
      password: 123                                                              
      driver-class-name: com.mysql.jdbc.Driver
      initialSize: 5
      minIdle: 5
      maxActive: 20
      maxWait: 60000
      timeBetweenEvictionRunsMillis: 60000
      minEvictableIdleTimeMillis: 300000

動態數據源測試

@DS("Master")
@Override
public User findByLoginName(String name) {
   User user = userMapper.findByLoginName(name);
   if(null!=user){
      user.setRoles(roleMapper.selectByUserId(user.getId()));
   }

   return user;
}

/**
 * 使用數據源2
 * @param loginName
 * @return
 */
@DS("Cluster")
@Override
public User findByLoginNameTest(String loginName) {
   return userMapper.findByLoginNameTest(loginName);
}

親測,調用不一樣的方法,執行不一樣的數據查詢成功。

相關文章
相關標籤/搜索