文档章节

SpringBoot + Mybatis 配置多数据源(Srping boot 二)

小海bug
 小海bug
发布于 2018/12/16 23:12
字数 1132
阅读 37
收藏 10

前置条件,你已经配置好spring boot+mybatis,可以参考之前的博客

实现逻辑通过注解+aop切面编程来动态更新datasource

第一步,配置多个DataSource

server:
  port: 8080
freezing:
    datasource:
        name: freezing
        url: jdbc:mysql://123.57.XXX.XXX:3636/freezingdb?characterEncoding=utf8&useSSL=false&serverTimezone=UTC
        username: freezing
        password: Zgjkj2015
        # 使用druid数据源
        type: com.alibaba.druid.pool.DruidDataSource
        driver-class-name: com.mysql.jdbc.Driver
        
ucenter:
   datasource:
        name: ucenter
        url: jdbc:mysql://123.57..XXX.XXX:3636/ucenter?characterEncoding=utf8&useSSL=false&serverTimezone=UTC
        username: ucenter
        password: Zgjkj2015
        # 使用druid数据源
        type: com.alibaba.druid.pool.DruidDataSource
        driver-class-name: com.mysql.jdbc.Driver
       

## 该配置节点为独立的节点,有很多同学容易将这个配置放在spring的节点下,导致配置无法被识别
mybatis:
  mapper-locations: classpath:mapping/*.xml  #注意:一定要对应mapper映射xml文件的所在路径
  type-aliases-package: com.hmkx.demo.model  # 注意:对应实体类的路径

第二步,定义数据源上下文管理类

public class DynamicDataSourceContextHolder {

    private Logger logger = Logger.getLogger(DynamicDataSourceContextHolder.class);
    //存放当前线程使用的数据源类型信息
    private static final ThreadLocal<String> contextHolder = new ThreadLocal<String>();
    //存放数据源id
    public static List<String> dataSourceIds = new ArrayList<String>();

    //设置数据源
    public static void setDataSourceType(String dataSourceType) {
        contextHolder.set(dataSourceType);
    }

    //获取数据源
    public static String getDataSourceType() {
        return contextHolder.get();
    }

    //清除数据源
    public static void clearDataSourceType() {
        contextHolder.remove();
    }

    //判断当前数据源是否存在
    public static boolean isContainsDataSource(String dataSourceId) {
        return dataSourceIds.contains(dataSourceId);
    }
}

 

第三步,通过AbstractRoutingDataSource 继承修改这个类来动态修改dataType

public class DynamicDataSource extends AbstractRoutingDataSource {
    @Override
    protected Object determineCurrentLookupKey() {
        return DynamicDataSourceContextHolder.getDataSourceType();
    }
}

第四步,DataSource注册

public class DynamicDataSourceRegister implements ImportBeanDefinitionRegistrar, EnvironmentAware {

    private Logger logger = Logger.getLogger(DynamicDataSourceRegister.class);

    //指定数据源类型,我这里用的druid(springboot2.0默认数据源是hikari如何想使用其他数据源可以自己配置)
//    private static final String DATASOURCE_TYPE_DEFAULT = "com.zaxxer.hikari.HikariDataSource";
    private static final String DATASOURCE_TYPE_DEFAULT = "com.alibaba.druid.pool.DruidDataSource";
    //默认数据源
    private DataSource defaultDataSource;
    //用户自定义数据源
    private Map<String, DataSource> slaveDataSources = new HashMap<>();

    @Override
    public void setEnvironment(Environment environment) {
        initDefaultDataSource(environment);
        initslaveDataSources(environment);
    }

    private void initDefaultDataSource(Environment env) {
        // 读取主数据源
        Map<String, Object> dsMap = new HashMap<>();
        dsMap.put("driver", env.getProperty("freezing.datasource.driver-class-name"));
        dsMap.put("url", env.getProperty("freezing.datasource.url"));
        dsMap.put("username", env.getProperty("freezing.datasource.username"));
        dsMap.put("password", env.getProperty("freezing.datasource.password"));
        logger.info(dsMap);
        defaultDataSource = buildDataSource(dsMap);
    }


    private void initslaveDataSources(Environment env) {
        // 读取配置文件获取更多数据源
       /* String dsPrefixs = env.getProperty("slave.datasource.names");
        for (String dsPrefix : dsPrefixs.split(",")) {
            // 多个数据源
            Map<String, Object> dsMap = new HashMap<>();
            dsMap.put("driver", env.getProperty("slave.datasource." + dsPrefix + ".driver"));
            dsMap.put("url", env.getProperty("slave.datasource." + dsPrefix + ".url"));
            dsMap.put("username", env.getProperty("slave.datasource." + dsPrefix + ".username"));
            dsMap.put("password", env.getProperty("slave.datasource." + dsPrefix + ".password"));
            DataSource ds = buildDataSource(dsMap);
            slaveDataSources.put(dsPrefix, ds);
        }*/

        Map<String, Object> dsMap = new HashMap<>();
        dsMap.put("driver", env.getProperty("ucenter.datasource.driver-class-name"));
        dsMap.put("url", env.getProperty("ucenter.datasource.url"));
        dsMap.put("username", env.getProperty("ucenter.datasource.username"));
        dsMap.put("password", env.getProperty("ucenter.datasource.password"));
        logger.info("ssssssssssssssssss"+dsMap);
        System.out.println("ssssssssssssssssss"+dsMap);
        DataSource ds = buildDataSource(dsMap);
        slaveDataSources.put("ucenter", ds);
    }

    @Override
    public void registerBeanDefinitions(AnnotationMetadata annotationMetadata, BeanDefinitionRegistry beanDefinitionRegistry) {
        Map<Object, Object> targetDataSources = new HashMap<Object, Object>();
        //添加默认数据源
        targetDataSources.put("dataSource", this.defaultDataSource);
        DynamicDataSourceContextHolder.dataSourceIds.add("dataSource");
        //添加其他数据源
        targetDataSources.putAll(slaveDataSources);
        for (String key : slaveDataSources.keySet()) {
            DynamicDataSourceContextHolder.dataSourceIds.add(key);
        }

        //创建DynamicDataSource
        GenericBeanDefinition beanDefinition = new GenericBeanDefinition();
        beanDefinition.setBeanClass(DynamicDataSource.class);
        beanDefinition.setSynthetic(true);
        MutablePropertyValues mpv = beanDefinition.getPropertyValues();
        mpv.addPropertyValue("defaultTargetDataSource", defaultDataSource);
        mpv.addPropertyValue("targetDataSources", targetDataSources);
        //注册 - BeanDefinitionRegistry
        beanDefinitionRegistry.registerBeanDefinition("dataSource", beanDefinition);

        logger.info("Dynamic DataSource Registry");
    }

    public DataSource buildDataSource(Map<String, Object> dataSourceMap) {
        try {
            Object type = dataSourceMap.get("type");
            if (type == null) {
                type = DATASOURCE_TYPE_DEFAULT;// 默认DataSource
            }
            Class<? extends DataSource> dataSourceType;
            dataSourceType = (Class<? extends DataSource>) Class.forName((String) type);
            String driverClassName = dataSourceMap.get("driver").toString();
            String url = dataSourceMap.get("url").toString();
            String username = dataSourceMap.get("username").toString();
            String password = dataSourceMap.get("password").toString();
            // 自定义DataSource配置
            DataSourceBuilder factory = DataSourceBuilder.create().driverClassName(driverClassName).url(url)
                    .username(username).password(password).type(dataSourceType);
            return factory.build();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
        return null;
    }
}

