SpringBoot3.0整合Mybatis-plus实现多数据源(重构类方式)

背景

前段时间在做一个数据中台的项目,系统用到了不同数据库中的数据。自己又不想手写JDBC连接,既然我有这个需求,那功能应该有人实现了,于是开始了网上搜了,搜索后发现基本都是讲读写分离、主备切换的,后面也查略了Mybatis-plus的官网,里面有这个功能,但好像是我组件的版本之间不兼容,导致出现了一些奇怪的bug,比如:Property ‘sqlSessionFactory‘ or ‘sqlSessionTemplate‘ are required或者是无法访问javax.servlet.http.HttpServlet 亦或者是Cannot resolve reference to bean ‘sqlSessionFactory‘ while setting constructor argument; nested exce

总之,历经多重磨难总算是解决了

选择合适的组件版本能事半功倍

组件版本

SpringBoot:3.0.0

JDK:19

MySQL:8.0.28

ClickHouse:21.4.6

clickhouse-jdbc:0.3.2-patch11

Mybatis-plus:3.5.7

dynamic-datasource:3.3.2

Druid:1.2.20

具体的pom.xml文件如下:

	<parent>
        <artifactId>spring-boot-starter-parent</artifactId>
        <groupId>org.springframework.boot</groupId>
        <!--springboot3 开始,JDK最低要求要11,该项目用的是19-->
        <version>3.0.0</version>
    </parent>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
        </dependency>
<!--        mysql-jdbc 驱动-->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>8.0.28</version>
        </dependency>
<!--        clickhouse-jdbc 驱动-->
        <dependency>
            <groupId>com.clickhouse</groupId>
            <artifactId>clickhouse-jdbc</artifactId>
            <version>0.3.2-patch11</version>
            <classifier>all</classifier>
            <exclusions>
                <exclusion>
                    <groupId>*</groupId>
                    <artifactId>*</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
<!--json web token-->
        <dependency>
            <groupId>io.jsonwebtoken</groupId>
            <artifactId>jjwt-api</artifactId>
            <version>0.11.5</version>
        </dependency>
        <dependency>
            <groupId>io.jsonwebtoken</groupId>
            <artifactId>jjwt-impl</artifactId>
            <version>0.11.5</version>
        </dependency>
        <dependency>
            <groupId>io.jsonwebtoken</groupId>
            <artifactId>jjwt-jackson</artifactId>
            <version>0.11.5</version>
        </dependency>
        <!-- lombok 依赖 -->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>
        <!-- mybatis-plus 依赖 -->
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
            <version>3.5.7</version>
        </dependency>
        <!-- 动态配置多种数据源 -->
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>dynamic-datasource-spring-boot-starter</artifactId>
            <version>3.3.2</version>
        </dependency>
        <!-- druid启动器的依赖  -->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid-spring-boot-3-starter</artifactId>
            <version>1.2.20</version>
        </dependency>
    </dependencies>

至于为什么选择这些版本的组件,我也不知道🤐干活就是了。

需求

在同一个(Maven)项目中,根据不同请求,实现对不同数据库的操作,数据源选择Druid。

源码

项目代码整体架构

application.yml配置文件

