spring boot+mybatis 多数据源切换(实例讲解)

由于公司业务划分了多个数据库,开发一个项目会同事调用多个库,经过学习我们采用了注解+aop的方式实现的

1.首先定义一个注解类

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface TargetDataSource {
 String value();//此处接收的是数据源的名称
}

2.然后建一个配置类,这个在项目启动时会加载数据源,一开始采用了HikariCP,查资料说是最快性能最好的,然后又发现了阿里的druid,这个功能比较全面,而且性能也还可以,最主要他还有监控功能,具体实现看如下代码

package com.example.demo.datasource;

import com.alibaba.druid.pool.DruidDataSource;
import com.alibaba.druid.support.http.StatViewServlet;
import com.alibaba.druid.support.http.WebStatFilter;
import com.example.demo.datasource.DynamicDataSource;
import com.zaxxer.hikari.HikariConfig;
import com.zaxxer.hikari.HikariDataSource;
import lombok.extern.slf4j.Slf4j;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
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.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.transaction.PlatformTransactionManager;
import org.w3c.dom.NodeList;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;

import javax.servlet.annotation.WebInitParam;
import javax.servlet.annotation.WebServlet;
import javax.sql.DataSource;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;
import java.io.File;
import com.alibaba.druid.support.http.StatViewServlet;
/**
 * Author: wangchao
 * Version:
 * Date:  2017/9/11
 * Description:数据源配置
 * Modification History:
 * Date    Author    Version   Description
 * --------------------------------------------------------------
 * Why & What is modified:
 */

@Configuration
@EnableScheduling
public class DataSourceConfig {

 /*@Autowired
 private DBProperties properties;*/
 @Value("${datasource.filePath}")
 private String filePath;//数据源配置

 @Bean(name = "dataSource")
 public DataSource dataSource() {
  //按照目标数据源名称和目标数据源对象的映射存放在Map中
  Map<Object, Object> targetDataSources = new HashMap<>();
  //查找xml数据连接字符串
  targetDataSources=getdataMap(filePath);
  //动态获取DBProperties类申明的属性
  /*Field[] fields=properties.getClass().getDeclaredFields();
  for(int i=0;i<fields.length;i++)
  {
   targetDataSources.put(fields[i].getName(), getFieldValueByName(fields[i].getName(),properties));
  }*/
  //采用是想AbstractRoutingDataSource的对象包装多数据源
  DynamicDataSource dataSource = new DynamicDataSource();
  dataSource.setTargetDataSources(targetDataSources);
  //设置默认的数据源,当拿不到数据源时,使用此配置
  //dataSource.setDefaultTargetDataSource(properties.getUzaiTravel());
  return dataSource;
 }

 @Bean
 public PlatformTransactionManager txManager() {
  return new DataSourceTransactionManager(dataSource());
 }

 /**
 *获取数据源集合
 */

 private Map<Object, Object> getdataMap(String fiePath)
 {

  try {
   Map<Object, Object> targetDataSources = new HashMap<>();
   File xmlFile = new File(fiePath);

   DocumentBuilderFactory builderFactory = DocumentBuilderFactory.newInstance();

   DocumentBuilder builder = builderFactory.newDocumentBuilder();

   Document doc = builder.parse(xmlFile);

   doc.getDocumentElement().normalize();

   System.out.println("Root element: " + doc.getDocumentElement().getNodeName());

   NodeList nList = doc.getElementsByTagName("db");
   for(int i = 0 ; i<nList.getLength();i++) {

    Node node = nList.item(i);
    Element ele = (Element)node;

    /*HikariConfig config = new HikariConfig();
    config.setDriverClassName(ele.getElementsByTagName("driver-class").item(0).getTextContent());
    config.setJdbcUrl(ele.getElementsByTagName("jdbc-url").item(0).getTextContent());
    config.setUsername(ele.getElementsByTagName("username").item(0).getTextContent());
    config.setPassword(ele.getElementsByTagName("password").item(0).getTextContent());
    //config.addDataSourceProperty("password", ele.getElementsByTagName("password").item(0).getTextContent());
    HikariDataSource dataSource = new HikariDataSource(config);*/

    DruidDataSource dataSource = new DruidDataSource();
    dataSource.setDriverClassName(ele.getElementsByTagName("driver-class").item(0).getTextContent());
    dataSource.setUsername(ele.getElementsByTagName("username").item(0).getTextContent());
    dataSource.setPassword(ele.getElementsByTagName("password").item(0).getTextContent());
    dataSource.setUrl(ele.getElementsByTagName("jdbc-url").item(0).getTextContent());
    dataSource.setInitialSize(5);
    dataSource.setMinIdle(1);
    dataSource.setMaxActive(10);// 启用监控统计功能
    dataSource.setFilters("stat");//设置是否显示sql语句
    targetDataSources.put(ele.getElementsByTagName("databasename").item(0).getTextContent(), dataSource);
   }
   return targetDataSources;
  }
  catch (Exception ex)
  {
   return null;
  }

 }
 //访问的ip
 @Value("${druid.IP}")
 private String IP;
 //登录名
 @Value("${druid.druidLgoinName}")
 private String druidLgoinName;
 //密码
 @Value("${druid.druidLgoinPassword}")
 private String druidLgoinPassword;

