Spring boot+Mybatis 使用DruidDataSource 以註解方式動態整合mysql,sqlserver多數據源

配置文件

#多源數據庫配置
datasource:
  mysql:
      type: com.alibaba.druid.pool.DruidDataSource
      driverClassName: com.mysql.jdbc.Driver
      url: jdbc:mysql://x.x.x.x:3306/table?useUnicode=true&useSSL=false&characterEncoding=utf8
      username: root
      password: root
      initialSize: 1
      minIdle: 3
      maxActive: 20
      maxWait: 60000
      timeBetweenEvictionRunsMillis: 60000
      minEvictableIdleTimeMillis: 30000
      validationQuery: select 'x'
      testWhileIdle: true
      testOnBorrow: false
      testOnReturn: false
      poolPreparedStatements: true
      maxPoolPreparedStatementPerConnectionSize: 20
      # 配置監控統計攔截的filters,去掉後監控界面sql無法統計,'wall'用於防火牆
      filters: stat,wall,slf4j
      # 通過connectProperties屬性來打開mergeSql功能;慢SQL記錄
      connectionProperties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=5000
      # 合併多個DruidDataSource的監控數據
      #useGlobalDataSourceStat: true
  sqlserver:
      url: jdbc:sqlserver://x.x.x.x:1433;databasename=cwbase001
      driverClassName: com.microsoft.sqlserver.jdbc.SQLServerDriver
      username: root    
      password: root
  hrsqlserver:
      url: jdbc:sqlserver://x.x.x.x:1433;databasename=HR
      driverClassName: com.microsoft.sqlserver.jdbc.SQLServerDriver
      username: root
      password: root

首先要將spring boot自帶的DataSourceAutoConfiguration禁掉

@SpringBootApplication(exclude = {
        DataSourceAutoConfiguration.class
})
public class Application {
    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
        System.out.println("######## 啓動成功 ########");
    }
}

枚舉類,多數據源枚舉

public enum DataSourceType {
	Mysql("mysql"),
	SQLServer("sqlserver"),
	HrSQLServer("hrsqlserver");

	private String name;

	DataSourceType(String name) {
		this.name = name;
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}
}

數據庫連接配置實體類,用戶存儲數據相關配置。

import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;

import java.util.Map;

/**
 * 數據源實體類
 */
@Component
@ConfigurationProperties(prefix = "datasource")
public class DataSourceBean {
    
    //mysql 配置
	private Map<String,String> mysql;
    //sql server配置
	private Map<String,String> sqlserver;
    //sql server配置
	private Map<String,String> hrsqlserver;

	public Map<String, String> getMysql() {
		return mysql;
	}

	public void setMysql(Map<String, String> mysql) {
		this.mysql = mysql;
	}

	public Map<String, String> getSqlserver() {
		return sqlserver;
	}

	public void setSqlserver(Map<String, String> sqlserver) {
		this.sqlserver = sqlserver;
	}

	public Map<String, String> getHrsqlserver() {
		return hrsqlserver;
	}

	public void setHrsqlserver(Map<String, String> hrsqlserver) {
		this.hrsqlserver = hrsqlserver;
	}
}

@MyDataSource 註解,在service層中使用,AOP中截獲設置

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
public @interface MyDataSource {
	DataSourceType value() default DataSourceType.Mysql;
}

JdbcContextHolder 上下文處理


public class JdbcContextHolder {

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

	public static void putDataSource(String name){
		local.set(name);
	}

	public static String getDataSource(){
		return local.get();
	}
}

DynamicDataSource 實現AOP動態切換的關鍵


import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;

/**
 * AbstractRoutingDataSource實現類DynamicDataSource
 * 實現AOP動態切換的關鍵
 */
public class DynamicDataSource extends AbstractRoutingDataSource {
	@Override
	protected Object determineCurrentLookupKey() {
		String dbName = JdbcContextHolder.getDataSource();
		if (dbName == null ){
			dbName =  DataSourceType.Mysql.getName();
		}
		logger.debug("數據源爲:"+dbName);
		return dbName;
	}
}

數據源處理 utils

import com.alibaba.druid.pool.DruidDataSource;

import javax.sql.DataSource;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

/**
 * 數據源工具
 */
