Spring Boot整合Mybatis配置多数据源

这篇具有很好参考价值的文章主要介绍了Spring Boot整合Mybatis配置多数据源。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。


前言

在之前的事件管理系统博客中有提到动态的多数据源配置

工作中难免需要做几个工具方便自己偷懒,加上之前的挡板,数据源肯定没法单一配置,所以需要多数据源配置。这里介绍两种配置:动态数据源和固定数据源模式。这两种我在目前的工作的工具开发中都有用到。


一、固定数据源配置

Mybatis是提供这种固定的多数据源配置的,需要分别配置包扫描(一般是不同的数据源扫描不同的包),事务处理器等。

  • yml配置,主要是不要用Spring Boot自带的数据库配置,spring.datasource,或者其他数据源配置,改用自己的,这样其实Spring boot的数据库自动配置DataSourceAutoConfiguration其实是失效了的。
## 这个是第一个固定配置
spring:
  datasource:
    druid:
      db-type: com.alibaba.druid.pool.DruidDataSource
      driver-class-name: com.ibm.db2.jcc.DB2Driver
      url: jdbc:db2://*****
      username: ****
      password: ****
      initial-size: 1
      min-idle: 1
      max-active: 1
      max-wait: 5000
      time-between-eviction-runs-millis: 60000
      min-evictable-idle-time-millis: 300000
      max-evictable-idle-time-millis: 900000
      connection-error-retry-attempts: 1
      break-after-acquire-failure: true
## 这是第二个动态数据源配置
app:
  datasource:
    mapDatasource:
      TESTDATASOURCE1:
        db-type: com.alibaba.druid.pool.DruidDataSource
        driver-class-name: com.microsoft.sqlserver.jdbc.SQLServerDriver
        url: jdbc:sqlserver://****
        username: ****
        password: ****
        initial-size: 5
        min-idle: 5
        max-active: 10
        max-wait: 5000
        time-between-eviction-runs-millis: 60000
        min-evictable-idle-time-millis: 300000
        max-evictable-idle-time-millis: 900000
        connection-error-retry-attempts: 1
        break-after-acquire-failure: true
      TESTDATASOURCE2:
        db-type: com.alibaba.druid.pool.DruidDataSource
        driver-class-name: com.microsoft.sqlserver.jdbc.SQLServerDriver
        url: jdbc:sqlserver://****
        username: ****
        password: ****
        initial-size: 5
        min-idle: 5
        max-active: 10
        max-wait: 5000
        time-between-eviction-runs-millis: 60000
        min-evictable-idle-time-millis: 300000
        max-evictable-idle-time-millis: 900000
        connection-error-retry-attempts: 1
        break-after-acquire-failure: true
      TESTDATASOURCE3:
        db-type: com.alibaba.druid.pool.DruidDataSource
        driver-class-name: com.ibm.db2.jcc.DB2Driver
        url: jdbc:db2://****
        username: ****
        password: ****
        initial-size: 5
        min-idle: 5
        max-active: 10
        max-wait: 5000
        time-between-eviction-runs-millis: 60000
        min-evictable-idle-time-millis: 300000
        max-evictable-idle-time-millis: 900000
        connection-error-retry-attempts: 1
        break-after-acquire-failure: true
      TESTDATASOURCE4:
        db-type: com.alibaba.druid.pool.DruidDataSource
        driver-class-name: com.ibm.db2.jcc.DB2Driver
        url: jdbc:db2://****
        username: ****
        password: ****
        initial-size: 5
        min-idle: 5
        max-active: 10
        max-wait: 5000
        time-between-eviction-runs-millis: 60000
        min-evictable-idle-time-millis: 300000
        max-evictable-idle-time-millis: 900000
        connection-error-retry-attempts: 1
        break-after-acquire-failure: true
  • Spring Boot配置类
@Data
@ConfigurationProperties(prefix = "app.datasource")
public class SystemDynamicDatasourceProperties {
    private Map<String, DruidDataSource> mapDatasource;
}

  • mybatis固定数据源配置
@Configuration
public class DataSourceConfiguration {


    @Configuration
    @MapperScan(basePackages = "com.test.mapper.datasource1", sqlSessionTemplateRef  = "source1SqlSessionTemplate")
    public static class source1DatasourceConfiguration {
        @Bean(name = "source1DataSource")
        @ConfigurationProperties(prefix = "spring.datasource.druid")
        public DruidDataSource source1DataSource(){
            return new DruidDataSource();
        }

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


