侧边栏壁纸
博主头像
昂洋编程 博主等级

鸟随鸾凤飞腾远,人伴贤良品自高

  • 累计撰写 71 篇文章
  • 累计创建 79 个标签
  • 累计收到 0 条评论

目 录CONTENT

文章目录

Springboot+Mybatis Plus+Druid实现多数据源

Administrator
2023-07-04 / 0 评论 / 0 点赞 / 119 阅读 / 0 字 / 正在检测是否收录...
温馨提示:
本文最后更新于2024-06-14,若内容或图片失效,请留言反馈。 部分素材来自网络,若不小心影响到您的利益,请联系我们删除。

参考: https://blog.csdn.net/wodejiaAA/article/details/130654780

pom.xml依赖

这里springboot的版本为2.4.2

       <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
        </dependency>

        <!--spring-boot-web-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <!--mybatis-plus-->
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
            <version>3.4.3.1</version>
        </dependency>

        <!-- druid-->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid-spring-boot-starter</artifactId>
            <version>1.2.8</version>
        </dependency>

	   <!--lombok-->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>

        <!--   mysql     -->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <scope>runtime</scope>
        </dependency>

application.yml

#配置数据源
spring:
  datasource:
    druid:
      # 初始连接数
      initial-size: 5
      # 最小连接数
      min-idle: 15
      # 最大连接数
      max-active: 30
      # eladmin库
      eladmin:
        url: jdbc:mysql://xxx:3306/eladmin?serverTimezone=Asia/Shanghai&characterEncoding=utf8&useSSL=false
        username: eladmin
        password: xxx
        driver-class-name: com.mysql.cj.jdbc.Driver
      # CRM库
      crm:
        url: jdbc:mysql://xxx:3306/crm?serverTimezone=Asia/Shanghai&characterEncoding=utf8&useSSL=false
        username: ygsw
        password: xxx
        driver-class-name: com.mysql.cj.jdbc.Driver
      # Lims库
      lims:
        url: jdbc:mysql://xxx:23260/lims?serverTimezone=Asia/Shanghai&characterEncoding=utf8&useSSL=false
        username: root
        password: xxx
        driver-class-name: com.mysql.cj.jdbc.Driver

启动类修改

@SpringBootApplication(exclude = { MybatisPlusAutoConfiguration.class, DruidDataSourceAutoConfigure.class, DataSourceAutoConfiguration.class})

注意这里是排除了Mybatis Plus自动配置类、Druid自动配置类以及数据源自动配置类,因为这些配置都是我们手动配置的

Mybatis-Plus 配置

package com.hugo.config;

import com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceBuilder;
import com.baomidou.mybatisplus.core.MybatisConfiguration;
import com.baomidou.mybatisplus.core.config.GlobalConfig;
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
import com.baomidou.mybatisplus.extension.spring.MybatisSqlSessionFactoryBean;
import com.hugo.enums.DataSourceEnum;
import org.apache.ibatis.plugin.Interceptor;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.type.JdbcType;
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 javax.sql.DataSource;
import java.util.HashMap;
import java.util.Map;

/**
 * 配置文件
 * Created by jinjin on 2020-09-21.
 */
@Configuration
@MapperScan(basePackages ={"com.hugo.**.mapper"})
public class MybatisPlusConfig {
    @Bean
    public MybatisPlusInterceptor paginationInterceptor() {
        MybatisPlusInterceptor paginationInterceptor = new MybatisPlusInterceptor();
        // 设置请求的页面大于最大页后操作, true调回到首页,false 继续请求  默认false
        // paginationInterceptor.setOverflow(false);
        // 设置最大单页限制数量,默认 500 条,-1 不受限制
        // paginationInterceptor.setLimit(500);
        // 开启 count 的 join 优化,只针对部分 left join
        //paginationInterceptor.setCountSqlParser(new JsqlParserCountOptimize(true));
        PaginationInnerInterceptor page = new PaginationInnerInterceptor();
        paginationInterceptor.addInnerInterceptor(page);
        return paginationInterceptor;
    }

    /**
     * 多数据源的创建 有几个就写几个
     */
    @Bean(name = "eladmin")
    @ConfigurationProperties(prefix = "spring.datasource.druid.eladmin" )
    public DataSource eladmin() {
        return DruidDataSourceBuilder.create().build();
    }