 @Bean
 public ServletRegistrationBean DruidStatViewServle() {
  //org.springframework.boot.context.embedded.ServletRegistrationBean提供类的进行注册.
  ServletRegistrationBean servletRegistrationBean = new ServletRegistrationBean(new StatViewServlet(), "/druid/*");
  //添加初始化参数:initParams

  //白名单:
  servletRegistrationBean.addInitParameter("allow",IP);
  //IP黑名单 (存在共同时,deny优先于allow) : 如果满足deny的话提示:Sorry, you are not permitted to view this page.
  // servletRegistrationBean.addInitParameter("deny", "192.168.1.73");
  //登录查看信息的账号密码.
  servletRegistrationBean.addInitParameter("loginUsername",druidLgoinName);
  servletRegistrationBean.addInitParameter("loginPassword",druidLgoinPassword);
  //是否能够重置数据.
  servletRegistrationBean.addInitParameter("resetEnable","false");
  return servletRegistrationBean;
 }

 /**

  * 注册一个:filterRegistrationBean

  * @return

 */
 @Bean
 public FilterRegistrationBean druidStatFilter2(){
  FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean(new WebStatFilter());
  //添加过滤规则.
  filterRegistrationBean.addUrlPatterns("/*");
  //添加不需要忽略的格式信息.
  filterRegistrationBean.addInitParameter("exclusions","*.js,*.gif,*.jpg,*.png,*.css,*.ico,/druid/*");
  return filterRegistrationBean;
 }

}

3.动态数据源,从之前已加载的数据源中选取,DynamicDataSource和DynamicDataSourceHolder配合使用

public class DynamicDataSource extends AbstractRoutingDataSource{
 //数据源路由,此方用于产生要选取的数据源逻辑名称
 @Override
 protected Object determineCurrentLookupKey() {
  //从共享线程中获取数据源名称
  return DynamicDataSourceHolder.getDataSource();
 }
}

public class DynamicDataSourceHolder {
 /**
  * 本地线程共享对象
  */
 private static final ThreadLocal<String> THREAD_LOCAL = new ThreadLocal<>();

 public static void putDataSource(String name) {
  THREAD_LOCAL.set(name);
 }

 public static String getDataSource() {
  return THREAD_LOCAL.get();
 }

 public static void removeDataSource() {
  THREAD_LOCAL.remove();
 }
}

4.就是使用aop,在dao层切换数据源

@Component
@Aspect
public class DataSourceAspect {
 //切换放在mapper接口的方法上,所以这里要配置AOP切面的切入点
 @Pointcut("execution( * com.example.demo.dao.*.*(..))")
 public void dataSourcePointCut() {
 }

 @Before("dataSourcePointCut()")
 public void before(JoinPoint joinPoint) {
  Object target = joinPoint.getTarget();
  String method = joinPoint.getSignature().getName();
  Class<?>[] clazz = target.getClass().getInterfaces();
  Class<?>[] parameterTypes = ((MethodSignature) joinPoint.getSignature()).getMethod().getParameterTypes();
  try {
   Method m = clazz[0].getMethod(method, parameterTypes);
   //如果方法上存在切换数据源的注解,则根据注解内容进行数据源切换
   if (m != null && m.isAnnotationPresent(TargetDataSource.class)) {
    TargetDataSource data = m.getAnnotation(TargetDataSource.class);
    String dataSourceName = data.value();
    DynamicDataSourceHolder.putDataSource(dataSourceName);

   } else {

   }
  } catch (Exception e) {

  }
 }