spring:
  datasource:
    dynamic:
      primary: system
      strict: false
      datasource:
        system:
          url: jdbc:mysql://xxx.xxx.xxx.xxx:xxxx/zhongtai_admin?useSSL=false&useUnicode=true&characterEncoding=utf8&serverTimezone=UTC
          username: xxxx
          password: xxxx
          driver-class-name: com.mysql.cj.jdbc.Driver
          type: com.alibaba.druid.pool.DruidDataSource
          initialSize: 5
          minIdle: 5
          maxActive: 20
          maxWait: 600000
          stat-view-servlet:
            enabled: true
            url-pattern: /druid/*
          timeBetweenEvictionRunsMillis: 600000
          minEvictableIdleTimeMillis: 300000
          validationQuery: SELECT 1
          testWhileIdle: true
          testOnBorrow: false
          testOnReturn: false
          poolPreparedStatements: true
          filters: stat,wall
          maxPoolPreparedStatementPerConnectionSize: 20
          useGlobalDataSourceStat: true
          connectionProperties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=500
        report:
          url: jdbc:clickhouse://xxx.xxx.xxx.xxx:xxxx/ads
          enabled: true
          driver-class-name: com.clickhouse.jdbc.ClickHouseDriver
          type: com.alibaba.druid.pool.DruidDataSource
          initialSize: 5
          minIdle: 5
          maxActive: 20
          maxWait: 600000
          stat-view-servlet:
            enabled: true
            url-pattern: /druid/*
          timeBetweenEvictionRunsMillis: 600000
          minEvictableIdleTimeMillis: 300000
          validationQuery: SELECT 1
          testWhileIdle: true
          testOnBorrow: false
          testOnReturn: false
          poolPreparedStatements: true
          filters: stat,wall
          maxPoolPreparedStatementPerConnectionSize: 20
          useGlobalDataSourceStat: true
          connectionProperties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=500

重写相关容器类

不知道大家是否还了解 SpringBoot 的自动装配原理(过程),像我们直接导 mybatis-plus-boot-starter 或是其他依赖,只要版本之间没问题,理论上是不会直接报:Property ‘sqlSessionFactory‘ or ‘sqlSessionTemplate‘ are required这些错误的,mybatis-plus 里是已经自动封装好了的。

ps:这个错误是我加了 dynamic-datasource-spring-boot-starter 后才报的,当时官网介绍很少(版本号什么的都没给),给了一个详细了解连接,跳转后要付费查略😡

遇到这些问题怎么办?只能自己重写相关方法,注入到bean中了。

SystemDataSourceConfig.java

ackage com.pzb.admin.config;

import com.alibaba.druid.pool.DruidDataSource;
import com.baomidou.mybatisplus.core.MybatisConfiguration;
import com.baomidou.mybatisplus.extension.spring.MybatisSqlSessionFactoryBean;
import org.apache.ibatis.logging.stdout.StdOutImpl;
import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionTemplate;
import org.mybatis.spring.annotation.MapperScan;
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;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;

import javax.sql.DataSource;
import java.sql.SQLException;

/**
 * @Description TODO 因为涉及多个数据源,现重写sqlSessionFactory等配置类,根据不同的mapper.xml映射文件,操作不同的数据源
 * @Author zbpeng
 * @DATE 2024/10/29 10:51
 */
@Configuration
@MapperScan(basePackages = "com.pzb.*.mapper", sqlSessionFactoryRef = "sqlSessionFactory")
public class SystemDataSourceConfig {
    @Bean(name = "dataSource")
    @ConfigurationProperties(prefix = "spring.datasource.dynamic.datasource.system")
    @Primary
    public DataSource dataSource() throws SQLException {
        // 选择Druid 数据源
        DruidDataSource druidDataSource = new DruidDataSource();
        druidDataSource.setFilters("stat");
        return druidDataSource;
    }

    @Bean(name = "sqlSessionFactory")
    @Primary
    public SqlSessionFactory sqlSessionFactory(@Qualifier("dataSource") DataSource dataSource) throws Exception {
        MybatisSqlSessionFactoryBean bean = new MybatisSqlSessionFactoryBean();
        bean.setDataSource(dataSource);
        bean.setTypeAliasesPackage("com.pzb.*.bean");
        bean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources("classpath:mappers/system/*.xml"));
        //其他配置 ps:sqlSessionFactoryBean内可以配置yml的一切配置
        MybatisConfiguration mybatisConfiguration = new MybatisConfiguration();
        //是否下划线转驼峰
        mybatisConfiguration.setMapUnderscoreToCamelCase(true);
        //设置日志
        mybatisConfiguration.setLogImpl(StdOutImpl.class);