    @Bean(name = "crm")
    @ConfigurationProperties(prefix = "spring.datasource.druid.crm" )
    public DataSource crm() {
        return DruidDataSourceBuilder.create().build();
    }

    @Bean(name = "lims")
    @ConfigurationProperties(prefix = "spring.datasource.druid.lims" )
    public DataSource lims() {
        return DruidDataSourceBuilder.create().build();
    }

    /**
     * 动态数据源配置
     * @return
     */
    @Bean
    @Primary
    public DataSource multipleDataSource(@Qualifier("eladmin") DataSource eladmin, @Qualifier("crm") DataSource crm, @Qualifier("lims") DataSource lims) {
        DynamicDataSource dynamicDataSource = new DynamicDataSource();
        Map< Object, Object > targetDataSources = new HashMap<>();
        targetDataSources.put(DataSourceEnum.ELADMIN.getValue(), eladmin);
        targetDataSources.put(DataSourceEnum.CRM.getValue(), crm);
        targetDataSources.put(DataSourceEnum.LIMS.getValue(), lims);
        //添加数据源
        dynamicDataSource.setTargetDataSources(targetDataSources);
        //设置默认数据源
        dynamicDataSource.setDefaultTargetDataSource(eladmin);
        return dynamicDataSource;
    }

    @Bean("sqlSessionFactory")
    public SqlSessionFactory sqlSessionFactory() throws Exception {
        MybatisSqlSessionFactoryBean sqlSessionFactory = new MybatisSqlSessionFactoryBean();
        sqlSessionFactory.setDataSource(multipleDataSource(eladmin(),crm(),lims()));
        // 指定所有的mapper.xml文件,否则在切换数据源时提示ibatis.binding.BindingException
        sqlSessionFactory.setMapperLocations(new PathMatchingResourcePatternResolver().getResources("classpath:/mapper/*Mapper.xml"));

        MybatisConfiguration configuration = new MybatisConfiguration();
        //configuration.setDefaultScriptingLanguage(MybatisXMLLanguageDriver.class);
        configuration.setJdbcTypeForNull(JdbcType.NULL);
        configuration.setMapUnderscoreToCamelCase(true);
        configuration.setCacheEnabled(false);

        sqlSessionFactory.setConfiguration(configuration);
        sqlSessionFactory.setPlugins(new Interceptor[]{
                //PerformanceInterceptor(),OptimisticLockerInterceptor()
                paginationInterceptor()
                //添加分页功能
        });
        //sqlSessionFactory.setGlobalConfig(globalConfiguration());
        return sqlSessionFactory.getObject();
    }

    @Bean
    public GlobalConfig globalConfig() {
        GlobalConfig globalConfig = new GlobalConfig();
        globalConfig.setMetaObjectHandler(new MybatisPlusFillHandler());
        return globalConfig;
    }
}

多数据源配置

  1. 数据源枚举类
package com.hugo.enums;

public enum DataSourceEnum {
    ELADMIN("eladmin"),CRM("crm"),LIMS("lims");

    private String value;

    DataSourceEnum(String value){this.value=value;}

    public String getValue() {
        return value;
    }
}

  1. 数据源切面配置
package com.hugo.annotation;

import com.hugo.enums.DataSourceEnum;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 * @author zhangmy
 * @date 2023/7/4 13:49
 * @description 自定义多数据源切换
 */
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD, ElementType.TYPE})
public @interface DataSource {
    DataSourceEnum value() default DataSourceEnum.ELADMIN;
}

package com.hugo.aspect;

import com.hugo.annotation.DataSource;
import com.hugo.utils.DynamicDataSourceContextHolder;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;

/**
 * @author zhangmy
 * @date 2023/7/4 13:50
 * @description
 */
@Aspect
@Order(1)
@Component
public class DataSourceAspect {

    @Pointcut("@annotation(com.hugo.annotation.DataSource)"
            + "|| @within(com.hugo.annotation.DataSource)")
    public void dsPc() {

    }

    @Before("dsPc()  && @annotation(dataSource)")
    public void doBefore(DataSource dataSource)  {
        DynamicDataSourceContextHolder.setDataSourceType(dataSource.value().getValue());
    }

