Spring boot+Mybatis 使用DruidDataSource 以註解方式動態整合mysql,sqlserver多資料來源
阿新 • • 發佈:2019-01-07
配置檔案
#多源資料庫配置
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));
}