到这里完成了,数据库动态切换的前置工作,接下来就是什么时候去切换了

第五步,自定义标签

import java.lang.annotation.*;

/**
 * @Author hht
 * @Date 2018-12-11
 * @Description 作用于类、接口或者方法上
 */
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface TargetDataSource {
    String name();
}

第六步,监控标签的aop

/**
 * @Author hht
 * @Date 2018-12-11
 * @Description 动态数据源通知
 */
@Aspect
@Order(-1)//保证在@Transactional之前执行
@Component
public class DynamicDattaSourceAspect {

    private Logger logger = Logger.getLogger(DynamicDattaSourceAspect.class);

    //改变数据源
    @Before("@annotation(targetDataSource)")
    public void changeDataSource(JoinPoint joinPoint, TargetDataSource targetDataSource) {
        String dbid = targetDataSource.name();

        if (!DynamicDataSourceContextHolder.isContainsDataSource(dbid)) {
            //joinPoint.getSignature() :获取连接点的方法签名对象
            logger.error("数据源 " + dbid + " 不存在使用默认的数据源 -> " + joinPoint.getSignature());
        } else {
            logger.debug("使用数据源:" + dbid);
            DynamicDataSourceContextHolder.setDataSourceType(dbid);
        }
    }

    @After("@annotation(targetDataSource)")
    public void clearDataSource(JoinPoint joinPoint, TargetDataSource targetDataSource) {
        logger.debug("清除数据源 " + targetDataSource.name() + " !");
        DynamicDataSourceContextHolder.clearDataSourceType();
    }
}

这时候基本完工,最后一步就是修改启动配置类

package com.hmkx;

import com.hmkx.config.DynamicDataSourceRegister;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Import;

@SpringBootApplication
@MapperScan("com.hmkx.mapper")
@Import(DynamicDataSourceRegister.class)
public class SpringMybatisDemoApplication {

