Spring Boot2.x-10 基于Spring Boot 2.1.2 + Mybatis 2.0.0实现多数据源,支持事务

本文涉及的产品
云数据库 RDS MySQL,集群系列 2核4GB
推荐场景:
搭建个人博客
RDS MySQL Serverless 基础系列,0.5-2RCU 50GB
RDS MySQL Serverless 高可用系列,价值2615元额度,1个月
简介: Spring Boot2.x-10 基于Spring Boot 2.1.2 + Mybatis 2.0.0实现多数据源,支持事务

概述


Spring Boot2.x-09 基于Spring Boot + Mybatis使用自定义注解实现数据库切换 通过自定义注解实现了数据库的切库。多数据源的支持我们通过这篇博文来梳理。


单个数据源 见 Spring Boot2.x-07Spring Boot2.1.2整合Mybatis


思路


让不同的数据源绑定不同的mybatis配置,再细化一点就是让不同的数据源扫描不同的包,让不同包下的mapper连接不同的数据源去处理业务逻辑。

多说一句,对于跨库的多表操作,这种整合是支持不了的


步骤

我们基于 Spring Boot2.x-09 基于Spring Boot + Mybatis使用自定义注解实现数据库切换 来改造下,让其支持多数据源。


20190203004331205.png


Step1 多数据源配置文件applicaiton.yml


自定义前缀,在标注了@Configuration的配置类中通过prefix 将数据源关联起来。 这里的配置类为DatasourceConfig。

# datasource  db1   前缀为自定义的datasource-db1
spring:
  datasource-db1:
    driver-class-name: com.mysql.cj.jdbc.Driver # JDBC连接Mysql6以上com.mysql.cj.jdbc.Driver (服务端为Mysql8)
    url: jdbc:mysql://localhost:3306/db1?serverTimezone=UTC&useUnicode=true&characterEncoding=utf8&useSSL=false
    username: root
    password: root
# datasource db2  前缀为自定义的datasource-db2
  datasource-db2: 
    driver-class-name: com.mysql.cj.jdbc.Driver # JDBC连接Mysql6以上com.mysql.cj.jdbc.Driver  (服务端为Mysql8)
    url: jdbc:mysql://localhost:3306/db2?serverTimezone=UTC&useUnicode=true&characterEncoding=utf8&useSSL=false
    username: root
    password: root
# mybatis 
mybatis_db1: 
  # 映射文件的路径 , 这个切换数据源的场景下不能配置 * 通配符,有多个 逗号隔开,继续跟 classpath:mapper_db1/XXX
  # 在MybatisConfig.java#sqlSessionFactoryBean方法中通过sqlSessionFactoryBean设置classpath:mapper_db1/*.xml ,不然每次都要改这个地方,不好维护。
  #  mapper-locations: classpath:mapper_db1/*.xml  
  # 类型别名包配置,只能指定具体的包,多个配置可以使用英文逗号隔开
  # 也可以通过sqlSessionFactoryBean.setTypeAliasesPackage("com.artisan.domain.db1")
  type-aliases-package: com.artisan.domain.db1
  # Mybatis SQL语句控制台打印
  configuration: 
      log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
 # mybatis 
mybatis_db2: 
  # 映射文件的路径 , 这个切换数据源的场景下不能配置 * 通配符,有多个 逗号隔开,继续跟 classpath:mapper/XXX
  # 在MybatisDB2Config.java#sqlSessionFactoryBean方法中通过sqlSessionFactoryBean设置classpath:mapper_db2/*.xml ,不然每次都要改这个地方,不好维护。
  #mapper-locations: classpath:mapper_db2/*.xml  
  # 类型别名包配置,只能指定具体的包,多个配置可以使用英文逗号隔开
  type-aliases-package: com.artisan.domain.db2
  # Mybatis SQL语句控制台打印
  configuration: 
      log-impl: org.apache.ibatis.logging.stdout.StdOutImpl

Step2 初始化多个数据源


这里同时也初始化了JdbcTemplate和事务管理,当然了对这个例子是可选操作。 主要是不同DataSource的初始化

多个数据源,操作JdbcTemplate和Transaction,需要指定使用哪个数据源,否则Spring根据type找到多个bean,不知道注入哪个。 【使用@Primary或者@Qualifier都可以】