    @After("dsPc()")
    public void doAfter() {
        DynamicDataSourceContextHolder.clearDataSourceType();
    }
}

  1. 动态数据源决策
package com.hugo.config;

import com.hugo.utils.DynamicDataSourceContextHolder;
import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;

/**
 * @author zhangmy
 * @date 2023/7/4 13:43
 * @description 动态数据源决策
 */
public class DynamicDataSource extends AbstractRoutingDataSource {
    @Override
    protected Object determineCurrentLookupKey() {
        return DynamicDataSourceContextHolder.getDataSourceType();
    }
}

  1. 数据源切换工具类
package com.hugo.utils;

import lombok.extern.slf4j.Slf4j;

/**
 * @author zhangmy
 * @date 2023/7/4 13:43
 * @description 数据源切换工具类
 */
@Slf4j
public class DynamicDataSourceContextHolder {

    /**
     * 使用ThreadLocal维护变量,ThreadLocal为每个使用该变量的线程提供独立的变量副本,
     * 所以每一个线程都可以独立地改变自己的副本,而不会影响其它线程所对应的副本。
     */
    private static final ThreadLocal<String> CONTEXT_HOLDER = new ThreadLocal<>();

    /**
     * 设置数据源的变量,参考DataSourceEnum.java类的取值
     */
    public static void setDataSourceType(String dsType) {
        log.info("切换到{}数据源", dsType);
        CONTEXT_HOLDER.set(dsType);
    }

    /**
     * 获得数据源的变量
     */
    public static String getDataSourceType() {
        return CONTEXT_HOLDER.get();
    }

    /**
     * 清空数据源变量
     */
    public static void clearDataSourceType() {
        CONTEXT_HOLDER.remove();
    }
}

测试示例

原先eladmin数据源是有一套完整的mapper、service和controller的,这里我们加一个lims的测试

LimsMapper.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.hugo.modules.system.service.mapper.LimsMapper">

  <!-- 根据ID查询样本信息 -->
  <select id="getSampleById" resultType="map">
    select * from gen_modual_sm where ID = #{ID}
  </select>

</mapper>

LimsMapper

package com.hugo.modules.system.service.mapper;

import org.apache.ibatis.annotations.Param;
import org.springframework.stereotype.Repository;

import java.util.Map;

/**
 * @author zhangmy
 * @date 2023/7/4 17:22
 * @description
 */
@Repository
public interface LimsMapper {

    /**
     * 根据ID查询样本信息
     * @param ID
     * @return
     */
    Map<String, Object> getSampleById(@Param("ID") String ID);
}

TestController

package com.hugo.modules.system.rest;

import com.alibaba.fastjson.JSONObject;
import com.hugo.annotation.rest.AnonymousGetMapping;
import com.hugo.modules.system.LimsService;
import com.hugo.modules.system.service.UserService;
import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.Map;

/**
 * @author zhangmy
 * @date 2023/7/4 17:20
 * @description
 */
@RestController
@RequestMapping("/api/test")
@RequiredArgsConstructor
public class TestController {

    private final UserService userService;
    private final LimsService limsService;

    @AnonymousGetMapping("/demo")
    public void test() {
        // 查询用户
        System.out.println(userService.getByUsername("admin"));
        Map<String, Object> sampleMap = limsService.getSampleById("00178a07-2f4b-4b8b-9f79-e29550dc62b1");
        System.out.println(JSONObject.toJSON(sampleMap));
    }
}

系统我们配置的默认是eladmin的数据源,测试代码中我们使用默认数据查询admin的账户信息,然后切换lims数据源查询样本信息,下面是运行结果
image

一些问题

系统启动的时候在ApplicationRunner使用了数据源报错ibatis.binding.BindingException

这里尤其注意MybatisPlusConfig中的sqlSessionFactory配置
image-1688466924115
不管是启动使用数据源还是启动后切换数据源报这个错,都是mapper绑定的关系出现了问题,由于需求比较简单,我们可以把mapper都放在一个包下面,然后如上图中配置mapper的位置即可

Failed to configure a DataSource: 'url' attribute is not specified and no em...

这个问题是因为我们配置了多数据源,就不能让系统自动配置数据源了,所以要在启动类上面排除DataSourceAutoConfiguration

0

评论区