        @Bean(name = "source1SqlSessionFactory")
        public SqlSessionFactory source1SqlSessionFactory(@Qualifier("source1DataSource") DataSource dataSource) throws Exception {
            SqlSessionFactoryBean bean = new SqlSessionFactoryBean();
            bean.setDataSource(dataSource);
            bean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources("classpath:mapper/source1/*.xml"));
            bean.setTypeAliasesPackage("com.source1.entity");
            return bean.getObject();
        }

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

    @Configuration
    @EnableConfigurationProperties(SystemDynamicDatasourceProperties.class)
    @MapperScan(basePackages = "com.source1.source1web.mapper.other", sqlSessionTemplateRef  = "otherSqlSessionTemplate")
    public static class DynamicDatasourceConfiguration {

        @Resource
        private SystemDynamicDatasourceProperties systemDynamicDatasourceProperties;
        @Bean(name = "otherDataSource")
        public SystemDynamicDatasource otherDataSource(){
            HashMap<Object, Object> map = new HashMap<>(systemDynamicDatasourceProperties.getMapDatasource());
            SystemDynamicDatasource systemDynamicDatasource = new SystemDynamicDatasource(map);
            return systemDynamicDatasource;
        }

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


        @Bean(name = "otherSqlSessionFactory")
        public SqlSessionFactory otherSqlSessionFactory(@Qualifier("otherDataSource") DataSource dataSource) throws Exception {
            SqlSessionFactoryBean bean = new SqlSessionFactoryBean();
            bean.setDataSource(dataSource);
            bean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources("classpath:mapper/other/*.xml"));
            bean.setTypeAliasesPackage("com.source1.source1web.entity");
            return bean.getObject();
        }

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

  • 说明
    这两种其实就已经是两种数据源的配置了,当使用com.test.mapper.datasource1包下的Mapper的时候,使用的是就Datasource1的数据源,包括事务管理器,当使用com.source1.source1web.mapper.other包下的Mapper的时候,就是第二种数据源。

但是,这种只适合于单数据库操作的事务,多数据库的事务属于分布式事务,不适于此,当一个数据库事务提交成功之后,另一个事务失败的话,无法回滚第一个。因为此项目只适用于查询和单数据库的插入,失败不做回滚。

二、动态数据源

其实就是在上面的基础上,上面已经配置好了数据源,和动态的配置,但是漏掉了一些配置的细节,就是动态数据源,其实Mybatis提供了动态数据源的抽象类AbstractRoutingDataSource,我们只需要继承这个类并重写determineCurrentLookupKey方法,找到相关的数据源即可。在这个配置里无论添加多少数据源都可以,动态添加也是可以的。

  • 动态数据源配置
public class SystemDynamicDatasource extends AbstractRoutingDataSource {
    private Map<Object,Object> dataSourceMap;
    public static final ThreadLocal<String> DATA_SOURCE = new ThreadLocal<>();

    public SystemDynamicDatasource(Map<Object, Object> dataSourceMap){
        this.dataSourceMap = dataSourceMap;
        super.setTargetDataSources(dataSourceMap);
        super.afterPropertiesSet();
    }

    public void setDataSource(Integer key, DataSource dataSource){
        DruidDataSource oldDataSource = (DruidDataSource) dataSourceMap.put(key, dataSource);
        if (oldDataSource != null) {
            oldDataSource.close();
        }
        afterPropertiesSet();
    }
    public void removeDataSource(String key){
        DruidDataSource oldDataSource = (DruidDataSource) dataSourceMap.remove(key);
        if (oldDataSource != null) {
            oldDataSource.close();
        }
        afterPropertiesSet();
    }
    public boolean isExist(String key){
        return dataSourceMap.get(key) != null;
    }
    @Override
    protected Object determineCurrentLookupKey() {
        return DATA_SOURCE.get();
    }
    public void setDataSource(String dataSource){
        DATA_SOURCE.set(dataSource);
    }

    public static void removeDataSource(){
        DATA_SOURCE.remove();
    }
}

说明:

  • 线上使用进入多线程环境,其实主要区别就是需要确定当前线程使用的是哪个数据源。Map里面存储的就是多数据源,其中key是每个数据源的key,当某个线程需要确定使用哪个数据源的时候,就是靠这个key来进行区分的。
  • ThreadLocal就是确定某个线程使用的是哪个key,这样保证了线程安全,不会相互影响,只要使用的时候注意remove即可。
  • determineCurrentLookupKey调用来决定哪个数据源。
  • AOP配置