package com.artisan.config;
import javax.sql.DataSource;
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.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import com.alibaba.druid.pool.DruidDataSource;
@Configuration
public class DatasourceConfig {
  //destroy-method="close":当数据库连接不使用的时候,将该连接重新放到数据池中
    @Bean(name=DataSources.DB1,destroyMethod="close")
    @ConfigurationProperties(prefix = "spring.datasource-db1")
    public DataSource dataSourceDB1() {
      // 创建数据源
        return DataSourceBuilder.create().type(DruidDataSource.class).build();
    }
    @Bean(name=DataSources.DB2,destroyMethod="close")
    @ConfigurationProperties(prefix = "spring.datasource-db2")
    public DataSource dataSourceDB2() {
      // 创建数据源
        return DataSourceBuilder.create().type(DruidDataSource.class).build();
    }
    // 支持JdbcTemplate (可选)
    @Bean(DataSources.DB1_JDBCTEMPLATE)
    public JdbcTemplate db1JdbcTemplate(
            @Qualifier(DataSources.DB1) DataSource dataSource) {
        return new JdbcTemplate(dataSource);
    }
    @Bean(DataSources.DB2_JDBCTEMPLATE)
    public JdbcTemplate db2JdbcTemplate(
            @Qualifier(DataSources.DB2) DataSource dataSource) {
        return new JdbcTemplate(dataSource);
    }
    // 支持事务(可选)
    @Bean(DataSources.DB1_TRANSACTION)
    public DataSourceTransactionManager db1TransactionManager(
            @Qualifier(DataSources.DB1) DataSource dataSource) {
        return new DataSourceTransactionManager(dataSource);
    }
    @Bean(DataSources.DB2_TRANSACTION)
    public DataSourceTransactionManager db2TransactionManager(
            @Qualifier(DataSources.DB2) DataSource dataSource) {
        return new DataSourceTransactionManager(dataSource);
    }
}


Step3 配置多个数据源


1. 扫描包的路径,不同的数据源扫描的包路径不同

2. 通过@Qualifier指定注入的数据源

3. Mybatis如果使用xml方式,配置文件中有配置项,通过前缀加载对应的配置项

MybatisDB1Config

package com.artisan.config;
import javax.sql.DataSource;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.SqlSessionTemplate;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
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 org.springframework.core.io.support.PathMatchingResourcePatternResolver;
@Configuration
//  db1的接口类的包名
@MapperScan(basePackages = { "com.artisan.dao.db1" }, sqlSessionFactoryRef = "db1SqlSessionFactoryBean")
public class MybatisDB1Config {
  @Autowired
  // 必须指定注入哪个数据源,否则找到多个会注入失败
  @Qualifier(DataSources.DB1)
  private DataSource db1;
  @Bean(name = "db1SqlSessionFactoryBean")
  @ConfigurationProperties(prefix = "mybatis-db1") // 和 配置文件中的前缀保持一致
  // @Primary 如果SqlSessionFactoryBean的名字和MybatisDB2Config中的一致(默认方法名),需要加上这个注解,优先注入该SqlSessionFactoryBean
  // 这里我们通过bean指定了name,并且方法名也不一样,所以如果情况不一样,看是否需要加入@Primary 。 如果需要两个方法上加一个就行了,都加的话,spring又找不到bean注入啦。。
  public SqlSessionFactoryBean db1SqlSessionFactoryBean() throws Exception {
    SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
    // 配置数据源
    sqlSessionFactoryBean.setDataSource(db1);
    // 如下的两行代码仅仅用于*.xml文件,如果整个持久层操作没用到xml文件的话,比如使用注解的方式,则无需加
    // 解决配置到配置文件中通过*配置找不到mapper文件的问题。 如果不设置这一行,在配置文件中,只能使用数组的方式一个个的罗列出来,并且要指定具体的文件名
    sqlSessionFactoryBean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources("classpath:mapper_db1/*.xml"));
    // 也可以通过在application.yml中配置
    //sqlSessionFactoryBean.setTypeAliasesPackage("com.artisan.domain.db1");
    return sqlSessionFactoryBean;
  }
  // 可选,如果需要通过SqlSessionTemplate来操作持久层就通过@Bean实例化,我们这个例子中没用到,随手写出来了
  @Bean(name="db1SqlSessionTemplate")
  public SqlSessionTemplate db1SqlSessionTemplate() throws Exception {
    SqlSessionTemplate template = new SqlSessionTemplate(db1SqlSessionFactoryBean().getObject());
    return template;
  }
}


