一、引入依赖

  引入数据库连接池的依赖——druid和面向切面编程的依赖——aop,如下所示:

        <!-- druid -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>1.1.21</version>
</dependency>
<!-- aop -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>

二、创建数据库

  1、主数据库

  使用前文中已经创建的名为spring_boot_demo的数据库。

  spring_boot_demo中t_user数据如下:

  

  2、辅数据库

  数据库名为other_data,库中建立数据表t_user,表结构与spring_boot_demo中的t_user一致。

  实际项目中,大多是跨数据库的数据源切换,常用在同公司的多个不同系统中共用一个用户数据库,或者二次开发项目在原有数据库基础上做拓展,保留原有的数据连接。

  这里为了方便操作,就都在mysql下部署数据库并且使表结构一致,方便形成数据对比。

  other_data中插入数据如下:

  

三、修改数据库连接配置信息

  在application.yml中,修改数据库连接配置如下:

spring:
application:
name: spring-boot-demo
datasource:
type: com.alibaba.druid.pool.DruidDataSource
druid:
primary:
url: jdbc:mysql://localhost:3306/spring_boot_demo?useUnicode=true&characterEncoding=utf-8&useSSL=false&allowMultiQueries=true&serverTimezone=GMT%2B8
username: root
password: root
driverClassName: com.mysql.jdbc.Driver
second:
url: jdbc:mysql://localhost:3306/other_data?useUnicode=true&characterEncoding=utf-8&useSSL=false&allowMultiQueries=true&serverTimezone=GMT%2B8
username: root
password: root
driverClassName: com.mysql.jdbc.Driver

  

四、编写代码

  结构如下:

  

  1、枚举类DataSourceName:

  该类用来存放数据源的名称,定义两个数据源名称分别为PRIMARYSECOND

package com.example.demo.enums;

/**
* DataSource的name常量
* 便于切换
* @author 我命倾尘
*/
public enum DataSourceName { /**
* 主数据源 spring_boot_demo
*/
PRIMARY("PRIMARY"), /**
* 副数据源other_data
*/
SECOND("SECOND"); private String dataSourceName;
private DataSourceName(String dataSourceName){
this.dataSourceName=dataSourceName;
}
DataSourceName(){ }
public String getDataSourceName(){
return this.dataSourceName;
}
}

  2、配置类DynamicDataSourceConfig:

  通过@ConfigurationProperties读取配置文件中的数据源配置信息,并通过DruidDataSourceBuilder.create().build()创建数据连接,将多个数据源放入map,注入到IoC中:

package com.example.demo.config;

import com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceBuilder;
import com.example.demo.bean.DynamicDataSource;
import com.example.demo.enums.DataSourceName;
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 javax.sql.DataSource;
import java.util.HashMap;
import java.util.Map; /**
* @author 我命倾尘
*/
@Configuration
public class DynamicDataSourceConfig {
/**
* 创建DataSource Bean,将数据源配置从配置文件中读出
*/ @Bean
@ConfigurationProperties("spring.datasource.druid.primary")
public DataSource oneDataSource(){
return DruidDataSourceBuilder.create().build();
} @Bean
@ConfigurationProperties("spring.datasource.druid.second")
public DataSource twoDataSource(){
return DruidDataSourceBuilder.create().build();
} /**
* 将数据源放入到 这个map中,注入到IoC
*/
@Bean
@Primary
public DynamicDataSource dataSource(DataSource oneDataSource, DataSource twoDataSource){
Map<Object,Object> targetDataSources=new HashMap<>(2);
targetDataSources.put(DataSourceName.PRIMARY.getDataSourceName(),oneDataSource);
targetDataSources.put(DataSourceName.SECOND.getDataSourceName(),twoDataSource);
return new DynamicDataSource(oneDataSource,targetDataSources);
}
}

  3、动态数据源DynamicDataSource:

  通过继承AbstractRoutingDataSource类,在构造函数中调用父类的方法,将配置类中放入map的数据源集合定为备选数据源,将传来的oneDataSource作为默认数据源

package com.example.demo.bean;

import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;

import javax.sql.DataSource;
import java.util.Map; /**
* @author 我命倾尘
*/
public class DynamicDataSource extends AbstractRoutingDataSource {
private static final ThreadLocal<String> contextHolder=new ThreadLocal<>();
/**
* 配置DataSource
* 设置defaultTargetDataSource为主数据库
*/
public DynamicDataSource(DataSource defaultTargetDataSource, Map<Object,Object> targetDataSources){
super.setDefaultTargetDataSource(defaultTargetDataSource);
super.setTargetDataSources(targetDataSources);
super.afterPropertiesSet();
}
public static String getDataSource(){
return contextHolder.get();
}
public static void setDataSource(String dataSource){
contextHolder.set(dataSource);
}
public static void clearDataSource(){
contextHolder.remove();
} @Override
protected Object determineCurrentLookupKey() {
return getDataSource();
}
}

  setTargetDataSources设置备选的数据源集合,

  setDefaultTargetDataSource设置默认数据源,

  determineCurrentLookupKey决定当前数据源的对应的key。

  4、自定义注释类DataSource:

package com.example.demo.annotation;

import com.example.demo.enums.DataSourceName;

import java.lang.annotation.*;

/**
* @author 我命倾尘
*/
@Documented
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface DataSource {
DataSourceName value() default DataSourceName.PRIMARY;
}

  @Documented指定被标注的注解会包含在javadoc中,

  @Target指定注释可能出现在Java程序中的语法位置(ElementType.METHOD则说明注解可能出现在方法上),

  @Retention指定注释的保留时间(RetentionPolicy.RUNTIME则是在java文件编译成class类时也依旧保存该注释)。

  5、切面类DataSourceAspect:

package com.example.demo.aspect;

import com.example.demo.annotation.DataSource;
import com.example.demo.bean.DynamicDataSource;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.Ordered;
import org.springframework.stereotype.Component; import java.lang.reflect.Method; /**
* @author 我命倾尘
*/
@Aspect
@Component
public class DataSourceAspect implements Ordered {
private Logger log= LoggerFactory.getLogger(DataSourceAspect.class); /**
* 切点:所有配置DataSource注解的方法
*/
@Pointcut("@annotation(com.example.demo.annotation.DataSource)")
public void dataSourcePointCut(){ } @Around(value = "dataSourcePointCut()")
public Object around(ProceedingJoinPoint point) throws Throwable{
Object result;
MethodSignature signature=(MethodSignature)point.getSignature();
Method method=signature.getMethod();
DataSource ds=method.getAnnotation(DataSource.class);
/**
* 判断DataSource的值
* 获取当前方法应用的数据源
*/
DynamicDataSource.setDataSource(ds.value().getDataSourceName());
try{
result=point.proceed();
}finally {
DynamicDataSource.clearDataSource();
}
return result;
} @Override
public int getOrder() {
return 1;
}
}

  Spring框架有很多相同接口的实现类,提供了Ordered接口来处理相同接口实现类之间的优先级问题。

  通过环绕切面,对方法上的注释进行了检验,如果获取到有DataSource注释,则会进行数据源的切换,否则按默认数据源进行处理。

  6、引入配置类:

  既然手动配置了动态切换数据连接池,就要在入口类中排除自动引入,并引入数据源的配置类,以及开启AOP:

package com.example.demo;

import com.example.demo.config.DynamicDataSourceConfig;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
import org.springframework.context.annotation.Import; @MapperScan("com.example.demo.mapper")
@Import({DynamicDataSourceConfig.class})
@SpringBootApplication(exclude = {DataSourceAutoConfiguration.class})
@EnableAspectJAutoProxy
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
}

  通过@Import引入配置类,在@SpringBootApplication后进行自动引入的排除。

  @EnableAspectJAutoProxy用来开启AOP。

五、简单测试

  1、不使用注解:

  UserController中的方法如下:

  @RequestMapping("/user/age")
  public int getAgeOfUser(){
return userService.getAgeByUsername("springbootdemo");
  }

  所得到的结果如下:

  

  这个结果是从主数据源spring_boot_demo数据库的表中得到的数据。

  2、在方法前添加注解@DataSource(DataSourceName.SECOND):

  UserController中的方法如下:

  @RequestMapping("/user/age")
  @DataSource(DataSourceName.SECOND)
  public int getAgeOfUser(){
return userService.getAgeByUsername("springbootdemo");
  }

  结果如下:

  

  这个结果则是从辅数据源other_data中得到的数据。

  前言:SpringBoot框架:使用mybatis连接mysql数据库完成数据访问(二)

最新文章

  1. maven 下载 源码和javadoc 命令
  2. &lt;&lt;Vector Calculus&gt;&gt;笔记
  3. QPS计算方法
  4. [转]unicode,ansi,utf-8,unicode big endian的故事
  5. FastDFS配置说明
  6. ceph-deploy 在下面ceph-nodes执行的命令
  7. mktime性能问题
  8. wildcard
  9. ToggleButton --------- 按钮实现开关效果
  10. cf466C Number of Ways
  11. 使用Github+Hexo框架搭建部署自己的博客
  12. javap反汇编的使用
  13. CentOS7设置定时任务 每隔30分钟执行一次命令
  14. class与struct的区别
  15. mongoDB开发规范
  16. pgpool-II 的使用
  17. plupload.Uploader多文件上传
  18. HDU 1106 排序 (排序+处理字符串)
  19. python简单验证码
  20. 我的MBTI小测试

热门文章

  1. Spring Boot Jpa 多条件查询+排序+分页
  2. MySQL数据库中查询数据库表、字段总数量,查询数据总量
  3. AI and Neuroscience: A virtuous circle
  4. Ubuntu 磁盘满了处理方法。
  5. 理解传输层中UDP协议首部校验和以及校验和计算方法的Java实现
  6. 初始化文章分类的方法 下拉的layui框
  7. JAVA指令集——GETSTATIC
  8. JavaScript基础语法资料
  9. APICloud数据云3.0 -- 让后端业务更简单
  10. Spine学习一 -渲染组件