    这里最好使用AOP进行统一配置,不要在代码里写,在代码里写既添加了大量重复代码,而且与业务相关,代码可读性差,最好做成AOP统一配置。

    代码如下:

    • 注解
      @Target({ElementType.TYPE, ElementType.METHOD})
      public @interface OtherDatasource {
          String value() default "";
      }
      

      可以通过这个注解来充当切点,但是本次使用仅作为额外数据,获取指定的数据源使用。

    • 切面
    @Aspect
    @Component
    @Slf4j
    public class OtherDataSourceAspect {
    
        @Autowired
        private SystemDynamicDatasource systemDynamicDatasource;
    
    //     @Pointcut("@annotation(com.ibank.im.app.aop.cache.annotation.SystemCacheable)")
    
        @Pointcut("execution(public * com.test.mapper.other.*.*(..))")
        public void pointcut(){}
    
        @Around("pointcut()")
        public Object systemCacheableAround(ProceedingJoinPoint joinPoint) throws Throwable {
            Class<?> targetCls=joinPoint.getTarget().getClass();
    
            OtherDatasource annotation = targetCls.getAnnotation(OtherDatasource.class);
            String datasource = null;
            if (Objects.isNull(annotation) || !StringUtils.hasText(datasource = annotation.value())) {
                MethodSignature methodSignature=(MethodSignature)joinPoint.getSignature();
                Method targetMethod=
                        targetCls.getDeclaredMethod(
                                methodSignature.getName(),
                                methodSignature.getParameterTypes());
                OtherDatasource methodAnnotation = targetMethod.getAnnotation(OtherDatasource.class);
    
                if (Objects.isNull(methodAnnotation) || !StringUtils.hasText(datasource = methodAnnotation.value())) {
                    Object[] args = joinPoint.getArgs();
                    if (Arrays.isNullOrEmpty(args)) {
                        throw new IllegalArgumentException("must have 1 param");
                    }
                    if (!(args[0] instanceof String)) {
                        throw new IllegalArgumentException("the first param must be databaseEnv");
                    }
                    datasource = (String) args[0];
                }
            }
    
            if (!systemDynamicDatasource.isExist(datasource)) {
                throw new IllegalArgumentException("databaseEnv does not exist");
            }
            try{
                systemDynamicDatasource.setDataSource(datasource);
                return joinPoint.proceed();
            }finally {
                systemDynamicDatasource.removeDataSource(datasource);
            }
        }
    }
    

说明:

  • 本次数据源直接在Mapper层使用,不在Service层使用,因为一个Service可能要使用多个不同的数据源操作,比较麻烦,直接作用在Mapper层比较合适。
  • 逻辑上,先判断这个类上有没有注解,有的话使用这个注解,如果没有在使用方法上的注解,方法上如果没有注解,就是用第一个String参数,在没有就会报错,在判断是否存在这个数据源。不存在直接报错。
  • 使用的时候,一定要用try包裹,使用完成必须remove掉当前的值,无论是否发生异常。不移除的话容易发生内存溢出等问题。
  • 切面执行方法就是ProceedingJoinPoint类的proceed方法,但是实际上这个方法有两个重载的函数,一个带参数一个不带参数,这里简要介绍一下:
    • 不带参数的:表示调用时传递什么参数,就是什么参数,Advice不干预,原样传递。因为本次过程不修改什么参数。所以使用的是这个
    • 带参数的:自然就是相反的,将替换掉调用时传递的参数,这时候方法里调用的就是切面里的参数。

因为目前业务需求问题,都是使用的参数进行传递,所以只能定义在方法参数上。像这个样子:

@Mapper
public interface TestMapper {

    int insertTest(String env, Entity entity);

}

第一个参数就决定是哪个数据源,但是实际上业务并不采用。因为无法固定使用某个数据源的问题,只能以参数的方式传递。文章来源地址https://www.toymoban.com/news/detail-817237.html


搞定收工!

到了这里,关于Spring Boot整合Mybatis配置多数据源的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • Spring Boot + MyBatis-Plus 实现 MySQL 主从复制动态数据源切换

    MySQL 主从复制是一种常见的数据库架构,它可以提高数据库的性能和可用性。 动态数据源切换则可以根据业务需求,在不同场景下使用不同的数据源,比如在读多写少的场景下,可以通过切换到从库来分担主库的压力 。 在本文中,我们将介绍如何在 Spring Boot 中实现 MySQL 动

