这里用到了Spring Boot + Mybatis + DynamicDataSource配置动态双数据源,可以动态切换数据源实现数据库的读写分离。

添加依赖

加入Mybatis启动器,这里添加了Druid连接池、Oracle数据库驱动为例。

  1. <dependency>
  2. <groupId>org.mybatis.spring.boot</groupId>
  3. <artifactId>mybatis-spring-boot-starter</artifactId>
  4. </dependency>
  5. <dependency>
  6. <groupId>com.alibaba</groupId>
  7. <artifactId>druid</artifactId>
  8. </dependency>
  9. <dependency>
  10. <groupId>com.oracle</groupId>
  11. <artifactId>ojdbc6</artifactId>
  12. </dependency>

添加启动类

  1. @EnableMybatis
  2. @EnableTransactionManagement
  3. @SpringBootApplication(exclude = { DataSourceAutoConfiguration.class })
  4. public class Application {
  5. public static void main(String[] args) {
  6. SpringApplication.run(ServiceApplication.class, args);
  7. }
  8. }

@SpringBootApplication(exclude = { DataSourceAutoConfiguration.class }):

这里用到了双数据源,需要排除数据源的自动配置,如果只有一个数据源用Spring Boot的自动配置就行。

@EnableTransactionManagement:开启事务支持。

@EnableMybatis:开启Mybatis功能

  1. @Target(ElementType.TYPE)
  2. @Retention(RetentionPolicy.RUNTIME)
  3. @Documented
  4. @Import(MybatisConfig.class)
  5. public @interface EnableMybatis {
  6. }

Mybatis配置类

  1. @Configuration
  2. @MapperScan(basePackages = DSConfig.BASE_PACKAGES)
  3. public class MybatisConfig implements DSConfig {
  4. @Primary
  5. @Bean
  6. public DynamicDataSource dynamicDataSource(@Qualifier(DB_MASTER) DataSource master,
  7. @Qualifier(DB_SLAVE) DataSource slave) {
  8. Map<Object, Object> dsMap = new HashMap<>();
  9. dsMap.put(DB_MASTER, master);
  10. dsMap.put(DB_MASTER, slave);
  11. DynamicDataSource dynamicDataSource = new DynamicDataSource();
  12. dynamicDataSource.setDefaultTargetDataSource(master);
  13. dynamicDataSource.setTargetDataSources(dsMap);
  14. return dynamicDataSource;
  15. }
  16. @Bean
  17. public PlatformTransactionManager transactionManager(DynamicDataSource dynamicDataSource) {
  18. return new DataSourceTransactionManager(dynamicDataSource);
  19. }
  20. @Bean
  21. public SqlSessionFactory sqlSessionFactory(DynamicDataSource dynamicDataSource)
  22. throws Exception {
  23. SqlSessionFactoryBean sessionFactory = new SqlSessionFactoryBean();
  24. sessionFactory.setDataSource(dynamicDataSource);
  25. sessionFactory.setMapperLocations(
  26. ((ResourcePatternResolver) new PathMatchingResourcePatternResolver())
  27. .getResources(DSConfig.MAPPER_LOCATIONS));
  28. return sessionFactory.getObject();
  29. }
  30. }

DSConfig常量类:

  1. public interface DSConfig {
  2. String DS_PREFIX = "spring.datasource";
  3. String DS_ACTIVE = "active";
  4. String DB_MASTER = "db-master";
  5. String DB_SLAVE = "db-slave";
  6. String DRUID = "druid";
  7. String DRUID_MONITOR_USERNAME = "spring.druid.username";
  8. String DRUID_MONITOR_PASSWORD = "spring.druid.password";
  9. String DRUID_MONITOR_URL = "/druid/*";
  10. String DRUID_FILTER_EXCLUSIONS = "*.js,*.gif,*.jpg,*.png,*.css,*.ico,/druid/*";
  11. String DRUID_FILTER_URL = "/*";
  12. String BASE_PACKAGES = "com.example.**.mapper";
  13. String MAPPER_LOCATIONS = "mapper/**/*.xml";
  14. }

连接池配置类