MybatisDB2Config

package com.artisan.config;
import javax.sql.DataSource;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.SqlSessionTemplate;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
@Configuration
@MapperScan(basePackages = { "com.artisan.dao.db2" },sqlSessionFactoryRef="db2SqlSessionFactoryBean" ) // 扫描的mybatis接口类的包名
public class MybatisDB2Config {
  @Autowired
  // 必须指定注入哪个数据源,否则找到多个会注入失败
  @Qualifier(DataSources.DB2)
  private DataSource db2;
  @Bean(name="db2SqlSessionFactoryBean")
  @ConfigurationProperties(prefix = "mybatis-db2") // 和 配置文件中的前缀保持一致
  public SqlSessionFactoryBean db2SqlSessionFactoryBean() throws Exception {
    SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
    // 配置数据源
    sqlSessionFactoryBean.setDataSource(db2);
    // 解决配置到配置文件中通过*配置找不到mapper文件的问题。 如果不设置这一行,在配置文件中,只能使用数组的方式一个个的罗列出来,并且要指定具体的文件名
        sqlSessionFactoryBean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources("classpath:mapper_db2/*.xml"));
    return sqlSessionFactoryBean;
  }
  // 可选,如果需要通过SqlSessionTemplate来操作持久层就通过@Bean实例化,我们这个例子中没用到,随手写出来了
  @Bean(name = "db2SqlSessionTemplate")
  public SqlSessionTemplate db2SqlSessionTemplate() throws Exception {
    SqlSessionTemplate template = new SqlSessionTemplate(db2SqlSessionFactoryBean().getObject());
    return template;
  }
}


验证测试


数据库表、Domain , Dao , Service,Controller中的代码见下面的Github连接,很简单,就不贴了,只说重点

贴下数据库的初始数据吧

db1:

20190203011738425.png

db2:


20190203011817252.png


启动Spring Boot工程,

访问 http://localhost:8080/db1?id=1


20190203011958376.png

访问 http://localhost:8080/db2?id=1


20190203011938938.png

支持事务

Step1 配置类中通过@Bean初始化DataSourceTransactionManager


20190203012117342.png


Step2 如何使用呢? @Transactional(DataSources.DB1_TRANSACTION)



20190203012329126.png


Step3 验证事务

访问: http://localhost:8080/db1/transTest

20190203012421435.png

数据库中的数据


20190203012534410.png

为了方便验证 artisanMapper.updateArtisan(artisan)中故意写错了个字段名,让其抛出异常,测试回滚。 执行方法后,可以看到因为第二个方法报错,第一个方法中的插入的数据也回滚了 。


顺便说下 集成JdbcTemplate


持久层我们这里用的Mybatis,有些同学说我想用JdbcTemplate呢


Step1 配置类中通过@Bean初始化每个数据库实例对应的JdbcTemplate


20190203012855160.png


Step2 如何使用呢? @Autowired @Qualifier(DataSources.DB1_JDBCTEMPLATE)


20190203012952179.png

Step3 验证


20190203013129268.png


启动工程,访问 http://localhost:8080/db1/jdbcTemplateTest

查看数据

20190203013220481.png

代码


https://github.com/yangshangwei/MultiDataSource