    2024年02月19日
    浏览(59)
  • Spring Boot 配置双数据源

    Survive by day and develop by night. talk for import biz , show your perfect code,full busy,skip hardness,make a better result,wait for change,challenge Survive. happy for hardess to solve denpendies. 需求: 1.基本步骤 添加依赖 添加 Spring Boot 和数据库驱动的依赖 配置数据源 在 application.properties 或 application.yml 中分别配

    2024年01月22日
    浏览(61)
  • Spring Boot入门(07):整合 MySQL 和 Druid数据源 | 全网最详细保姆级教学(两万字)

            作为现代Web应用开发的重要技术栈之一,Spring Boot在快速构建可靠、高效、易维护的应用方面具有独特的优势。而在实际开发中,数据库作为系统的重要组成部分,对于数据源的选择和配置也是至关重要的。本篇文章将全面介绍如何使用Spring Boot整合MySQL和Druid数据

    2024年02月12日
    浏览(50)
  • Spring Boot配置多个Kafka数据源

    application.properties配置文件如下 1.第一个kakfa 2.第二个kakfa 备注: 生产者消费者代码参考链接,开发同学需要以实际情况按要求自己变更下代码即可: Spring Boot 集成多个 Kafka_springboot集成多个kafka_//承续缘_纪录片的博客-CSDN博客

    2024年02月07日
    浏览(73)
  • 【Java】Spring Boot配置动态数据源

    1.1 创建动态数据源 通过实现Spring提供的AbstractRoutingDataSource类,可以实现自己的数据源选择逻辑,从而可以实现数据源的动态切换。 1.2 创建动态数据源配置类 跟配置静态多数据源一样,需要手动配置下面的三个 Bean,只不过DynamicDataSource类的targetDataSources是空的。 1.3 创建动

    2024年02月09日
    浏览(51)
  • 使用mybatis和dynamic-datasource-spring-boot-starter动态切换数据源操作数据库

    记录 :415 场景 :使用mybatis和dynamic-datasource-spring-boot-starter动态切换数据源操作数据库。 版本 :JDK 1.8,Spring Boot 2.6.3,dynamic-datasource-spring-boot-starter-3.3.2,mybatis-3.5.9。 源码 :https://github.com/baomidou/dynamic-datasource-spring-boot-starter dynamic-datasource-spring-boot-starter :一个基于springboot的快

    2023年04月19日
    浏览(44)
  • 如何在Spring Boot中配置双数据源?

    在许多应用程序中, 可能会遇到需要连接多个数据库的情况 。这些数据库可以是不同的类型,例如关系型数据库和NoSQL数据库,或者它们可以是相同类型但包含不同的数据。为了处理这种情况,我们可以使用双数据源来管理多个数据库连接。 双数据源是指在一个应用程序中

    2024年02月11日
    浏览(47)
  • 使用dynamic-datasource-spring-boot-starter动态切换数据源操作数据库(MyBatis-3.5.9)

    记录 :383 场景 :使用dynamic-datasource-spring-boot-starter动态切换数据源,使用MyBatis操作数据库。提供三种示例:一,使用@DS注解作用到类上。二,使用@DS注解作用到方法上。三,不使用注解,使用DynamicDataSourceContextHolder类在方法内灵活切换不同数据源。 源码: https://github.com/

    2024年01月20日
    浏览(51)
  • Spring Boot多数据源配置详解及报错问题解决

    Spring Boot多数据源配置详解 前几天,公司提了个需求,对一个项目进行二次开发,在开发过程中,需要配置多数据源来进行数据库的操作。下面我将主键探索总结的配置流程和遇到的各种坑做以总结,希望能够帮到遇到同样问题的友友。有错的地方,请各位大佬留言指出。

    2024年02月16日
    浏览(51)
  • spring boot下基于spring data jpa配置mysql+达梦多数据源(以不同包路径方式,mysql为主数据源)

    :mysql 达梦/dameng jpa 多数据源 spring boot:2.1.17.RELEASE mysql驱动:8.0.21(跟随boot版本) 达梦驱动:8.1.2.192 lombok:1.18.12(跟随boot版本) 以mysql为主数据源,达梦为第二数据源方式配置 适用于旧项目二次开发接入达梦数据库或基于通用二方/三方包做业务扩展等场景 将以不

    2024年02月05日
    浏览(60)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包