Druid连接池的自动配置类:

  1. @Configuration
  2. @Import({ PropertiesConfig.class })
  3. @ConditionalOnClass(DruidDataSource.class)
  4. @ConditionalOnProperty(prefix = DSConfig.DS_PREFIX, value = DSConfig.DS_ACTIVE, havingValue = DSConfig.DRUID)
  5. public class DruidAutoConfig implements DSConfig {
  6. private Logger logger = LoggerUtils.getLogger(this);
  7. @Bean(name = DB_MASTER, initMethod = "init", destroyMethod = "close")
  8. public DataSource dataSourceMaster(DruidMasterProperties masterProperties) throws SQLException {
  9. logger.debug("master properties: {}", masterProperties.toString());
  10. DruidDataSource dds = new DruidDataSource();
  11. dds.setDriverClassName(masterProperties.getDriverClassName());
  12. dds.setUrl(masterProperties.getUrl());
  13. dds.setUsername(masterProperties.getUsername());
  14. dds.setPassword(masterProperties.getPassword());
  15. dds.setInitialSize(masterProperties.getInitialSize());
  16. dds.setMinIdle(masterProperties.getMinIdle());
  17. dds.setMaxActive(masterProperties.getMaxActive());
  18. dds.setMaxWait(masterProperties.getMaxWait());
  19. dds.setTimeBetweenEvictionRunsMillis(masterProperties.getTimeBetweenEvictionRunsMillis());
  20. dds.setMinEvictableIdleTimeMillis(masterProperties.getMinEvictableIdleTimeMillis());
  21. dds.setValidationQuery(masterProperties.getValidationQuery());
  22. dds.setTestOnBorrow(masterProperties.isTestOnBorrow());
  23. dds.setTestWhileIdle(masterProperties.isTestWhileIdle());
  24. dds.setTestOnReturn(masterProperties.isTestOnReturn());
  25. dds.setPoolPreparedStatements(masterProperties.isPoolPreparedStatements());
  26. dds.setMaxPoolPreparedStatementPerConnectionSize(
  27. masterProperties.getMaxPoolPreparedStatementPerConnectionSize());
  28. dds.setFilters(masterProperties.getFilters());
  29. return dds;
  30. }
  31. @Bean(name = DB_SLAVE, initMethod = "init", destroyMethod = "close")
  32. public DataSource dataSourceSlave(DruidSlaveProperties slaveProperties) throws SQLException {
  33. logger.debug("slave properties: {}", slaveProperties.toString());
  34. DruidDataSource dds = new DruidDataSource();
  35. dds.setDriverClassName(slaveProperties.getDriverClassName());
  36. dds.setUrl(slaveProperties.getUrl());
  37. dds.setUsername(slaveProperties.getUsername());
  38. dds.setPassword(slaveProperties.getPassword());
  39. dds.setInitialSize(slaveProperties.getInitialSize());
  40. dds.setMinIdle(slaveProperties.getMinIdle());
  41. dds.setMaxActive(slaveProperties.getMaxActive());
  42. dds.setMaxWait(slaveProperties.getMaxWait());
  43. dds.setTimeBetweenEvictionRunsMillis(slaveProperties.getTimeBetweenEvictionRunsMillis());
  44. dds.setMinEvictableIdleTimeMillis(slaveProperties.getMinEvictableIdleTimeMillis());
  45. dds.setValidationQuery(slaveProperties.getValidationQuery());
  46. dds.setTestOnBorrow(slaveProperties.isTestOnBorrow());
  47. dds.setTestWhileIdle(slaveProperties.isTestWhileIdle());
  48. dds.setTestOnReturn(slaveProperties.isTestOnReturn());
  49. dds.setPoolPreparedStatements(slaveProperties.isPoolPreparedStatements());
  50. dds.setMaxPoolPreparedStatementPerConnectionSize(
  51. slaveProperties.getMaxPoolPreparedStatementPerConnectionSize());
  52. dds.setFilters(slaveProperties.getFilters());
  53. return dds;
  54. }
  55. @Bean
  56. public ServletRegistrationBean druidServletRegistrationBean(EnvConfig env) {
  57. String username = env.getStringValue(DSConfig.DRUID_MONITOR_USERNAME);
  58. String password = env.getStringValue(DSConfig.DRUID_MONITOR_PASSWORD);
  59. return new ServletRegistrationBean(new DruidStatViewServlet(username, password),
  60. DSConfig.DRUID_MONITOR_URL);
  61. }
  62. @Bean
  63. public FilterRegistrationBean druidFilterRegistrationBean() {
  64. WebStatFilter wsf = new WebStatFilter();
  65. FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean();
  66. filterRegistrationBean.setFilter(wsf);
  67. filterRegistrationBean.setUrlPatterns(Arrays.asList(DSConfig.DRUID_FILTER_URL));
  68. filterRegistrationBean.setInitParameters(
  69. Collections.singletonMap("exclusions", DSConfig.DRUID_FILTER_EXCLUSIONS));
  70. return filterRegistrationBean;
  71. }
  72. }

