您的当前位置:首页正文

记录 springboot + mybatis 项目如何配置双数据源

2024-11-29 来源:个人技术集锦


前言

有时一个web项目中需要同时访问两个数据库,比如读写分离、分库、主从库分离等,这时,常规的springboot单数据源就无法满足需求,需要对配置进行调整。本文记录了实际工作中配置双数据源时遇到的一些问题和解决思路。

觉得不错的同学可以加我公众号,会经常分享一些技术干货,以及热点AI和科技新闻


一、项目及需求简介

项目框架:springboot + mybatis-plus
需求:需要对一些数据做大量的计算和统计,定时任务触发,统计比较耗时

二、修改双数据源实现

2.1.主要依赖

我项目中用的Druid连接池,需要单独引入依赖
Hikari是springboot2.X以后的默认数据源连接池,不需要引入任何依赖。

	<parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.3.12.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    
    <mybatisplus-boot.version>3.4.2</mybatisplus-boot.version>
    <druid-boot.version>1.2.5</druid-boot.version>
    <mybaits.version>3.5.7</mybaits.version>
    
    <dependency>
         <groupId>com.baomidou</groupId>
         <artifactId>mybatis-plus-boot-starter</artifactId>
         <version>${mybatisplus-boot.version}</version>
     </dependency>
     <dependency>
       <groupId>com.alibaba</groupId>
         <artifactId>druid-spring-boot-starter</artifactId>
         <version>${druid-boot.version}</version>
     </dependency>
     <dependency>
         <groupId>org.mybatis</groupId>
         <artifactId>mybatis</artifactId>
         <version>${mybaits.version}</version>
     </dependency>

2.2.配置文件

数据库连接池可能是Hikari或者Druid,下面给出两种不同的配置方式示例:

Hikari:

spring:
  datasource:
    db01:
      jdbc-url: jdbc:dm://192.168.124.221:5236
      username: panda
      password: 12345678
      driver-class-name: dm.jdbc.driver.DmDriver
      type: com.zaxxer.hikari.HikariDataSource
      #最大连接数,小于等于0会被重置为默认值10;大于零小于1会被重置为minimum-idle的值
      maximum-pool-size: 50
      #最小空闲连接,默认值 10,小于0或大于maximum-pool-size,都会重置为maximum-pool-size
      minimum-idle: 20
      #连接超时时间:毫秒,小于250毫秒,否则被重置为默认值30秒
      connection-timeout: 60000
      #空闲连接超时时间,默认值600000ms(10分钟),大于等于max-lifetime且max-lifetime>0,会被重置为0;
      #不等于0且小于10秒,会被重置为10秒。
      #只有空闲连接数大于最大连接数且空闲时间超过该值,才会被释放(自动释放过期链接)
      idle-timeout: 600000
      #连接最大存活时间.不等于0且小于30秒,会被重置为默认值30分钟.设置应该比mysql设置的超时时间短
      max-lifetime: 640000
      #连接测试查询
      connection-test-query: SELECT 1 from dual
    db02:
      jdbc-url: jdbc:mysql://192.168.124.221:3306/panda?useSSl=true&useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai
      username: user01
      password: 12345678
      driver-class-name: com.mysql.cj.jdbc.Driver
      type: com.zaxxer.hikari.HikariDataSource
      maximum-pool-size: 10
      minimum-idle: 3
      connection-timeout: 60000
      idle-timeout: 600000
      max-lifetime: 640000
      connection-test-query: SELECT 1 from dual


Druid:

