多数据源 - 不同Mapper域配不同数据源
编辑前言
有些时候一个项目里需要配备多个数据源,做主从,做读写,或者分离不同业务库等等,实现的方式也有多种,一般都有注解切换数据源,或者在代码行里手动切换数据源上下文环境,这两种方式可以说殊途同归,都是想办法把当前数据源设置到ThreadLocal里,再执行数据库操作的时候执行线程从ThreadLocal中取数据源。
此篇文章的需求来源亦是分离不同的业务库,但与上述做法稍微有点不一样,本文没有做注解以提供在业务代码里灵活切换,而是项目初始化时就配置好各个mapper作用域的数据源,不同的mapper域调不同的数据源,互相之间互不干扰也无法切换,因此,在写业务代码时也可以灵活切换数据源,只是在写mapper操作数据库的时候要注意,各就各位,不要乱串SQL和数据源的对应关系。
思路
基本思路是,有多少个数据库即配多少个数据源,每个数据源去扫描各自mapper包,即限制不同数据源作用的mapper域不一样。
在写SQL的时候,是哪个库的表就要写到对应的mapper域,否则就是抛出表不存在异常。此外,多数据源之间的事务也是分布式事务,这意味着需要引入一套分布式事务处理方案,如果你的项目对事务要求严格,但此文没有对分布式事务的介绍。
配置
这是我的数据库配置参数,配在datasource.properties
文件中
# 连接池
hikari.maximum-pool-size=20
hikari.minimum-idle=0
hikari.connection-timeout=60000
hikari.idle-timeout=600000
hikari.auto-commit=true
# 用户库
user.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
user.datasource.url=jdbc:mysql://192.168.73.60:3306/multiple_db_1?useUnicode=true&characterEncoding=UTF-8&serverTimezone=CTT
user.datasource.username=root
user.datasource.password=root
user.datasource.pool-name=hikari-user
# 订单库
order.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
order.datasource.url=jdbc:mysql://192.168.73.60:3306/multiple_db_2?useUnicode=true&characterEncoding=UTF-8&serverTimezone=CTT
order.datasource.username=root
order.datasource.password=root
order.datasource.pool-name=hikari-order
# 支付库
pay.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
pay.datasource.url=jdbc:mysql://192.168.73.60:3306/multiple_db_3?useUnicode=true&characterEncoding=UTF-8&serverTimezone=CTT
pay.datasource.username=root
pay.datasource.password=root
pay.datasource.pool-name=hikari-pay
# 物流库
logistics.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
logistics.datasource.url=jdbc:mysql://192.168.73.60:3306/multiple_db_4?useUnicode=true&characterEncoding=UTF-8&serverTimezone=CTT
logistics.datasource.username=root
logistics.datasource.password=root
logistics.datasource.pool-name=hikari-logistics
初始化配置 - 包结构和配置文件说明
HikariCPProperties.java
- 连接池配置参数
LogisticsDBProperties.java
- 物流库配置参数
LogisticsDataSourceConfig.java
- 物流数据源配置
OrderDBProperties.java
- 订单库配置参数
OrderDataSourceConfig.java
- 订单数据源配置
PayDBProperties.java
- 支付库配置参数
PayDataSourceConfig.java
- 支付数据源配置
UserDBProperties.java
- 用户库配置参数
UserDataSourceConfig.java
- 用户数据源配置(默认数据源)
其中,类名以Properties
结尾的类都是简单读上述配置文件中相关模块的内容,如HikariCPProperties
读取连接池配置
package online.heycm.multipledb.config;
import lombok.Data;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.PropertySource;
import org.springframework.stereotype.Component;
/**
* hikari连接池参数
*/
@Component
@PropertySource("classpath:datasource.properties")
@Data
public class HikariCPProperties {
@Value("${hikari.maximum-pool-size}")
private int maximumPoolSize;
@Value("${hikari.minimum-idle}")
private int minimumIdle;
@Value("${hikari.connection-timeout}")
private long connectionTimeout;
@Value("${hikari.idle-timeout}")
private long idleTimeout;
@Value("${hikari.auto-commit}")
private Boolean autoCommit;
}
类名以DataSourceConfig
结尾的类,即各个数据源的初始化配置了,以UserDataSourceConfig
为例
package online.heycm.multipledb.config;
import com.baomidou.mybatisplus.core.MybatisConfiguration;
import com.zaxxer.hikari.HikariDataSource;
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.logging.stdout.StdOutImpl;
import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
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;
/**
* 用户模块数据源(默认)
*/
@Slf4j
@Configuration
@MapperScan(basePackages = {UserDataSourceConfig.MAPPER_PACKAGES}, sqlSessionFactoryRef = UserDataSourceConfig.SQL_SESSION_FACTORY)
public class UserDataSourceConfig {
static final String MAPPER_PACKAGES = "online.heycm.multipledb.domain.user.mapper";
static final String MAPPER_LOCATIONS = "classpath:/mapper/user/*.xml";
static final String DATA_SOURCE = "userDataSource";
static final String TRANSACTION_MANAGER = "userTransactionManager";
static final String SQL_SESSION_FACTORY = "userSqlSessionFactory";
@Autowired
private HikariCPProperties hikariCPProperties;
@Autowired
private UserDBProperties dbProperties;
@Bean(DATA_SOURCE)
@Primary
public DataSource dataSource() {
log.info("init DataSource: [{}]...", DATA_SOURCE);
HikariDataSource ds = new HikariDataSource();
ds.setDriverClassName(dbProperties.getDriverClassName());
ds.setJdbcUrl(dbProperties.getUrl());
ds.setUsername(dbProperties.getUsername());
ds.setPassword(dbProperties.getPassword());
ds.setPoolName(dbProperties.getPoolName());
ds.setMaximumPoolSize(hikariCPProperties.getMaximumPoolSize());
ds.setMinimumIdle(hikariCPProperties.getMinimumIdle());
ds.setConnectionTimeout(hikariCPProperties.getConnectionTimeout());
ds.setIdleTimeout(hikariCPProperties.getIdleTimeout());
ds.setAutoCommit(hikariCPProperties.getAutoCommit());
log.info("init DataSource: [{}] success.", DATA_SOURCE);
return ds;
}
@Bean(TRANSACTION_MANAGER)
@Primary
public DataSourceTransactionManager transactionManager(@Qualifier(DATA_SOURCE) DataSource ds) {
log.info("init TransactionManager: [{}] success.", TRANSACTION_MANAGER);
return new DataSourceTransactionManager(ds);
}
@Bean(SQL_SESSION_FACTORY)
@Primary
public SqlSessionFactory sqlSessionFactory(@Qualifier(DATA_SOURCE) DataSource ds) throws Exception {
log.info("init SqlSessionFactory: [{}]...", SQL_SESSION_FACTORY);
SqlSessionFactoryBean factoryBean = new SqlSessionFactoryBean();
factoryBean.setDataSource(ds);
factoryBean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources(MAPPER_LOCATIONS));
// mybatis配置
MybatisConfiguration configuration = new MybatisConfiguration();
configuration.setMapUnderscoreToCamelCase(true);
configuration.setLogImpl(StdOutImpl.class);
factoryBean.setConfiguration(configuration);
log.info("init SqlSessionFactory: [{}] success.", SQL_SESSION_FACTORY);
return factoryBean.getObject();
}
}
MAPPER_PACKAGES
- 指定扫描的mapper包,即为此数据源的作用域,在这个mapper域下的所有数据库操作全部指向此数据源
MAPPER_LOCATIONS
- 指定扫描 MAPPER_PACKAGES 对应的XML文件
DATA_SOURCE
- 数据源实例名称
TRANSACTION_MANAGER
- 事务管理器实例名称
SQL_SESSION_FACTORY
- Session工厂实例名称
每个数据源都有配置:
数据源实例 - DataSource
事务管理器实例 - DataSourceTransactionManager
Session工厂实例 - SqlSessionFactory
此外,SpringBoot 需要一个数据源作为默认数据源,这里指定用户数据源为默认数据源,因此需要特别指定 @Primary
,其他数据源配置大同小异,只需要扫描不同包下的mapper和mapper xml即可。
至此,不同mapper域的数据源配置就已经配好,可以在service的任何位置调用任意mapper,mapper根据配置访问各自数据源,再啰嗦一句,这里是无法统一协调各数据源的事务管理的,如果对事务要求较高,需要另行处理。
仓库地址
- 3
- 0
-
分享