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

本文涉及的产品
RDS AI 助手,专业版
RDS Agent(兼容OpenClaw),2核4GB
RDS MySQL DuckDB 分析主实例,集群系列 4核8GB
简介: 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

相关实践学习
每个IT人都想学的“Web应用上云经典架构”实战
本实验从Web应用上云这个最基本的、最普遍的需求出发,帮助IT从业者们通过“阿里云Web应用上云解决方案”,了解一个企业级Web应用上云的常见架构,了解如何构建一个高可用、可扩展的企业级应用架构。
MySQL数据库入门学习
本课程通过最流行的开源数据库MySQL带你了解数据库的世界。   相关的阿里云产品:云数据库RDS MySQL 版 阿里云关系型数据库RDS(Relational Database Service)是一种稳定可靠、可弹性伸缩的在线数据库服务,提供容灾、备份、恢复、迁移等方面的全套解决方案,彻底解决数据库运维的烦恼。 了解产品详情: https://www.aliyun.com/product/rds/mysql 
相关文章
|
8月前
|
XML Java 应用服务中间件
【SpringBoot(一)】Spring的认知、容器功能讲解与自动装配原理的入门,带你熟悉Springboot中基本的注解使用
SpringBoot专栏开篇第一章,讲述认识SpringBoot、Bean容器功能的讲解、自动装配原理的入门,还有其他常用的Springboot注解!如果想要了解SpringBoot,那么就进来看看吧!
742 2
|
9月前
|
人工智能 Java 机器人
基于Spring AI Alibaba + Spring Boot + Ollama搭建本地AI对话机器人API
Spring AI Alibaba集成Ollama,基于Java构建本地大模型应用,支持流式对话、knife4j接口可视化,实现高隐私、免API密钥的离线AI服务。
7073 2
基于Spring AI Alibaba + Spring Boot + Ollama搭建本地AI对话机器人API
存储 JSON Java
930 0
|
10月前
|
监控 Java API
Spring Boot 3.2 结合 Spring Cloud 微服务架构实操指南 现代分布式应用系统构建实战教程
Spring Boot 3.2 + Spring Cloud 2023.0 微服务架构实践摘要 本文基于Spring Boot 3.2.5和Spring Cloud 2023.0.1最新稳定版本,演示现代微服务架构的构建过程。主要内容包括: 技术栈选择:采用Spring Cloud Netflix Eureka 4.1.0作为服务注册中心,Resilience4j 2.1.0替代Hystrix实现熔断机制,配合OpenFeign和Gateway等组件。 核心实操步骤: 搭建Eureka注册中心服务 构建商品
1422 3
|
安全 Java Apache
微服务——SpringBoot使用归纳——Spring Boot中集成 Shiro——Shiro 身份和权限认证
本文介绍了 Apache Shiro 的身份认证与权限认证机制。在身份认证部分,分析了 Shiro 的认证流程,包括应用程序调用 `Subject.login(token)` 方法、SecurityManager 接管认证以及通过 Realm 进行具体的安全验证。权限认证部分阐述了权限(permission)、角色(role)和用户(user)三者的关系,其中用户可拥有多个角色,角色则对应不同的权限组合,例如普通用户仅能查看或添加信息,而管理员可执行所有操作。
667 0
|
12月前
|
Java 数据库连接 数据库
Spring boot 使用mybatis generator 自动生成代码插件
本文介绍了在Spring Boot项目中使用MyBatis Generator插件自动生成代码的详细步骤。首先创建一个新的Spring Boot项目,接着引入MyBatis Generator插件并配置`pom.xml`文件。然后删除默认的`application.properties`文件,创建`application.yml`进行相关配置,如设置Mapper路径和实体类包名。重点在于配置`generatorConfig.xml`文件,包括数据库驱动、连接信息、生成模型、映射文件及DAO的包名和位置。最后通过IDE配置运行插件生成代码,并在主类添加`@MapperScan`注解完成整合
1635 1
Spring boot 使用mybatis generator 自动生成代码插件
|
XML Java 数据库连接
微服务——SpringBoot使用归纳——Spring Boot集成MyBatis——基于注解的整合
本文介绍了Spring Boot集成MyBatis的两种方式:基于XML和注解的形式。重点讲解了注解方式,包括@Select、@Insert、@Update、@Delete等常用注解的使用方法,以及多参数时@Param注解的应用。同时,针对字段映射不一致的问题,提供了@Results和@ResultMap的解决方案。文章还提到实际项目中常结合XML与注解的优点,灵活使用两者以提高开发效率,并附带课程源码供下载学习。
1146 0
|
前端开发 Java 数据库连接
Java后端开发-使用springboot进行Mybatis连接数据库步骤
本文介绍了使用Java和IDEA进行数据库操作的详细步骤,涵盖从数据库准备到测试类编写及运行的全过程。主要内容包括: 1. **数据库准备**:创建数据库和表。 2. **查询数据库**:验证数据库是否可用。 3. **IDEA代码配置**:构建实体类并配置数据库连接。 4. **测试类编写**:编写并运行测试类以确保一切正常。
823 2