public class DataSourceUtil {

	/**
	 * 獲取指定類的成員變量
	 * @param clazz
	 * @return 成員變量名的List
	 */
	public static List<String> getClassFields(Class clazz){
		List<String> list = new ArrayList<>();
		Field[] fields = clazz.getDeclaredFields();
		for (Field field : fields){
			list.add(field.getName());
		}
		return list;
	}

	/**
	 * 依據成員變量獲取值
	 * @param fieldName 變量名
	 * @param o 已注入的實體
	 * @return Object
	 * @throws Exception 拋出異常
	 */
	public static Object getFieldValueByName(String fieldName, Object o) throws Exception{
		String firstLetter = fieldName.substring(0, 1).toUpperCase();
		String getter = "get" + firstLetter + fieldName.substring(1);
		Method method = o.getClass().getMethod(getter, new Class[] {});
		Object value = method.invoke(o, new Object[] {});
		return value;
	}

	/**
	 * 依據數據配置 獲取datasource 對象
	 * @param params Map 數據配置
	 * @return 返回datasource
	 * @throws SQLException 拋出Sql 異常
	 */
	public static DataSource getDataSource(Map<String,String> params) throws SQLException {
		DruidDataSource datasource = new DruidDataSource();
		datasource.setUrl(params.get("url"));
		datasource.setUsername(params.get("username"));
		datasource.setPassword(params.get("password"));
		datasource.setDriverClassName(params.get("driverClassName"));
		if (params.containsKey("initialSize")) {
			datasource.setInitialSize(Integer.parseInt(params.get("initialSize")));
		}
		if (params.containsKey("minIdle")) {
			datasource.setMinIdle(Integer.parseInt(params.get("minIdle")));
		}
		if (params.containsKey("maxActive")) {
			datasource.setMaxActive(Integer.parseInt(params.get("maxActive")));
		}
		if (params.containsKey("maxWait")){
			datasource.setMaxWait(Long.parseLong(params.get("maxWait")));
		}
		if (params.containsKey("timeBetweenEvictionRunsMillis")){
			datasource.setTimeBetweenEvictionRunsMillis(Long.parseLong(params.get("timeBetweenEvictionRunsMillis")));
		}
		if (params.containsKey("minEvictableIdleTimeMillis")){
			datasource.setMinEvictableIdleTimeMillis(Long.parseLong(params.get("minEvictableIdleTimeMillis")));
		}
		if (params.containsKey("validationQuery")){
			datasource.setValidationQuery(params.get("validationQuery"));
		}
		if (params.containsKey("testWhileIdle")){
			datasource.setTestWhileIdle(Boolean.parseBoolean(params.get("testWhileIdle")));
		}
		if (params.containsKey("testOnBorrow")){
			datasource.setTestOnBorrow(Boolean.parseBoolean(params.get("testOnBorrow")));
		}
		if (params.containsKey("testOnReturn")){
			datasource.setTestOnBorrow(Boolean.parseBoolean(params.get("testOnReturn")));
		}
		if (params.containsKey("poolPreparedStatements")){
			datasource.setPoolPreparedStatements(Boolean.parseBoolean(params.get("poolPreparedStatements")));
		}
		if (params.containsKey("maxPoolPreparedStatementPerConnectionSize")){
			datasource.setMaxPoolPreparedStatementPerConnectionSize(
					Integer.parseInt(params.get("maxPoolPreparedStatementPerConnectionSize")));
		}
		if (params.containsKey("filters")){
			datasource.setFilters(params.get("filters"));
		}
		if (params.containsKey("connectionProperties")){
			datasource.setConnectionProperties(params.get("connectionProperties"));
		}
		return datasource;
	}
}

數據源配置,整合Druid

import com.alibaba.druid.support.http.StatViewServlet;
import com.alibaba.druid.support.http.WebStatFilter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.boot.web.servlet.ServletRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;

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

/**
 * 數據庫配置
 */
@SuppressWarnings("AlibabaRemoveCommentedCode")
@Configuration
public class DataSourceConfig {

	private Logger logger = LoggerFactory.getLogger(this.getClass());

	@Autowired
	private DataSourceBean dataSourceBean;