        bean.setConfiguration(mybatisConfiguration);

        return bean.getObject();
    }

    @Bean(name = "transactionManager")
    @Primary
    public DataSourceTransactionManager transactionManager(@Qualifier("dataSource") DataSource dataSource) {
        return new DataSourceTransactionManager(dataSource);
    }

    @Bean(name = "sqlSessionTemplate")
    @Primary
    public SqlSessionTemplate sqlSessionTemplate(@Qualifier("sqlSessionFactory") SqlSessionFactory sqlSessionFactory) {
        return new SqlSessionTemplate(sqlSessionFactory);
    }
}

ReportDataSourceConfig.java

package com.pzb.statistic.config;

import com.alibaba.druid.pool.DruidDataSource;
import com.baomidou.mybatisplus.core.MybatisConfiguration;
import com.baomidou.mybatisplus.extension.spring.MybatisSqlSessionFactoryBean;
import org.apache.ibatis.logging.stdout.StdOutImpl;
import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionTemplate;
import org.mybatis.spring.annotation.MapperScan;
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;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;

import javax.sql.DataSource;
import java.sql.SQLException;

/**
 * @Description TODO 因为涉及多个数据源,现重写sqlSessionFactory等配置类,根据不同的mapper.xml映射文件,操作不同的数据源
 * @Author zbpeng
 * @DATE 2024/10/29 10:55
 */
@Configuration
@MapperScan(basePackages = "com.pzb.statistic.mapper", sqlSessionFactoryRef = "sqlSessionFactory2")
public class ReportDataSourceConfig {
    @Bean(name = "dataSource2")
    @ConfigurationProperties(prefix = "spring.datasource.dynamic.datasource.report")
    public DataSource dataSource() throws SQLException {
        DruidDataSource druidDataSource = new DruidDataSource();
        druidDataSource.setFilters("stat");
        return druidDataSource;
    }

    @Bean(name = "sqlSessionFactory2")
    public SqlSessionFactory sqlSessionFactory(@Qualifier("dataSource2") DataSource dataSource) throws Exception {
        MybatisSqlSessionFactoryBean bean = new MybatisSqlSessionFactoryBean();
        bean.setDataSource(dataSource);
        bean.setTypeAliasesPackage("com.pzb.report.bean");
        bean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources("classpath:mappers/report/*.xml"));
        //其他配置 ps:sqlSessionFactoryBean内可以配置yml的一切配置
        MybatisConfiguration mybatisConfiguration = new MybatisConfiguration();
        //是否下划线转驼峰
        mybatisConfiguration.setMapUnderscoreToCamelCase(true);
        //设置日志
        mybatisConfiguration.setLogImpl(StdOutImpl.class);

        bean.setConfiguration(mybatisConfiguration);

        return bean.getObject();
    }

    @Bean(name = "transactionManager2")
    public DataSourceTransactionManager transactionManager(@Qualifier("dataSource2") DataSource dataSource) {
        return new DataSourceTransactionManager(dataSource);
    }

    @Bean(name = "sqlSessionTemplate2")
    public SqlSessionTemplate sqlSessionTemplate(@Qualifier("sqlSessionFactory2") SqlSessionFactory sqlSessionFactory) {
        return new SqlSessionTemplate(sqlSessionFactory);
    }
}

​ 后面就正常写就行了,根据你调的Mapper接口方法,找到对应的.xml映射文件,.xml映射文件绑定了不同的JDBC连接器,进而操作不同的数据库。(大概)


不知个位大佬有没有更好的解决方法方便讲解一下😭

参考资料:

https://blog.csdn.net/weixin_42675423/article/details/126932977

https://www.cnblogs.com/gudian/p/15163126.html

posted @ 2024-10-30 17:57  MrSponge  Views(113)  Comments(0Edit  收藏  举报