 //执行完切面后,将线程共享中的数据源名称清空
 @After("dataSourcePointCut()")
 public void after(JoinPoint joinPoint){
  DynamicDataSourceHolder.removeDataSource();
 }
}

数据连接都配置在xml里面

xml路径在配置文件里面配置,这样适用读写分离和多个不同的数据源,而且多个项目可以共用这一个配置

最后引用注解,需要注意的是注解的数据库名称和xml里面databasename节点是一一对应的,可以随便自定义,比如读写是一个数据库名字,这时候就可以定义成pringtest_r表示读库

至此多数据源就配置完成,至于阿里的druid下次再分享,代码都贴出来,如果大家感觉还有哪些不足的地方,欢迎指正。

以上这篇spring boot+mybatis 多数据源切换(实例讲解)就是小编分享给大家的全部内容了,希望能给大家一个参考,也希望大家多多支持我们。

(0)

相关推荐

  • Spring与Mybatis相结合实现多数据源切换功能

    废话不多说,关键代码如下所示: 1. 代码: DbContextHolder public class DbContextHolder { //线程安全的ThreadLocal private static final ThreadLocal<String> contextHolder = new ThreadLocal<String>(); public static void setDbType(String dbType) { contextHolder.set(dbType

  • Spring Boot 动态数据源示例(多数据源自动切换)

    本文实现案例场景: 某系统除了需要从自己的主要数据库上读取和管理数据外,还有一部分业务涉及到其他多个数据库,要求可以在任何方法上可以灵活指定具体要操作的数据库. 为了在开发中以最简单的方法使用,本文基于注解和AOP的方法实现,在spring boot框架的项目中,添加本文实现的代码类后,只需要配置好数据源就可以直接通过注解使用,简单方便. 一配置二使用 1. 启动类注册动态数据源 2. 配置文件中配置多个数据源 3. 在需要的方法上使用注解指定数据源 1.在启动类添加 @Import({Dyna

  • 详解springboot+mybatis多数据源最简解决方案

    说起多数据源,一般都来解决那些问题呢,主从模式或者业务比较复杂需要连接不同的分库来支持业务.我们项目是后者的模式,网上找了很多,大都是根据jpa来做多数据源解决方案,要不就是老的spring多数据源解决方案,还有的是利用aop动态切换,感觉有点小复杂,其实我只是想找一个简单的多数据支持而已,折腾了两个小时整理出来,供大家参考. 废话不多说直接上代码吧 配置文件 pom包就不贴了比较简单该依赖的就依赖,主要是数据库这边的配置: mybatis.config-locations=classpath:

  • springboot + mybatis配置多数据源示例

    在实际开发中,我们一个项目可能会用到多个数据库,通常一个数据库对应一个数据源. 代码结构: 简要原理: 1)DatabaseType列出所有的数据源的key---key 2)DatabaseContextHolder是一个线程安全的DatabaseType容器,并提供了向其中设置和获取DatabaseType的方法 3)DynamicDataSource继承AbstractRoutingDataSource并重写其中的方法determineCurrentLookupKey(),在该方法中使用Da

  • Spring+MyBatis多数据源配置实现示例

    最近用到了MyBatis配置多数据源,原以为简单配置下就行了,实际操作后发现还是要费些事的,这里记录下,以作备忘 不多废话,直接上代码,后面会有简单的实现介绍 jdbc和log4j的配置 #定义输出格式 ConversionPattern=%d %-5p [%t] %c - %m%n log4j.rootLogger=DEBUG,Console log4j.logger.com.cnblogs.lzrabbit=DEBUG log4j.logger.org.springframework=ERR

  • spring boot+mybatis 多数据源切换(实例讲解)

    由于公司业务划分了多个数据库,开发一个项目会同事调用多个库,经过学习我们采用了注解+aop的方式实现的 1.首先定义一个注解类 @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.METHOD) public @interface TargetDataSource { String value();//此处接收的是数据源的名称 } 2.然后建一个配置类,这个在项目启动时会加载数据源,一开始采用了HikariCP,查资料说是最快性能最好的

  • Spring boot + mybatis + orcale实现步骤实例代码讲解

    接着上次的实现, 添加 mybatis 查询 orcale 数据库 第一步: 新建几个必须的包, 结果如下 第二步: 在service包下新建personService.java 根据名字查person方法接口 package com.example.first.service; import com.example.first.entity.Person; public interface personService { Person queryPersonByName(String name

  • spring boot + mybatis实现动态切换数据源实例代码

    前言 前几天有个需求,需要使用不同的数据源,例如某业务要用A数据源,另一个业务要用B数据源.我上网收集了一些资料整合了一下,虽然最后这个需求不了了之了,但是多数据源动态切换还是蛮好用的,所以记录一下,或许以后有用呢?或者自己感兴趣又想玩呢! 下面话不多说了,随着小编来一起看看详细的介绍吧 方法如下: 1.加个依赖 <dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybat

  • spring boot mybatis多数据源解决方案过程解析

    在我们的项目中不免会遇到需要在一个项目中使用多个数据源的问题,像我在得到一个任务将用户的聊天记录进行迁移的时候,就是用到了三个数据源,当时使用的AOP的编程方式根据访问的方法的不同进行动态的切换数据源,觉得性能不太好,先在又新用到了一种使用方式,觉得不错,记录下来. 介绍一下DEMO项目,使用的spring boot集成mybatis,mybatis查询数据库是基于注解形式查询的,目的查询两个数据库test1和test2的用户信息,并在控制台打印. 1.pom文件 <dependencies>

  • Spring Boot应用开发初探与实例讲解

    Spring Boot是由Pivotal团队提供的全新Spring开发框架,其设计目的是用来简化新Spring应用的初始搭建以及开发过程. 从它的名字可以看出,Spring Boot 的作用在于创建和启动新的基于 Spring 框架的项目. 它的目的是帮助开发人员很容易的创建出独立运行和产品级别的基于 Spring 框架的应用. 它包含的特性如下: 应用独立运行,对于Web应用直接嵌入应用服务器(Tomcat or Jetty) 根据项目的依赖(Maven or Gradle中定义的依赖)自动配

  • 详解spring boot mybatis全注解化

    本文重点给大家介绍spring boot mybatis 注解化的实例代码,具体内容大家参考下本文: pom.xml <!-- 引入mybatis --> <dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> <version>1.3.0</version

  • 详解Spring Boot + Mybatis 实现动态数据源

    动态数据源 在很多具体应用场景的时候,我们需要用到动态数据源的情况,比如多租户的场景,系统登录时需要根据用户信息切换到用户对应的数据库.又比如业务A要访问A数据库,业务B要访问B数据库等,都可以使用动态数据源方案进行解决.接下来,我们就来讲解如何实现动态数据源,以及在过程中剖析动态数据源背后的实现原理. 实现案例 本教程案例基于 Spring Boot + Mybatis + MySQL 实现. 数据库设计 首先需要安装好MySQL数据库,新建数据库 example,创建example表,用来测

  • Spring Boot + Mybatis 实现动态数据源案例分析

    动态数据源 在很多具体应用场景的时候,我们需要用到动态数据源的情况,比如多租户的场景,系统登录时需要根据用户信息切换到用户对应的数据库.又比如业务A要访问A数据库,业务B要访问B数据库等,都可以使用动态数据源方案进行解决.接下来,我们就来讲解如何实现动态数据源,以及在过程中剖析动态数据源背后的实现原理. 实现案例 本教程案例基于 Spring Boot + Mybatis + MySQL 实现. 数据库设计 首先需要安装好MySQL数据库,新建数据库 master,slave,分别创建用户表,用

  • Spring Boot + Mybatis多数据源和动态数据源配置方法

    网上的文章基本上都是只有多数据源或只有动态数据源,而最近的项目需要同时使用两种方式,记录一下配置方法供大家参考. 应用场景 项目需要同时连接两个不同的数据库A, B,并且它们都为主从架构,一台写库,多台读库. 多数据源 首先要将spring boot自带的DataSourceAutoConfiguration禁掉,因为它会读取application.properties文件的spring.datasource.*属性并自动配置单数据源.在@SpringBootApplication注解中添加ex

  • Spring AOP如何实现注解式的Mybatis多数据源切换详解

    一.为什么要使用多数据源切换? 多数据源切换是为了满足什么业务场景?正常情况下,一个微服务或者说一个WEB项目,在使用Mybatis作为数据库链接和操作框架的情况下通常只需要构建一个系统库,在该系统库创建业务表来满足需求,当然也有分为测试库和正式库dev/prod,不过这俩库的切换是使用配置文件进行切分的,在项目启动时或者打成maven JAR包指定environment-dev.properties或者environment-prod.properties. 那么当程序运行过程中,比如一个co

随机推荐