废话不多说直接上代码
pom文件的引入:记得一定要用阿里的镜像哦,不然下载速度会让你崩溃
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.gbicc</groupId>
<artifactId>demo</artifactId>
<version>1.0-SNAPSHOT</version>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.1.RELEASE</version>
<relativePath />
</parent>
<properties>
<mybatis-spring-boot>1.3.4</mybatis-spring-boot>
<mysql-connector>5.1.39</mysql-connector>
<java.version>1.8</java.version>
<oracle.version>11.2.0.3</oracle.version>
<druid.version>1.1.14</druid.version>
<mybatis.spring.boot.starter.version>1.3.2</mybatis.spring.boot.starter.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<!-- SpringBoot Web容器 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!--阿里数据库连接池 -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>${druid.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
<!-- MySQL 连接驱动依赖 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<!--oracle驱动-->
<dependency>
<groupId>com.oracle</groupId>
<artifactId>ojdbc6</artifactId>
<version>${oracle.version}</version>
</dependency>
<!-- SpringBoot Mybatis 依赖 -->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>${mybatis-spring-boot}</version>
</dependency>
<!--Spring框架基本的核心工具-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context-support</artifactId>
</dependency>
<!-- lombok依赖-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<!-- logback 依赖-->
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
</dependency>
<!--Slf4j 依赖-->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>jcl-over-slf4j</artifactId>
</dependency>
</dependencies>
<!--阿里云镜像插件下载-->
<pluginRepositories>
<pluginRepository>
<id>public</id>
<name>aliyun nexus</name>
<url>http://maven.aliyun.com/nexus/content/groups/public/</url>
<releases>
<enabled>true</enabled>
</releases>
<snapshots>
<enabled>false</enabled>
</snapshots>
</pluginRepository>
</pluginRepositories>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<fork>true</fork> <!-- 如果没有该配置,devtools不会生效 -->
</configuration>
</plugin>
</plugins>
</build>
</project>
我的配置使用的yml文件,当然application.properties也是可以的
server:
port: 80
spring:
application:
name: demo
datasource:
## test1 database
test1:
jdbc-url: jdbc:oracle:thin:@127.0.0.1:1521:zwsj
username: user_frame2
password: user_frame2
driverClassName: oracle.jdbc.OracleDriver
test2:
jdbc-url: jdbc:mysql://localhost:3306/ry-vue?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
username: root
password: root
driver-class-name: com.mysql.jdbc.Driver
#mybatis配置
mybatis:
# 搜索指定包别名
typeAliasesPackage: com.gbicc.demo.**.domain
# 配置mapper的扫描,找到所有的mapper.xml映射文件
mapperLocations: classpath:mybatis/**/*Mapper.xml
# 加载全局的配置文件
configLocation: classpath:mybatis/mybatis-config.xml
多数据源配置动态加载
import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.jdbc.DataSourceBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import javax.sql.DataSource;
import java.util.HashMap;
import java.util.Map;
@Configuration
@MapperScan(basePackages = "com.gbicc.demo.mapper",sqlSessionFactoryRef = "SqlSessionFactory")
public class DataSourceConfig {
//bean创建对象,交给spring管理
@Bean(name = "test1DataSource")
@Primary//指定那个为主数据源
@ConfigurationProperties(prefix = "spring.datasource.test1")//绑定配置文件中的属性到bean中
public DataSource getDateSource1(){
return DataSourceBuilder.create().build();
}
@Bean(name = "test2DataSource")
@ConfigurationProperties(prefix = "spring.datasource.test2")//读取的数据源前缀, 跟yml文件对应
public DataSource getDateSource2(){
return DataSourceBuilder.create().build();
}
@Bean(name = "dynamicDataSource")
public DynamicDataSource DataSource(@Qualifier("test1DataSource")DataSource test1datasource,
@Qualifier("test2DataSource")DataSource test2datasource ){
Map<Object, Object> targetDataSources = new HashMap<>();
targetDataSources.put(DataBaseType.TEST01,test1datasource);
targetDataSources.put(DataBaseType.TEST02,test2datasource);
DynamicDataSource dataSource=new DynamicDataSource();
dataSource.setTargetDataSources(targetDataSources);
dataSource.setDefaultTargetDataSource(test1datasource);
return dataSource;
}
@Bean(name = "SqlSessionFactory")
public SqlSessionFactory test1SqlSessionFactory(@Qualifier("dynamicDataSource") DataSource dynamicDataSource)
throws Exception{
SqlSessionFactoryBean bean=new SqlSessionFactoryBean();
bean.setDataSource(dynamicDataSource);
bean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources("classpath:mybatis/**/*Mapper.xml"));
return bean.getObject();
}
接下来配置一个最重要的一个类,
继承AbstractRoutingDataSource 抽象类,并重写determineCurrentLookupKey()方法,AbstractRoutingDataSource 类中determineCurrentLookupKey方法的key为数据源的名称,value为动态的数据源,determineCurrentLookupKey该类中还有一个非常重要的属性:private Map<Object, Object> targetDataSources;这个属性是会将你的数据源对象接受,然后设置给datasource类。
package com.gbicc.demo.config;
import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
import javax.sql.DataSource;
import java.util.Map;
/**\
* 定义一个动态数据源:
* 继承AbstractRoutingDataSource 抽象类,并重写determineCurrentLookupKey()方法
*/
public class DynamicDataSource extends AbstractRoutingDataSource {
@Override
protected Object determineCurrentLookupKey() {
DataBaseType dataBaseType = DataSourceType.getDataBaseType();
return dataBaseType;
}
}
创建DataSourceType类,当然更多人喜欢用DynamicDataSourceContextHolder进行命名,这里我用了DataSourceType,名称随意,对程序的运行没有影响的。该类的作用就是创建一个安全的线程,然后将数据源放置进去。
public class DataSourceType {
// 使用ThreadLocal保证线程安全
private static final ThreadLocal<DataBaseType> TYPE = new ThreadLocal<DataBaseType>();
// 往当前线程里设置数据源类型
public static void setDataBaseType(DataBaseType dataBaseType) {
if (dataBaseType == null) {
throw new NullPointerException();
}
System.err.println("[将当前数据源改为]:" + dataBaseType);
TYPE.set(dataBaseType);
}
// 获取数据源类型
public static DataBaseType getDataBaseType() {
DataBaseType dataBaseType = TYPE.get() == null ? DataBaseType.TEST01 : TYPE.get();
System.err.println("[获取当前数据源的类型为]:" + dataBaseType);
return dataBaseType;
}
// 清空数据类型
public static void clearDataBaseType() {
TYPE.remove();
}
}
创建一个枚举类。名称必须要和yml配置文件中的名字保持一直,否则无法加载
package com.gbicc.demo.config;
public enum DataBaseType {
TEST01, TEST02
}
ok,到此多数据就配置完成了,接下来要配置AOP进行扫描注解。
DataSource这个注解吧,我是这么写的,好几种配置方式。我给大家举例三种方式吧,看你的具体场景使用,然后具体的使用。
第一种配置方式:
package com.gbicc.demo.config;
import java.lang.annotation.*;
/**
* 切换数据注解 可以用于类或者方法级别 方法级别优先级 > 类级别
*/
@Target({ElementType.METHOD, ElementType.TYPE, ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface DataSource {
public DataBaseType value() default DataBaseType.TEST01; //该值即key值,默认使用默认数据库
}
配置AOP拦截,不然注解无法使用。
package com.gbicc.demo.config;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;
@Aspect
@Component
public class DynamicDataSourceAspect {
@Before("@annotation(dataSource)")//拦截我们的注解
public void changeDataSource(JoinPoint point, DataSource dataSource) throws Throwable {
DataBaseType value = dataSource.value();
if (value.equals(DataBaseType.TEST01)){
DataSourceType.setDataBaseType(DataBaseType.TEST01);
}else if (value.equals(DataBaseType.TEST02)){
DataSourceType.setDataBaseType(DataBaseType.TEST02);
}else {
DataSourceType.setDataBaseType(DataBaseType.TEST01);//默认使用主数据库
}
}
@After("@annotation(dataSource)") //清除数据源的配置
public void restoreDataSource(JoinPoint point, DataSource dataSource) {
DataSourceType.clearDataBaseType();
}
}
OK,这样就算配置完成了。
使用的时候这么用就行,这个注解可以作用在类中,也是以作用再方法中。
@DataSource(value = DataBaseType.TEST02) @DataSource(value = DataBaseType.数据库名称)
@DataSource(value = DataBaseType.TEST02)
public Integer test02hello(){
return demoMapper.hello();
}
@DataSource(value = DataBaseType.TEST01)
public Integer test01hello(){
return demoMapper.hello();
}
这是我的mapper层的代码:我的mysql和oracle都有一张sys_user表。
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.gbicc.demo.mapper.DemoMapper">
<select id="hello" resultType="Integer">
SELECT COUNT(*) FROM sys_user
</select>
</mapper>
第二种配置方式就比较简单粗暴了。
直接上代码:
package com.gbicc.demo.config;
import org.springframework.stereotype.Component;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
@Aspect
@Component
public class DataSourceAop {
@Before("execution(* com.gbicc.demo.service.Impl..*.test01*(..))")
public void setDataSource2test01() {
System.err.println("test01业务");
DataSourceType.setDataBaseType(DataSourceType.DataBaseType.TEST01);
}
@Before("execution(* com.gbicc.demo.service.Impl..*.test02*(..))")
public void setDataSource2test02() {
System.err.println("test02业务");
DataSourceType.setDataBaseType(DataSourceType.DataBaseType.TEST02);
}
}
直接AOP扫描包,不再使用自定义注解,当然这个时候记得将DynamicDataSourceAspect删除了。这个时候把,切换数据源就要写代码了,在你的方法需要切换数据源的地方要这么写:
public Integer test02hello(){
DataSourceType.setDataBaseType(DataBaseType.TEST01)
return demoMapper.hello();
}
我的上面AOP的扫描注解包是直接扫面test02开头的方法查询的从表数据库,test01开头的查询的主表库。
16:51:29.503 [main] INFO o.a.c.h.Http11NioProtocol - [log,173] - Starting ProtocolHandler ["http-nio-80"]
16:51:29.510 [main] INFO o.a.t.u.n.NioSelectorPool - [log,173] - Using a shared selector for servlet write/read
16:51:29.525 [main] INFO c.g.DemoApplication - [logStarted,59] - Started DemoApplication in 2.046 seconds (JVM running for 3.777)
16:51:34.978 [http-nio-80-exec-1] INFO o.a.c.c.C.[.[.[/] - [log,173] - Initializing Spring DispatcherServlet 'dispatcherServlet'
[将当前数据源改为]:TEST01
[获取当前数据源的类型为]:TEST01
16:51:35.022 [http-nio-80-exec-1] INFO c.z.h.HikariDataSource - [getConnection,110] - HikariPool-1 - Starting...
16:51:35.102 [http-nio-80-exec-1] INFO c.z.h.pool.PoolBase - [getAndSetNetworkTimeout,525] - HikariPool-1 - Driver does not support get/set network timeout for connections. (oracle.jdbc.driver.T4CConnection.getNetworkTimeout()I)
16:51:35.106 [http-nio-80-exec-1] INFO c.z.h.HikariDataSource - [getConnection,123] - HikariPool-1 - Start completed.
[将当前数据源改为]:TEST02
1数据源当前用户数为:29
[获取当前数据源的类型为]:TEST02
16:51:35.192 [http-nio-80-exec-1] INFO c.z.h.HikariDataSource - [getConnection,110] - HikariPool-2 - Starting...
16:51:35.193 [http-nio-80-exec-1] WARN c.z.h.u.DriverDataSource - [<init>,68] - Registered driver with driverClassName=com.mysql.jdbc.Driver was not found, trying direct instantiation.
16:51:35.840 [http-nio-80-exec-1] INFO c.z.h.HikariDataSource - [getConnection,123] - HikariPool-2 - Start completed.
2数据源当前用户数为:2
3数据源当前用户数为:29
[获取当前数据源的类型为]:TEST01
这是我的程序运行的结果。
第三种配置:DataSource类的代码换了换方式而已:
/**
* 切换数据注解 可以用于类或者方法级别 方法级别优先级 > 类级别
*/
@Target({ElementType.METHOD, ElementType.TYPE, ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface DataSource {
String value() default "test01"; //该值即key值,默认使用默认数据库
}
当然这个类的判断也要修改一下哦:DynamicDataSourceAspect
package com.gbicc.demo.config;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;
@Aspect
@Component
public class DynamicDataSourceAspect {
@Before("@annotation(dataSource)")//拦截我们的注解
public void changeDataSource(JoinPoint point, DataSource dataSource) throws Throwable {
String value = dataSource.value();
if (value.equals("test01")){
DataSourceType.setDataBaseType(DataBaseType.TEST01);
}else if (value.equals("test02")){
DataSourceType.setDataBaseType(DataBaseType.TEST02);
}else {
DataSourceType.setDataBaseType(DataBaseType.TEST01);//默认使用主数据库
}
}
@After("@annotation(dataSource)") //清除数据源的配置
public void restoreDataSource(JoinPoint point, DataSource dataSource) {
DataSourceType.clearDataBaseType();
}
}
如有什么疑问欢迎加入qq群来咨询我哦:35142661