spring:
  datasource:
    db01:
      url: jdbc:dm://192.168.124.221:5236
      username: panda
      password: 12345678
      driver-class-name: dm.jdbc.driver.DmDriver
      type: com.alibaba.druid.pool.DruidDataSource
      initial-size: 5  # 初始化大小
      min-idle: 5  # 最小
      max-active: 100  # 最大
      max-wait: 60000  # 配置获取连接等待超时的时间
      validation-query: select 1 from dual
      time-between-eviction-runs-millis: 60000  # 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒
      min-evictable-idle-time-millis: 300000  # 指定一个空闲连接最少空闲多久后可被清除,单位是毫秒
      filters: config,stat  # 配置监控统计拦截的filters,去掉后监控界面sql无法统计,'wall'用于防火墙
      # 通过connectProperties属性来打开mergeSql功能;慢SQL记录
      connectionProperties: druid.stat.slowSqlMillis=200;druid.stat.logSlowSql=true;config.decrypt=false
      test-while-idle: true
      test-on-borrow: true
      test-on-return: false
      pool-prepared-statements: true # 是否缓存preparedStatement,也就是PSCache  官方建议MySQL下建议关闭   个人建议如果想用SQL防火墙 建议打开
      max-pool-prepared-statement-per-connection-size: 20
    db02:
      url: jdbc:mysql://192.168.124.221:3306/panda?useSSl=true&useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai
      username: user01
      password: 12345678
      driver-class-name: com.mysql.cj.jdbc.Driver
      type: com.alibaba.druid.pool.DruidDataSource
      initial-size: 5
      min-idle: 5
      max-active: 100
      max-wait: 60000
      validation-query: select 1 from dual
      time-between-eviction-runs-millis: 60000
      min-evictable-idle-time-millis: 300000
      filters: config,stat,wall
      connectionProperties: druid.stat.slowSqlMillis=200;druid.stat.logSlowSql=true;config.decrypt=false
      test-while-idle: true
      test-on-borrow: true
      test-on-return: false
      pool-prepared-statements: true
      max-pool-prepared-statement-per-connection-size: 20
    druid:
      web-stat-filter:
        enabled: true
        url-pattern: /*
        exclusions: /druid/*,*.js,*.gif,*.jpg,*.bmp,*.png,*.css,*.ico
        session-stat-enable: true
        session-stat-max-count: 10
      stat-view-servlet:
        enabled: true
        url-pattern: /druid/*
        reset-enable: true
        login-username: admin
        login-password: password
        allow: 192.168.18.5,192.168.18.9



2.3 config 配置类

import com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceBuilder;
import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionFactoryBean;
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 javax.sql.DataSource;

@Configuration
@MapperScan(basePackages = "mapper.db01", sqlSessionFactoryRef = "db01SqlSessionFactory")
public class DataSourceConfigDB01 {

    @Primary
    @Bean("db01DataSource")
    @ConfigurationProperties(prefix = "spring.datasource.db01")
    public DataSource getDB01DataSource(){
        //使用Druid连接池时,要用专门的DataSourceBuilder:
        return DruidDataSourceBuilder.create().build();
		//使用默认的Hikari连接池时,用默认的DataSourceBuilder:
        return DataSourceBuilder.create().build();

		// 这里也可以使用以下方式自己注入属性
    	DruidDataSource datasource = new DruidDataSource();
        datasource.setUrl(url);
        datasource.setUsername(username);
        return datasource;
    }

    @Primary
    @Bean("db01SqlSessionFactory")
    public SqlSessionFactory db01SqlSessionFactory(@Qualifier("db01DataSource") DataSource dataSource) throws Exception {
        SqlSessionFactoryBean bean = new SqlSessionFactoryBean();
        bean.setDataSource(dataSource);
        bean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources("classpath*:mapper/db01/*.xml"));
        return bean.getObject();
    }

    @Primary
    @Bean("db01SqlSessionTemplate")
    public SqlSessionTemplate db01SqlSessionTemplate(@Qualifier("db01SqlSessionFactory") SqlSessionFactory sqlSessionFactory){
        return new SqlSessionTemplate(sqlSessionFactory);
    }

}

import com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceBuilder;
import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionFactoryBean;
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.boot.jdbc.DataSourceBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;

import javax.sql.DataSource;

@Configuration
@MapperScan(basePackages = "mapper.db02", sqlSessionFactoryRef = "db02SqlSessionFactory")
public class DataSourceConfigDB02 {

    @Bean("db02DataSource")
    @ConfigurationProperties(prefix = "spring.datasource.db02")
    public DataSource getDB02DataSource(){ 	
        //使用Druid连接池时,要用专门的DataSourceBuilder:
        return DruidDataSourceBuilder.create().build();
		//使用默认的Hikari连接池时,用默认的DataSourceBuilder:
        return DataSourceBuilder.create().build();

		// 这里也可以使用以下方式自己注入属性
    	DruidDataSource datasource = new DruidDataSource();
        datasource.setUrl(url);
        datasource.setUsername(username);
        return datasource;
 		、、、  
    }

    @Bean("db02SqlSessionFactory")
    public SqlSessionFactory db02SqlSessionFactory(@Qualifier("db02DataSource") DataSource dataSource) throws Exception {
        SqlSessionFactoryBean bean = new SqlSessionFactoryBean();
        bean.setDataSource(dataSource);
        bean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources("classpath*:mapper/db02/*.xml"));
        return bean.getObject();
    }

    @Bean("db02SqlSessionTemplate")
    public SqlSessionTemplate db02SqlSessionTemplate(@Qualifier("db02SqlSessionFactory") SqlSessionFactory sqlSessionFactory){
        return new SqlSessionTemplate(sqlSessionFactory);
    }
    
}

2.4 其他

mapper、service、controller 的使用和但数据源没有区别

三、注意事项

3.1 Mybatis & Mybatis-plus

//在配置SqlSessionFactory时需要注意
//如果使用的是Mybatis-plus
MybatisSqlSessionFactoryBean sqlSessionFactory = new MybatisSqlSessionFactoryBean();
//如果使用的是Mybatis
SqlSessionFactoryBean factoryBean = new SqlSessionFactoryBean();

3.2 @Primary

@Primary注解可以标注出有多个候选者有资格自动装配单值依赖项时,优先考虑的Bean对象,如果没有或者标了多个都会出现错误

3.3 扫描路径

这两个扫描路径要根据自己实际情况更换

@MapperScan(basePackages = {"com.mapper.master"})

sqlSessionFactory.setMapperLocations(new PathMatchingResourcePatternResolver()
                .getResources("classpath:**/*.xml"));

希望这篇文章能帮助到大家

显示全文