根据类路径下有DruidDataSource这个类即有Druid这个jar包和配置文件中spring.datasource.active=druid才开启对Druid连接池的自动配置。

导入的配置文件:

  1. @Configuration
  2. @ComponentScan(basePackages = "com.example.common.config.properties")
  3. public class PropertiesConfig {
  4. }

DruidMasterProperties、DruidSlaveProperties属性文件读取的配置省略。

连接池监控配置类:

  1. public class DruidStatViewServlet extends StatViewServlet {
  2. private static final long serialVersionUID = 1L;
  3. private String username;
  4. private String password;
  5. @Override
  6. public String getInitParameter(String name) {
  7. if ("loginUsername".equals(name)) {
  8. return username;
  9. }
  10. if ("loginPassword".equals(name)) {
  11. return password;
  12. }
  13. return super.getInitParameter(name);
  14. }
  15. public DruidStatViewServlet(String username, String password) {
  16. super();
  17. this.username = username;
  18. this.password = password;
  19. }
  20. public String getUsername() {
  21. return username;
  22. }
  23. public String getPassword() {
  24. return password;
  25. }
  26. }

在META-INF/spring.factories中加入Druid自动配置映射:

  1. org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
  2. com.example.common.config.ds.DruidAutoConfig

切换数据源

切换数据源注解:

  1. @Target(ElementType.METHOD)
  2. @Retention(RetentionPolicy.RUNTIME)
  3. @Documented
  4. public @interface DS {
  5. String value() default DSConfig.DB_MASTER;
  6. }

动态数据源类:

  1. public class DynamicDataSource extends AbstractRoutingDataSource {
  2. private final Logger logger = LoggerUtils.getLogger(this);
  3. @Override
  4. protected Object determineCurrentLookupKey() {
  5. logger.debug("当前数据源为{}", DataSourceContextHolder.getDS());
  6. return DataSourceContextHolder.getDS();
  7. }
  8. }

动态数据源AOP实现类:

  1. @Aspect
  2. @Component
  3. public class DynamicDataSourceAspect {
  4. @Before("@annotation(DS)")
  5. public void beforeSwitchDS(JoinPoint point) {
  6. Class<?> className = point.getTarget().getClass();
  7. String methodName = point.getSignature().getName();
  8. Class<?>[] argClass = ((MethodSignature) point.getSignature()).getParameterTypes();
  9. String dataSource = DataSourceContextHolder.DEFAULT_DS;
  10. try {
  11. Method method = className.getMethod(methodName, argClass);
  12. if (method.isAnnotationPresent(DS.class)) {
  13. DS annotation = method.getAnnotation(DS.class);
  14. dataSource = annotation.value();
  15. }
  16. } catch (Exception e) {
  17. e.printStackTrace();
  18. }
  19. DataSourceContextHolder.setDS(dataSource);
  20. }
  21. @After("@annotation(DS)")
  22. public void afterSwitchDS(JoinPoint point) {
  23. DataSourceContextHolder.clearDS();
  24. }
  25. }

绑定当前线程数据源类:

  1. public class DataSourceContextHolder {
  2. public static final String DEFAULT_DS = DSConfig.DB_MASTER;
  3. private static final ThreadLocal<String> DS_HOLDER = new ThreadLocal<>();
  4. public static void setDS(String dbType) {
  5. DS_HOLDER.set(dbType);
  6. }
  7. public static String getDS() {
  8. return (DS_HOLDER.get());
  9. }
  10. public static void clearDS() {
  11. DS_HOLDER.remove();
  12. }
  13. }

推荐阅读

干货:2TB架构师四阶段视频教程

面经:史上最全Java多线程面试题及答案

面经:史上最全阿里高级Java面试题

面经:史上最全Spring面试题

教程:最全Spring Boot全套视频教程

书籍:进阶Java架构师必看的15本书

工具:推荐一款在线创作流程图、思维导图软件

分享Java干货,高并发编程,热门技术教程,微服务及分布式技术,架构设计,区块链技术,人工智能,大数据,Java面试题,以及前沿热门资讯等。

