赞
踩
有时一个web项目中需要同时访问两个数据库,比如读写分离、分库、主从库分离等,这时,常规的springboot单数据源就无法满足需求,需要对配置进行调整。本文记录了实际工作中配置双数据源时遇到的一些问题和解决思路。
觉得不错的同学可以加我公众号,会经常分享一些技术干货,以及热点AI和科技新闻
项目框架:springboot + mybatis-plus
需求:需要对一些数据做大量的计算和统计,定时任务触发,统计比较耗时
我项目中用的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>
数据库连接池可能是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
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); } }
mapper、service、controller 的使用和但数据源没有区别
//在配置SqlSessionFactory时需要注意
//如果使用的是Mybatis-plus
MybatisSqlSessionFactoryBean sqlSessionFactory = new MybatisSqlSessionFactoryBean();
//如果使用的是Mybatis
SqlSessionFactoryBean factoryBean = new SqlSessionFactoryBean();
@Primary注解可以标注出有多个候选者有资格自动装配单值依赖项时,优先考虑的Bean对象,如果没有或者标了多个都会出现错误
这两个扫描路径要根据自己实际情况更换
@MapperScan(basePackages = {"com.mapper.master"})
sqlSessionFactory.setMapperLocations(new PathMatchingResourcePatternResolver()
.getResources("classpath:**/*.xml"));
希望这篇文章能帮助到大家
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。