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 mysql;
    //sql server配置
	private Map sqlserver;
    //sql server配置
	private Map hrsqlserver;

	public Map getMysql() {
		return mysql;
	}

	public void setMysql(Map mysql) {
		this.mysql = mysql;
	}

	public Map getSqlserver() {
		return sqlserver;
	}

	public void setSqlserver(Map sqlserver) {
		this.sqlserver = sqlserver;
	}

	public Map getHrsqlserver() {
		return hrsqlserver;
	}

	public void setHrsqlserver(Map 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 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 getClassFields(Class clazz){
		List 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 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 map = new HashMap<>();

		List fields = DataSourceUtil.getClassFields(DataSourceBean.class);
		int i = 0;
		for (String field:fields){
			Map config = null;
			try {
				config = (Map) 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();
		}
	}
}

目录结构

Spring boot+Mybatis 使用DruidDataSource 以注解方式动态整合mysql,sqlserver多数据源_第1张图片

使用方式 在service方法上写入注解

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

你可能感兴趣的:(spring,java)