    public static void main(String[] args) {
        SpringApplication.run(SpringMybatisDemoApplication.class, args);
    }
}

完成了,下面是如何使用

@Service
public class ArticleServiceImpl  implements ArticleService {


    Logger log = LoggerFactory.getLogger(ArticleServiceImpl.class);

    @Resource
    private ArticlesMapper articlesMapper;


    @Override
    public Articles getArticleById(int id) {
        //使用默认数据库
        Articles articles = articlesMapper.selectByPrimaryKey(501112);
        log.info(articles.getTitle());
        return articles;
    }

    @Override
    @TargetDataSource(name="ucenter")
    public int updateByPrimaryKey(int id) {
        //使用ucenter数据库
        ArticlesWithBLOBs art = new ArticlesWithBLOBs();
        art.setId(501112);
        art.setTitle("测试");
        int res = articlesMapper.updateByPrimaryKeySelective(art);
        return res;
    }

}

记录完了,有错误或者更好的办法欢迎指代码下载地址  https://gitee.com/huhaitao/SpringBootMybatisForManyDatabase

© 著作权归作者所有

共有 人打赏支持
小海bug
粉丝 19
博文 64
码字总数 35922
作品 0
北京
架构师
私信 提问
Spring Boot学习笔记

多模块开发 [SpringBoot学习]-IDEA创建Gradle多Module结构的SpringBoot项目 RabbitMQ RabbitMQ 安装 linux安装RabbitMQ详细教程 Ubuntu 16.04 RabbitMq 安装与运行(安装篇) ubantu安装...

OSC_fly
2018/07/26
0
0
SpringBootBucket 2.0.4 发布,代号“傲娇的小二晶”

SpringBootBucket 自从1.0.0版本发布后就有好多人喜欢,目前码云上面star数量接近1.2k。上个月还收到了红薯签名的1000 star奖杯,这个我自己也觉得很惊讶。 由于SpringBoot 1.x官方将终止维护...

一刀
2018/09/16
1K
4
SpringBoot 学习二:操作数据库

本文将从以下几个方面介绍: 前言 配置数据源 SpringBoot 整合 Mybatis SpringBoot 整合 JdbcTemplate SpringBoot 整合 Redis 前言 在上篇文章 SpringBoot 学习一 中已经学习了 SpringBoot的...

tsmyk0715
2018/09/26
0
0
spring boot + jta + druid整合demo

地址: https://gitee.com/liuchangng/springboot-mybatis-jta 项目使用到的技术如下: spring boot mybatis jta (分布式事务) druid (多数据源) 地址: https://gitee.com/liuchangng/springbo......

liuchangng
2017/09/28
0
1
SpringBootBucket 1.0.0 发布,SprintBoot 全家桶

Spring Boot 现在已经成为Java 开发领域的一颗璀璨明珠,它本身是包容万象的,可以跟各种技术集成。 本项目对目前Web开发中常用的各个技术,通过和SpringBoot的集成,并且对各种技术通过“一...

一刀
2018/03/05
7.3K
17

没有更多内容

加载失败,请刷新页面

加载更多

SQL语句查询

1.1 排序 通过order by语句,可以将查询出的结果进行排序。放置在select语句的最后。 格式: SELECT * FROM 表名 ORDER BY 排序字段ASC|DESC; ASC 升序 (默认) DESC 降序 1.查询所有商品信息,...

stars永恒
34分钟前
2
0
IntelliJ IDEA 第一个 Scala 程序

IntelliJ 安装完成 Scala 插件后,你需要尝试使用 IntelliJ 来创建并且运行第一个程序。 通常这个程序只是简单的输出 Hello World。 创建一个新工程 在文件下面选择新建,然后选择创建工程。...

honeymose
38分钟前
2
0
mysql分表,分区的区别和联系

一,什么是mysql分表,分区 什么是分表,从表面意思上看呢,就是把一张表分成N多个小表,具体请看mysql分表的3种方法 什么是分区,分区呢就是把一张表的数据分成N多个区块,这些区块可以在同...

吴伟祥
40分钟前
1
0
csapp 习题 - 如何实现异或 exclusive-or

阅读 csapp v3 时,练习题 2.13 很有意思。练习题描述如下。 位设置是对于参数 mask 中每一个为 1 的位,那么参数 x 中相应位则被设置为 1 ;位清除是对于参数 mask 中每一个为 1 的位,那么...

ylme
昨天
5
0
Amino——产品迭代

兴趣部落产品迭代 时间 版本号 更新内容 备注 2019年1月2日 v3.1.1 支持定制部落首页的内容tab,酋长可以将精华、相册、分类添加到部落首页啦。 支持申请酋长,酋长可以直接推送优质话题,快...

铸剑为犁413
昨天
3
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部