相关实践学习
如何在云端创建MySQL数据库
开始实验后,系统会自动创建一台自建MySQL的 源数据库 ECS 实例和一台 目标数据库 RDS。
全面了解阿里云能为你做什么
阿里云在全球各地部署高效节能的绿色数据中心,利用清洁计算为万物互联的新世界提供源源不断的能源动力,目前开服的区域包括中国(华北、华东、华南、香港)、新加坡、美国(美东、美西)、欧洲、中东、澳大利亚、日本。目前阿里云的产品涵盖弹性计算、数据库、存储与CDN、分析与搜索、云通信、网络、管理与监控、应用服务、互联网中间件、移动服务、视频服务等。通过本课程,来了解阿里云能够为你的业务带来哪些帮助     相关的阿里云产品:云服务器ECS 云服务器 ECS(Elastic Compute Service)是一种弹性可伸缩的计算服务,助您降低 IT 成本,提升运维效率,使您更专注于核心业务创新。产品详情: https://www.aliyun.com/product/ecs
相关文章
|
1月前
|
Java 测试技术 开发者
springboot学习四:Spring Boot profile多环境配置、devtools热部署
这篇文章主要介绍了如何在Spring Boot中进行多环境配置以及如何整合DevTools实现热部署,以提高开发效率。
60 2
|
1月前
|
前端开发 Java 程序员
springboot 学习十五:Spring Boot 优雅的集成Swagger2、Knife4j
这篇文章是关于如何在Spring Boot项目中集成Swagger2和Knife4j来生成和美化API接口文档的详细教程。
92 1
|
1月前
|
Java API Spring
springboot学习七:Spring Boot2.x 拦截器基础入门&实战项目场景实现
这篇文章是关于Spring Boot 2.x中拦截器的入门教程和实战项目场景实现的详细指南。
26 0
springboot学习七:Spring Boot2.x 拦截器基础入门&实战项目场景实现
|
1月前
|
Java Spring
springboot 学习十一:Spring Boot 优雅的集成 Lombok
这篇文章是关于如何在Spring Boot项目中集成Lombok,以简化JavaBean的编写,避免冗余代码,并提供了相关的配置步骤和常用注解的介绍。
96 0
|
Java API Maven
Springboot 系列(四)Spring Boot 日志框架
Springboot 系列(四)Spring Boot 日志框架
768 0
Springboot 系列(四)Spring Boot 日志框架
|
1月前
|
JavaScript 安全 Java
如何使用 Spring Boot 和 Ant Design Pro Vue 实现动态路由和菜单功能,快速搭建前后端分离的应用框架
本文介绍了如何使用 Spring Boot 和 Ant Design Pro Vue 实现动态路由和菜单功能,快速搭建前后端分离的应用框架。首先,确保开发环境已安装必要的工具,然后创建并配置 Spring Boot 项目,包括添加依赖和配置 Spring Security。接着,创建后端 API 和前端项目,配置动态路由和菜单。最后,运行项目并分享实践心得,包括版本兼容性、安全性、性能调优等方面。
146 1
|
20天前
|
JavaScript 安全 Java
如何使用 Spring Boot 和 Ant Design Pro Vue 构建一个具有动态路由和菜单功能的前后端分离应用。
本文介绍了如何使用 Spring Boot 和 Ant Design Pro Vue 构建一个具有动态路由和菜单功能的前后端分离应用。首先,创建并配置 Spring Boot 项目,实现后端 API;然后,使用 Ant Design Pro Vue 创建前端项目,配置动态路由和菜单。通过具体案例,展示了如何快速搭建高效、易维护的项目框架。
95 62
|
18天前
|
JavaScript 安全 Java
如何使用 Spring Boot 和 Ant Design Pro Vue 构建一个前后端分离的应用框架,实现动态路由和菜单功能
本文介绍了如何使用 Spring Boot 和 Ant Design Pro Vue 构建一个前后端分离的应用框架,实现动态路由和菜单功能。首先,确保开发环境已安装必要的工具,然后创建并配置 Spring Boot 项目,包括添加依赖和配置 Spring Security。接着,创建后端 API 和前端项目,配置动态路由和菜单。最后,运行项目并分享实践心得,帮助开发者提高开发效率和应用的可维护性。
36 2
|
20天前
|
JavaScript Java 项目管理
Java毕设学习 基于SpringBoot + Vue 的医院管理系统 持续给大家寻找Java毕设学习项目(附源码)
基于SpringBoot + Vue的医院管理系统,涵盖医院、患者、挂号、药物、检查、病床、排班管理和数据分析等功能。开发工具为IDEA和HBuilder X,环境需配置jdk8、Node.js14、MySQL8。文末提供源码下载链接。
|
2月前
|
前端开发 JavaScript Java
基于Java+Springboot+Vue开发的大学竞赛报名管理系统
基于Java+Springboot+Vue开发的大学竞赛报名管理系统(前后端分离),这是一项为大学生课程设计作业而开发的项目。该系统旨在帮助大学生学习并掌握Java编程技能,同时锻炼他们的项目设计与开发能力。通过学习基于Java的大学竞赛报名管理系统项目,大学生可以在实践中学习和提升自己的能力,为以后的职业发展打下坚实基础。
219 3
基于Java+Springboot+Vue开发的大学竞赛报名管理系统