	@Bean(name = "dynamicDataSource")
	@Primary  //優先使用,多數據源
	public DataSource dataSource(){
		DynamicDataSource dynamicDataSource = new DynamicDataSource();
		//配置多個數據源
		Map<Object,Object> map = new HashMap<>();

		List<String> fields = DataSourceUtil.getClassFields(DataSourceBean.class);
		int i = 0;
		for (String field:fields){
			Map<String,String> config = null;
			try {
				config = (Map<String, String>) DataSourceUtil.getFieldValueByName(field,dataSourceBean);
			} catch (Exception e) {
				e.printStackTrace();
			}
			if (config == null){
				logger.error("數據源配置失敗:"+field);
				continue;
			}
			try {
				DataSource dataSource = DataSourceUtil.getDataSource(config);
				if (i == 0){
					logger.debug("設置默認數據源:"+field);
					dynamicDataSource.setDefaultTargetDataSource(dataSource);
				}
				map.put(field,DataSourceUtil.getDataSource(config));
				logger.debug("鏈接數據庫:"+field);
				i++;
			} catch (SQLException e) {
				logger.error("druid configuration initialization filter", e);
			}
		}
		logger.debug("共配置了"+i+"個數據源");
		dynamicDataSource.setTargetDataSources(map);
		return dynamicDataSource;
	}

	@Bean(name="druidServlet")
	public ServletRegistrationBean druidServlet() {
		ServletRegistrationBean reg = new ServletRegistrationBean();
		reg.setServlet(new StatViewServlet());
		reg.addUrlMappings("/druid/*");
		reg.addInitParameter("allow", ""); //白名單
		return reg;
	}

	@Bean(name = "filterRegistrationBean")
	public FilterRegistrationBean filterRegistrationBean() {
		FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean();
		filterRegistrationBean.setFilter(new WebStatFilter());
		filterRegistrationBean.addUrlPatterns("/*");
		filterRegistrationBean.addInitParameter("exclusions", "*.js,*.gif,*.jpg,*.png,*.css,*.ico,/druid/*");
		filterRegistrationBean.addInitParameter("profileEnable", "true");
		filterRegistrationBean.addInitParameter("principalCookieName","USER_COOKIE");
		filterRegistrationBean.addInitParameter("principalSessionName","USER_SESSION");
		filterRegistrationBean.addInitParameter("DruidWebStatFilter","/*");
		return filterRegistrationBean;
	}
}

DataSourceAspect Aop依據上下文進行賦值

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;

import javax.xml.crypto.Data;
import java.lang.reflect.Method;

/**
 * AOP根據註解給上下文賦值
 */
@Aspect
@Order(3)
@Component
public class DataSourceAspect {
	private Logger logger = LoggerFactory.getLogger(this.getClass());

	//切點
	@Pointcut("execution(* com.xxx.*.service..*(..)))")
	public void aspect(){
		System.out.println("aspect");
	}

	@Before("aspect()")
	private void before(JoinPoint joinPoint){
		Object target = joinPoint.getTarget();
		String method = joinPoint.getSignature().getName();
		Class<?> classz = target.getClass();
		Class<?>[] parameterTypes = ((MethodSignature) joinPoint.getSignature()).getMethod().getParameterTypes();
		try {
			Method m = classz.getMethod(method,parameterTypes);
			if (m != null && m.isAnnotationPresent(MyDataSource.class)){
				MyDataSource data = m.getAnnotation(MyDataSource.class);
				JdbcContextHolder.putDataSource(data.value().getName());
				logger.debug("===============上下文賦值完成:"+data.value().getName());
			}else{
				JdbcContextHolder.putDataSource(DataSourceType.Mysql.getName());
				logger.debug("===============使用默認數據源:"+DataSourceType.Mysql.getName());
			}
		}catch (Exception e){
			e.printStackTrace();
		}
	}
}

目錄結構


使用方式 在service方法上寫入註解

	@Override
	@MyDataSource(DataSourceType.Mysql)
	//@MyDataSource(DataSourceType.HrSQLServer)
    //#@MyDataSource(DataSourceType.SQLServer)
	public int count(HelpDO helpDO, Map<String, Object> map) {
		return helpDao.count(getQuery(helpDO,map));
	}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章