环境:
springboot2
tomcat8
jdk1.8
驱动及加密:com.alibaba.druid
多数据源
1、数据源枚举定义
public interface DatasourceEnum {
String DATA_SOURCE = "dataSource"; //主数据源
String DATA_SOURCE_2 = "dataSource2"; //其他业务的数据源
String DATA_SOURCE_3 = "dataSource3"; //其他业务的数据源
}
2、定义数据源实体
import com.alibaba.druid.pool.DruidDataSource;
import lombok.Getter;
import lombok.Setter;
@Setter
@Getter
public class MutiDataSourceProperties {
private String dataSourceName;
private String url;
private String username;
private String password;
private String driverClassName;
private String validationQuery = "SELECT 'x'";
public void config(DruidDataSource dataSource) {
dataSource.setUrl(url);
dataSource.setUsername(username);
dataSource.setPassword(password);
dataSource.setDriverClassName(driverClassName);
dataSource.setValidationQuery(validationQuery);
}
}
3、自定义注解
import java.lang.annotation.*;
/**
* @author keeps
* @Title: DataSource
* @ProjectName aifaceboot
* @Description: 多数据源标识
*/
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface DataSource {
String name() default DatasourceEnum.DATA_SOURCE;
}
4、数据源基础配置类
import lombok.Getter;
import lombok.Setter;
/**
* @author keeps
* @Title: DataConnectionProperties
* @ProjectName aifaceboot
* @Description: 数据源基础配置类
*/
@Setter
@Getter
public class DataSourceSchool {
private String dataSourceName;
private String dialectType;
}
5、定义数据源帮助类,共享上下文线程
/**
* @author keeps
* @Title: DataSourceContextHolder
* @ProjectName aifaceboot
* @Description: 动态切换数据源datasource的上下文
*/
public class DataSourceContextHolder {
private static final ThreadLocal
/**
* 设置数据源类型
*
* @param dataSourceThread 数据库名,数据源类型
*/
public static void setDataSourceSchool(DataSourceSchool dataSourceThread) {
contextHolder.set(dataSourceThread);
}
public static void setDataSourceType(String dataSourceName) {
DataSourceSchool dataSourceThread = new DataSourceSchool();
String dialectType = DataSourceDialect.dataSourceNameDialect.get(dataSourceName);
dataSourceThread.setDialectType(dialectType);
dataSourceThread.setDataSourceName(dataSourceName);
contextHolder.set(dataSourceThread);
}
/**
* 获取数据源类型
*/
public static DataSourceSchool getDataSourceSchool() {
if(contextHolder.get()==null){
return new DataSourceSchool();
}
return contextHolder.get();
}
public static String getDialectType() {
DataSourceSchool dataSourceSchool = contextHolder.get();
if (dataSourceSchool!=null && dataSourceSchool.getDialectType() != null){
return dataSourceSchool.getDialectType();
}
return null;
}
public static String getSQLDialect(String sql, int offset,int limit) {
String dialectType = getDialectType();
if (dialectType == null){
return sql;
}
if (dialectType.toUpperCase().contains(Dialect.Type.MYSQL.name().toUpperCase())){
return new MysqlDialect().getLimitSql(sql, offset, limit);
} else if (dialectType.toUpperCase().contains(Dialect.Type.ORACLE.name().toUpperCase())) {
return new OracleDialect().getLimitSql(sql, offset, limit);
} else if (dialectType.toUpperCase().contains(Dialect.Type.SQLSERVER.name().toUpperCase())) {
return new SqlServerDialect().getLimitSql(sql, offset, limit);
}
return sql;
}
/**
* 清除数据源类型
*/
public static void clearDataSourceSchool() {
contextHolder.remove();
}
}
6、创建动态数据源路由
import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
/**
* @author keeps
* @Title: DynamicDataSource
* @ProjectName aifaceboot
* @Description: 动态数据源路由
*/
public class DynamicDataSource extends AbstractRoutingDataSource {
public DynamicDataSource() {
}
/*
* DynamicDataSourceContextHolder代码中使用setDataSourceType
* 设置当前的数据源,在路由类中使用getDataSourceType进行获取,
* 交给AbstractRoutingDataSource进行注入使用。
*/
@Override
protected Object determineCurrentLookupKey() {
return DataSourceContextHolder.getDataSourceSchool().getDataSourceName();
}
7、定义切面,环绕
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.*;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.Ordered;
import java.lang.reflect.Method;
/**
* @author keeps
* @Title: MultiSourceExAop
* @ProjectName aifaceboot
* @Description: 多数据源切换的aop
*/
@Slf4j
@Aspect
public class MultiSourceAop implements Ordered {
@Autowired
private MutiDataSourceProperties mutiDataSourceProperties;
@Pointcut("@annotation(com.newcap.xy.common.mutidatasource.annotion.DataSource)")
private void cut()
{
}
@Around("cut()")
public Object around(ProceedingJoinPoint point)throws Throwable{
//jdbcCoordinator.close();
Signature signature = point.getSignature();
MethodSignature methodSignature = null;
if (!(signature instanceof MethodSignature)) {
throw new IllegalArgumentException("该注解只能用于方法");
}
methodSignature = (MethodSignature)signature;
Object target = point.getTarget();
Method currentMethod = target.getClass().getMethod(methodSignature.getName(), methodSignature.getParameterTypes());
DataSource datasource = (DataSource)currentMethod.getAnnotation(DataSource.class);
DataSourceSchool dataSourceSchool = new DataSourceSchool();
String dialectType = DataSourceDialect.dataSourceNameDialect.get(datasource.name());
dataSourceSchool.setDataSourceName(datasource.name());
dataSourceSchool.setDialectType(dialectType);
DataSourceContextHolder.setDataSourceSchool(dataSourceSchool);
if (datasource != null) {
this.log.debug("设置数据源为:" + datasource.name());
} else {
/* String dialectType = DataSourceDialect.dataSourceNameDialect.get(datasource.name());
dataSourceSchool.setDataSourceName(datasource.name());
dataSourceSchool.setDialectType(dialectType);
DataSourceContextHolder.setDataSourceSchool(dataSourceSchool);*/
this.log.debug("设置数据源为:dataSourceCurrent");
}
try
{
return point.proceed();
} finally {
this.log.debug("清空数据源信息!");
DataSourceContextHolder.clearDataSourceSchool();
}
}
public int getOrder()
{
return -1;
}
}
8、植入springboot
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* @author keeps
* @Title: DataSourceAopConfig
* @Description: 初版生成 AOP
*/
@Configuration
public class DataSourceAopConfig {
/**
* 多数据源切换的aop
*/
@Bean
public MultiSourceAop multiSourceExAop() {
return new MultiSourceAop();
}
}
9、创建数据源分类map方便维护
import java.util.HashMap;
import java.util.Map;
/**
* @author keeps
* @Title: DataSourceD
* @ProjectName newcapboot
* @Description: 数据源分类
*/
public class DataSourceDialect {
public static Map
}
10、创建连接池优化实体
import com.alibaba.druid.pool.DruidDataSource;
import java.sql.SQLException;
/**
* @author keeps
* @Title: DruidProperties
* @ProjectName aifaceboot
* @Description: Druid Properties
*/
public class DruidProperties {
private String url;
private String username;
private String password;
private String driverClassName;
private Integer initialSize = 2;
private Integer minIdle = 1;
private Integer maxActive = 20;
private Integer maxWait = 60000;
private Integer timeBetweenEvictionRunsMillis = 60000;
private Integer minEvictableIdleTimeMillis = 300000;
private String validationQuery = " select 1 FROM DUAL ";
private Boolean testWhileIdle = true;
private Boolean testOnBorrow = false;
private Boolean testOnReturn = false;
private Boolean poolPreparedStatements = true;
private Integer maxPoolPreparedStatementPerConnectionSize = 20;
private String filters = "stat";
public void config(DruidDataSource dataSource) {
dataSource.setUrl(url);
dataSource.setUsername(username);
dataSource.setPassword(password);
dataSource.setDriverClassName(driverClassName);
dataSource.setInitialSize(initialSize); //定义初始连接数
dataSource.setMinIdle(minIdle); //最小空闲
dataSource.setMaxActive(maxActive); //定义最大连接数
dataSource.setMaxWait(maxWait); //最长等待时间
// 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒
dataSource.setTimeBetweenEvictionRunsMillis(timeBetweenEvictionRunsMillis);
// 配置一个连接在池中最小生存的时间,单位是毫秒
dataSource.setMinEvictableIdleTimeMillis(minEvictableIdleTimeMillis);
dataSource.setValidationQuery(validationQuery);
dataSource.setTestWhileIdle(testWhileIdle);
dataSource.setTestOnBorrow(testOnBorrow);
dataSource.setTestOnReturn(testOnReturn);
// 打开PSCache,并且指定每个连接上PSCache的大小
dataSource.setPoolPreparedStatements(poolPreparedStatements);
dataSource.setMaxPoolPreparedStatementPerConnectionSize(maxPoolPreparedStatementPerConnectionSize);
try {
dataSource.setFilters(filters);
} catch (SQLException e) {
e.printStackTrace();
}
}
public String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getDriverClassName() {
return driverClassName;
}
public void setDriverClassName(String driverClassName) {
this.driverClassName = driverClassName;
}
public Integer getInitialSize() {
return initialSize;
}
public void setInitialSize(Integer initialSize) {
this.initialSize = initialSize;
}
public Integer getMinIdle() {
return minIdle;
}
public void setMinIdle(Integer minIdle) {
this.minIdle = minIdle;
}
public Integer getMaxActive() {
return maxActive;
}
public void setMaxActive(Integer maxActive) {
this.maxActive = maxActive;
}
public Integer getMaxWait() {
return maxWait;
}
public void setMaxWait(Integer maxWait) {
this.maxWait = maxWait;
}
public Integer getTimeBetweenEvictionRunsMillis() {
return timeBetweenEvictionRunsMillis;
}
public void setTimeBetweenEvictionRunsMillis(Integer timeBetweenEvictionRunsMillis) {
this.timeBetweenEvictionRunsMillis = timeBetweenEvictionRunsMillis;
}
public Integer getMinEvictableIdleTimeMillis() {
return minEvictableIdleTimeMillis;
}
public void setMinEvictableIdleTimeMillis(Integer minEvictableIdleTimeMillis) {
this.minEvictableIdleTimeMillis = minEvictableIdleTimeMillis;
}
public String getValidationQuery() {
return validationQuery;
}
public void setValidationQuery(String validationQuery) {
this.validationQuery = validationQuery;
}
public Boolean getTestWhileIdle() {
return testWhileIdle;
}
public void setTestWhileIdle(Boolean testWhileIdle) {
this.testWhileIdle = testWhileIdle;
}
public Boolean getTestOnBorrow() {
return testOnBorrow;
}
public void setTestOnBorrow(Boolean testOnBorrow) {
this.testOnBorrow = testOnBorrow;
}
public Boolean getTestOnReturn() {
return testOnReturn;
}
public void setTestOnReturn(Boolean testOnReturn) {
this.testOnReturn = testOnReturn;
}
public Boolean getPoolPreparedStatements() {
return poolPreparedStatements;
}
public void setPoolPreparedStatements(Boolean poolPreparedStatements) {
this.poolPreparedStatements = poolPreparedStatements;
}
public Integer getMaxPoolPreparedStatementPerConnectionSize() {
return maxPoolPreparedStatementPerConnectionSize;
}
public void setMaxPoolPreparedStatementPerConnectionSize(Integer maxPoolPreparedStatementPerConnectionSize) {
this.maxPoolPreparedStatementPerConnectionSize = maxPoolPreparedStatementPerConnectionSize;
}
public String getFilters() {
return filters;
}
public void setFilters(String filters) {
this.filters = filters;
}
}
以上为多数据源所需AOP相关各个类。我是把上一篇文章的直接站过来了
/** * @author keeps * @Title: MutiDataSourceProperties * @ProjectName aifaceboot * @Description: 多数据源配置 */ @Setter @Getter public class MutiDataSourceProperties { private String dataSourceName; private String url; private String username; private String password; private String driverClassName; private String validationQuery = "SELECT 'x'"; public void config(DruidDataSource dataSource) { dataSource.setUrl(url); dataSource.setUsername(username); dataSource.setPassword(password); dataSource.setDriverClassName(driverClassName); dataSource.setValidationQuery(validationQuery); } }
import com.alibaba.druid.pool.DruidDataSource; import java.util.List; /** * @author keeps * @Title: MultiDataSourceExtraProperties * @ProjectName aifaceboot * @Description: 其它数据源列表 */ public class MultiDataSourceExtraProperties { private ListdataConnectionPropertiesList; public List getDataConnectionPropertiesList() { return dataConnectionPropertiesList; } public void setDataConnectionPropertiesList(List dataConnectionPropertiesList) { this.dataConnectionPropertiesList = dataConnectionPropertiesList; } public void config(DruidDataSource druidDataSource, MutiDataSourceProperties dataConnectionProperties) { druidDataSource.setUrl(dataConnectionProperties.getUrl()); druidDataSource.setUsername(dataConnectionProperties.getUsername()); druidDataSource.setPassword(dataConnectionProperties.getPassword()); druidDataSource.setDriverClassName(dataConnectionProperties.getDriverClassName()); druidDataSource.setValidationQuery(dataConnectionProperties.getValidationQuery()); } }
import com.alibaba.druid.pool.DruidDataSource; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; 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 org.springframework.transaction.annotation.EnableTransactionManagement; import java.sql.SQLException; import java.util.HashMap; /** * @author keeps * @Title: MultiDataSourceConfig * @ProjectName aifaceboot * @Description: 引入多数据源 */ @ConditionalOnProperty(prefix = "newcap.jndi-datasource", name = "open", havingValue = "false") @Configuration @EnableTransactionManagement(order = 2) @Slf4j public class MultiDataSourceConfig { /** * druid配置 */ @Primary @Bean(name = "primaryDataSource") @Qualifier("primaryDataSource") @ConfigurationProperties(prefix = "spring.datasource") public DruidProperties druidProperties() { DruidProperties druidProperties = new DruidProperties(); return druidProperties; } /** * 多数据源配置 扩展配置 */ @Bean @ConfigurationProperties(prefix = "newcap.multi-datasource") public MultiDataSourceExtraProperties multiDataSourceExtraProperties() { return new MultiDataSourceExtraProperties(); } /** * 多数据源配置 原配置【只是为兼容MultiSourceExAop】 */ @Bean public MutiDataSourceProperties mutiDataSourceProperties() { MutiDataSourceProperties mutiDataSourceProperties=new MutiDataSourceProperties(); return mutiDataSourceProperties; } /** * 单个数据源连接配置 */ @ConditionalOnProperty(prefix = "newcap.multi-datasource", name = "open", havingValue = "false") @Bean public DruidDataSource dataSource() { DruidProperties druidProperties = druidProperties(); DruidDataSource dataSource = new DruidDataSource(); druidProperties.config(dataSource); DataSourceDialect.dataSourceNameDialect.put(DatasourceEnum.DATA_SOURCE,druidProperties.getDriverClassName()); return dataSource; } /** * 多数据源连接池配置 */ @ConditionalOnProperty(prefix = "newcap.multi-datasource", name = "open", havingValue = "true") @Bean public DynamicDataSource dynamicDataSource() { HashMap
spring: jpa: #jpa 方言 database-platform: org.hibernate.dialect.MySQL5InnoDBDialect datasource: #主数据库连接 url: jdbc:mysql://IP:3306/aiface?useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai&useSSL=false username: root password: 123456 driver-class-name: com.mysql.cj.jdbc.Driver validation-query: select 1
spring: jpa: #jpa 无需修改 database-platform: org.hibernate.dialect.Oracle10gDialect datasource: url: jdbc:oracle:thin:@IP:1521:face1 username: facetest password: newcapec driver-class-name: oracle.jdbc.OracleDriver validation-query: select 1 FROM DUAL
spring:
profiles:
active: oracle
#druid连接池基础配置
datasource:
type: com.alibaba.druid.pool.DruidDataSource
maxActive: 400
initialSize: 50
maxWait: 60000
minIdle: 10
timeBetweenEvictionRunsMillis: 60000
minEvictableIdleTimeMillis: 300000
validationQuery: select 1
testWhileIdle: true
testOnBorrow: false
testOnReturn: false
maxOpenPreparedStatements: 100
# 打开PSCache,并且指定每个连接上PSCache的大小
poolPreparedStatements: true
maxPoolPreparedStatementPerConnectionSize: 20
# 配置监控统计拦截的filters,去掉后监控界面sql无法统计,'wall'用于防火墙
filters: stat,wall
# 通过connectProperties属性来打开mergeSql功能;慢SQL记录
connectionProperties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=5000
# 合并多个DruidDataSource的监控数据
useGlobalDataSourceStat: true
newcap:
multi-datasource:
open: false
dataConnectionPropertiesList:
- dataSourceName: dataSource2
dialect: oracle
url: jdbc:oracle:thin:@IP:1521:jwkq
username: ccense
password: ccense
driver-class-name: oracle.jdbc.OracleDriver
validation-query: select 1 FROM DUAL
Dao层方法上添加注解
@DataSource(name = DatasourceEnum.DATA_SOURCE)