应用场景:
项目需要同时连接多个不同的数据库A, B,并且它们都需要读写操作
项目技术选项:
SpringBoot_ssm
数据库:MySQL 、SQL server
看网上有很多类似笔记,但有些繁琐,这里简单说明,记个笔记。
以此记录:
依赖
这里仅仅展示部份关键依赖
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.0.0</version>
</dependency>
<!-- 导入Mysql数据库链接jar包 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.30</version>
</dependency>
<!--SQL server数据库jar-->
<dependency>
<groupId>com.microsoft.sqlserver</groupId>
<artifactId>sqljdbc4</artifactId>
<version>4.0</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>1.1.10</version>
</dependency>
数据库配置
#====================数据源MySQL配置===================#
spring.datasource.mysql.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.mysql.url=jdbc\:mysql\://127.0.0.1\:3306/数据库名?useUnicode\=true&characterEncoding\=UTF-8
spring.datasource.mysql.username=账号
spring.datasource.mysql.password=密码
#====================SQL Server=======================#
spring.datasource.sqlserver.driver-class-name=com.microsoft.sqlserver.jdbc.SQLServerDriver
spring.datasource.sqlserver.url=jdbc:sqlserver://127.0.0.1:1433;DatabaseName=xinsong
spring.datasource.sqlserver.username=sa
spring.datasource.sqlserver.password=root
#====================druid连接池=======================#
# 使用druid连接池
spring.datasource.type=com.alibaba.druid.pool.DruidDataSource
spring.datasource.minIdle= 1
spring.datasource.maxActive=20
# 说明:这里省略其他诸多配置。。。。。
在SpringBoot中配置数据源(这里不采用spring boot自带的DataSourceAutoConfiguration)
1.创建DataSourceConfig类,主要用于配置所需要用到的数据库
@Configuration
public class DataSourceConfig {
//MySQL作为主数据源(默认数据源)
@Bean(name="mysql")
@Primary// 注意:这里需要该注解声明是默认数据源
@ConfigurationProperties(prefix="spring.datasource.mysql")
public DataSource mysqlDateSource(){
return DruidDataSourceBuilder.create().build();
}
@Bean(name="sqlserver")
@ConfigurationProperties(prefix="spring.datasource.sqlserver")
public DataSource sqlServerDateSource(){
return DruidDataSourceBuilder.create().build();
}
@Bean(name = "dynamicDataSource")
public DataSource dynamicDataSource() {
DynamicDataSource dynamicDataSource = new DynamicDataSource();
// 默认数据源
dynamicDataSource.setDefaultTargetDataSource(mysqlDateSource());
// 配置多数据源
Map<Object, Object> dataBaseMap = new HashMap<>(16);
dataBaseMap.put("sqlserver", sqlServerDateSource());
dataBaseMap.put("mysql", mysqlDateSource());
dynamicDataSource.setTargetDataSources(dataBaseMap);
return dynamicDataSource;
}
@Bean(name="sqlServerSqlSessionFactory")
public SqlSessionFactory sqlSessionFactory(@Qualifier("dynamicDataSource")DataSource dataSource) throws Exception {
SqlSessionFactoryBean bean = new SqlSessionFactoryBean();
bean.setDataSource(dataSource);
return bean.getObject();
}
@Bean(name="sqlServerTransactionManager")//事务管理@Transactional(TransactionManager="mysqlTransactionManager")
public PlatformTransactionManager platformTransactionManager(@Qualifier("dynamicDataSource")DataSource dataSource) {
return new DataSourceTransactionManager(dataSource);
}
@Bean(name="sqlServerSqlSessionTemplate")
public SqlSessionTemplate sqlSessionTemplate(@Qualifier("sqlServerSqlSessionFactory")SqlSessionFactory sqlSessionFactory) throws Exception {
return new SqlSessionTemplate(sqlSessionFactory);
}
2.duird配置
@Configuration
public class DruidConfig {
@Bean
@ConditionalOnMissingBean
public ServletRegistrationBean druidServlet() {
ServletRegistrationBean servletRegistrationBean = new ServletRegistrationBean(new StatViewServlet(), "/druid/*");
//白名单
servletRegistrationBean.addInitParameter("allow", "192.168.6.195");
//IP黑名单(存在共同时,deny优先于allow) : 如果满足deny的话提示:Sorry, you are not permitted to view this page.
servletRegistrationBean.addInitParameter("deny", "192.168.6.73");
//用于登陆的账号密码
servletRegistrationBean.addInitParameter("loginUsername", "admin");
servletRegistrationBean.addInitParameter("loginPassword", "admin");
//是否能重置数据
servletRegistrationBean.addInitParameter("resetEnable", "true");
return servletRegistrationBean;
}
/**
* @Date: 2019/5/29 20:51
* @Description: 注册filter信息,用于拦截
*/
@Bean
public FilterRegistrationBean<WebStatFilter> filterRegistrationBean() {
//创建过滤器
FilterRegistrationBean<WebStatFilter> filterRegistrationBean = new FilterRegistrationBean<>(new WebStatFilter());
//设置过滤器过滤路径
filterRegistrationBean.addUrlPatterns("/*");
//忽略过滤得形式
filterRegistrationBean.addInitParameter("exclusions", "*.js,*.gif,*.jpg,*.png,*.css,*.ico,/druid/*");
return filterRegistrationBean;
}
}
3.实现AOP动态切换,需要先创建一个注解:@DataSource
创建:
import java.lang.annotation.*;
/**
* @author ttllihao
* @description: AOP切换数据源注解,默认值mysql,即MySQL是默认主数据源
*/
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface DataSource {
String value() default "mysql";
}
4.动态数据源上下文.
/**
* @author ttllihao
* @description: 动态数据源上下文.
*/
@Component
public class DataSourceContextHolder {
/**
* 线程独立
*/
private static ThreadLocal<String> contextHolder = new ThreadLocal<String>();
public static final String DB_DEFAULT__MySQL = "mysql";
public static final String DB_TYPE_SQLServer = "sqlserver";
public static String getDataBaseType() {
return contextHolder.get();
}
public static void setDataBaseType(String dataBase) {
contextHolder.set(dataBase);
}
public static void clearDataBaseType() {
contextHolder.remove();
}
/**
* @author ttllihao
* @description: 获取数据源名
*/
public class DynamicDataSource extends AbstractRoutingDataSource {
private Logger logger = LogManager.getLogger("TestController");
@Override
protected Object determineCurrentLookupKey() {
logger.info("当前数据源:{}"+ DataSourceContextHolder.getDataBaseType());
return DataSourceContextHolder.getDataBaseType();
}
6.最重要的一步
**
* @author ttllihao
* @description: 动态切换数据源类
*/
@Aspect
@Component
@Order(-10)
public class DynamicDataSourceAspect {
private Logger logger = LogManager.getLogger("DynamicDataSourceAspect");
@Before("@annotation(DataSource)")
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.DB_DEFAULT__MySQL;
try {
// 得到访问的方法对象
Method method = className.getMethod(methodName, argClass);
// 判断是否存在@DS注解
if (method.isAnnotationPresent(DataSource.class)) {
DataSource annotation = method.getAnnotation(DataSource.class);
// 取出注解中的数据源名
dataSource = annotation.value();
}
} catch (Exception e) {
e.printStackTrace();
}
// 切换数据源
DataSourceContextHolder.setDataBaseType(dataSource);
}
@After("@annotation(DataSource)")
public void afterSwitchDS(JoinPoint point){
DataSourceContextHolder.clearDataBaseType();
}
}
使用:在impl层方法上面加入注解,默认(主)数据源可以不写注解,写注解也可以不填value值