Spring Boot集成Mybatis双数据源的更多相关文章

  1. Spring boot 与mybatis 多数据源问题

    https://www.cnblogs.com/ityouknow/p/6102399.html Spring Boot 集成Mybatis实现多数据源 https://blog.csdn.net/m ...

  2. Spring Boot集成MyBatis开发Web项目

    1.Maven构建Spring Boot 创建Maven Web工程,引入spring-boot-starter-parent依赖 <project xmlns="http://mav ...

  3. 详解Spring Boot集成MyBatis的开发流程

    MyBatis是支持定制化SQL.存储过程以及高级映射的优秀的持久层框架,避免了几乎所有的JDBC代码和手动设置参数以及获取结果集. spring Boot是能支持快速创建Spring应用的Java框 ...

  4. 【spring boot】14.spring boot集成mybatis,注解方式OR映射文件方式AND pagehelper分页插件【Mybatis】pagehelper分页插件分页查询无效解决方法

    spring boot集成mybatis,集成使用mybatis拖沓了好久,今天终于可以补起来了. 本篇源码中,同时使用了Spring data JPA 和 Mybatis两种方式. 在使用的过程中一 ...

  5. spring boot集成mybatis(1)

    Spring Boot 集成教程 Spring Boot 介绍 Spring Boot 开发环境搭建(Eclipse) Spring Boot Hello World (restful接口)例子 sp ...

  6. spring boot集成mybatis(2) - 使用pagehelper实现分页

    Spring Boot 集成教程 Spring Boot 介绍 Spring Boot 开发环境搭建(Eclipse) Spring Boot Hello World (restful接口)例子 sp ...

  7. spring boot集成mybatis(3) - mybatis generator 配置

    Spring Boot 集成教程 Spring Boot 介绍 Spring Boot 开发环境搭建(Eclipse) Spring Boot Hello World (restful接口)例子 sp ...

  8. spring boot集成MyBatis 通用Mapper 使用总结

    spring boot集成MyBatis 通用Mapper 使用总结 2019年 参考资料: Spring boot集成 MyBatis 通用Mapper SpringBoot框架之通用mapper插 ...

  9. spring boot集成mybatis只剩两个sql 并提示 Cannot obtain primary key information from the database, generated objects may be incomplete

    前言 spring boot集成mybatis时只生成两个sql, 搞了一个早上,终于找到原因了 找了很多办法都没有解决, 最后注意到生成sql的时候打印了一句话: Cannot obtain pri ...

随机推荐

  1. “希希敬敬对”队软件工程第九次作业-beta冲刺第五次随笔

    “希希敬敬对”队软件工程第九次作业-beta冲刺第五次随笔 队名:  “希希敬敬对” 龙江腾(队长) 201810775001 杨希                   201810812008 何敬 ...

  2. 转 Xshell ssh长时间连接不掉线设置

    1.Xshell客户端设置 2.服务器设置 vi /etc/ssh/sshd_config 把ClientAliveInterval 0和ClientAliveCountMax 3前的井号去掉,并把C ...

  3. Springboot系列1_什么是Springboot

    Springboot系列1_什么是Springboot */--> code {color: #FF0000} pre.src {background-color: #002b36; color ...

  4. Codeforces 498A Crazy Town

    C. Crazy Town time limit per test 1 second memory limit per test 256 megabytes input standard input ...

  5. SQL数据库—<7>事务、异常和游标

    事务 一.什么是事务能够保证数据的一致性的代码控制,要么执行提交,要么滚回事务的初始状态 二.事务的四大特性:ACIDA:原子性-------事务不可拆开,要么执行要么回滚无中间状态C:一致性---- ...

  6. 对于一键退出APP功能实现的技术探讨

    在Android的开发过程中,会经常存在“一键退出APP”的需求.经过一段时间的整理,其主要实现方式有以下几种. 本质:一键结束当前APP的所有activity&一键结束当前APP进程,两者合 ...

  7. 第十一章 存储之ConfigMap

    1.描述信息 ConfigMap 功能在 Kubernetes1.2 版本中引入,许多应用程序会从配置文件.命令行参数或环境变量中读取配置信息.ConfigMap API 给我们提供了向容器中注入配置 ...

  8. 力扣 —— Two Sum ( 两数之和) python实现

    题目描述: 中文: 给定一个整数数组 nums 和一个目标值 target,请你在该数组中找出和为目标值的那 两个 整数,并返回他们的数组下标. 你可以假设每种输入只会对应一个答案.但是,你不能重复利 ...

  9. codeblocks编译调试C语言二级指针小记

    夜已深,暂时附上一个截图,后面慢慢道来. 下图时用codeblocks调试C语言的界面,codeblocks版本是17.12nosetup版,也为继承mingw,我用的编程器是tdm-gcc-5.1. ...

  10. Hibernate的优点与缺点

    Hibernate优点: 1.对象化.人员以面相对象的思想来操作数据库.Hibernate支持许多面向对象的特性,如组合,继承,多态等. 2.更好的移植性.对于不同的数据库,开发者只需要使用相同的数据 ...