如何使用 Spring Boot 该如何处理多个数据库的读写,一般有以下几种策略:
第一种策略:
第二种策略:
本文主讲:基于SpringBoot + MyBatis + Oracle + Druid环境 ,实现多套数据源
第一步:多套数据源构建,基于application.properties文件
package com.zzg.common.datasource;
import javax.sql.DataSource;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import com.alibaba.druid.pool.DruidDataSource;
/**
* 多套数据源配置
*
* @author zzg
*
*/
@Configuration
public class DataSourceConfig {
// 日志记录
public static final Logger log = LoggerFactory.getLogger(DataSourceConfig.class);
/**
* 下面的配置信息可以读取配置文件,其实可以直接写死 如果是多数据源的话 还是考虑读取配置文件
*/
@Value("${spring.datasource.initialSize}")
private int initialSize;
@Value("${spring.datasource.minIdle}")
private int minIdle;
@Value("${spring.datasource.maxActive}")
private int maxActive;
@Value("${spring.datasource.maxWait}")
private int maxWait;
@Value("${spring.datasource.timeBetweenEvictionRunsMillis}")
private int timeBetweenEvictionRunsMillis;
@Value("${spring.datasource.minEvictableIdleTimeMillis}")
private int minEvictableIdleTimeMillis;
@Value("${spring.datasource.validationQuery}")
private String validationQuery;
@Value("${spring.datasource.testWhileIdle}")
private boolean testWhileIdle;
@Value("${spring.datasource.testOnBorrow}")
private boolean testOnBorrow;
@Value("${spring.datasource.testOnReturn}")
private boolean testOnReturn;
@Value("${spring.datasource.poolPreparedStatements}")
private boolean poolPreparedStatements;
@Value("${spring.datasource.maxPoolPreparedStatementPerConnectionSize}")
private int maxPoolPreparedStatementPerConnectionSize;
@Value("{spring.datasource.connectionProperties}")
private String connectionProperties;
@Value("${spring.datasource.filters}")
private String filters;
@Primary
@Bean("outDataSource")
public DataSource outDataSource() {
// 多数据源创建DataSource 对象,并指定数据库连接池,我这里使用AliBaBa Druid 数据库连接池
DruidDataSource druidDataSource = new DruidDataSource();
try {
// oralce 数据库账户密码基础配置
druidDataSource.setUsername("erms_zhx");
druidDataSource.setPassword("erms_zhx");
druidDataSource.setUrl("jdbc:oracle:thin:@192.168.1.150:1521:orcl");
druidDataSource.setDriverClassName("oracle.jdbc.OracleDriver");
// Druid 数据库连接池具体配置
// 具体配置
druidDataSource.setInitialSize(initialSize);
druidDataSource.setMinIdle(minIdle);
druidDataSource.setMaxActive(maxActive);
druidDataSource.setMaxWait(maxWait);
druidDataSource.setTimeBetweenEvictionRunsMillis(timeBetweenEvictionRunsMillis);
druidDataSource.setMinEvictableIdleTimeMillis(minEvictableIdleTimeMillis);
druidDataSource.setValidationQuery(validationQuery);
druidDataSource.setTestWhileIdle(testWhileIdle);
druidDataSource.setTestOnBorrow(testOnBorrow);
druidDataSource.setTestOnReturn(testOnReturn);
druidDataSource.setPoolPreparedStatements(poolPreparedStatements);
druidDataSource.setMaxPoolPreparedStatementPerConnectionSize(maxPoolPreparedStatementPerConnectionSize);
/**
* 这个是用来配置 druid 监控sql语句的 非常有用 如果你有两个数据源 这个配置哪个数据源就监控哪个数据源的sql
* 同时配置那就都监控
*/
druidDataSource.setFilters(filters);
druidDataSource.setConnectionProperties(connectionProperties);
} catch (Exception e) {
log.error(e.getMessage());
e.printStackTrace();
}
return druidDataSource;
}
@Bean("inDataSource")
public DataSource inDataSource() {
// 多数据源创建DataSource 对象,并指定数据库连接池,我这里使用AliBaBa Druid 数据库连接池
DruidDataSource druidDataSource = new DruidDataSource();
try {
// oralce 数据库账户密码基础配置
druidDataSource.setUsername("erms_zh_online");
druidDataSource.setPassword("erms_zh_online");
druidDataSource.setUrl("jdbc:oracle:thin:@192.168.1.150:1521:orcl");
druidDataSource.setDriverClassName("oracle.jdbc.OracleDriver");
// Druid 数据库连接池具体配置
// 具体配置
druidDataSource.setInitialSize(initialSize);
druidDataSource.setMinIdle(minIdle);
druidDataSource.setMaxActive(maxActive);
druidDataSource.setMaxWait(maxWait);
druidDataSource.setTimeBetweenEvictionRunsMillis(timeBetweenEvictionRunsMillis);
druidDataSource.setMinEvictableIdleTimeMillis(minEvictableIdleTimeMillis);
druidDataSource.setValidationQuery(validationQuery);
druidDataSource.setTestWhileIdle(testWhileIdle);
druidDataSource.setTestOnBorrow(testOnBorrow);
druidDataSource.setTestOnReturn(testOnReturn);
druidDataSource.setPoolPreparedStatements(poolPreparedStatements);
druidDataSource.setMaxPoolPreparedStatementPerConnectionSize(maxPoolPreparedStatementPerConnectionSize);
/**
* 这个是用来配置 druid 监控sql语句的 非常有用 如果你有两个数据源 这个配置哪个数据源就监控哪个数据源的sql
* 同时配置那就都监控
*/
druidDataSource.setFilters(filters);
druidDataSource.setConnectionProperties(connectionProperties);
} catch (Exception e) {
log.error(e.getMessage());
e.printStackTrace();
}
return druidDataSource;
}
}
application.properties 涉及Druid 数据库连接池配置:
# 自定义Druid 配置
# 初始化时建立物理连接的个数
spring.datasource.initialSize=10
# 最大连接池数量
spring.datasource.maxActive=30
# 最小连接池数量
spring.datasource.minIdle=5
# 获取连接时最大等待时间,单位毫秒
spring.datasource.maxWait=60000
# 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒
spring.datasource.timeBetweenEvictionRunsMillis=60000
# 连接保持空闲而不被驱逐的最小时间
spring.datasource.minEvictableIdleTimeMillis=300000
# 用来检测连接是否有效的sql,要求是一个查询语句
spring.datasource.validationQuery=SELECT 1 FROM DUAL
# 建议配置为true,不影响性能,并且保证安全性。申请连接的时候检测,如果空闲时间大于timeBetweenEvictionRunsMillis,执行validationQuery检测连接是否有效。
spring.datasource.testWhileIdle=true
# 申请连接时执行validationQuery检测连接是否有效,做了这个配置会降低性能。
spring.datasource.testOnBorrow=false
# 归还连接时执行validationQuery检测连接是否有效,做了这个配置会降低性能。
spring.datasource.testOnReturn=false
# 是否缓存preparedStatement,也就是PSCache。PSCache对支持游标的数据库性能提升巨大,比如说oracle。在mysql下建议关闭。
spring.datasource.poolPreparedStatements=true
# 要启用PSCache,必须配置大于0,当大于0时,poolPreparedStatements自动触发修改为true。
spring.datasource.maxPoolPreparedStatementPerConnectionSize=50
# 通过connectProperties属性来打开mergeSql功能;慢SQL记录
spring.datasource.connectionProperties=druid.stat.mergeSql=true;druid.stat.slowSqlMillis=5000
# 配置sql 注入方式
spring.datasource.filters=stat,slf4j
第二步:实列化多套数据源的SqlSessionFactory
对象
说明:每个数据库使用独立的一套数据库连接,数据库连接使用的 SqlSession
进行会话连接,SqlSession
是由SqlSessionFactory
生成。因此,需要分别配置SqlSessionFactory
。
package com.zzg.common.datasource;
import javax.sql.DataSource;
import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
/**
* 迁入库数据库配置
* @author zzg
*
*/
@Configuration
@MapperScan(basePackages="com.zzg.mapper.in",sqlSessionFactoryRef="inSqlSessionFactory")
public class InMyBatisConfig {
@Bean("inSqlSessionFactory")
public SqlSessionFactory outSqlSessionFactory(@Qualifier(value="inDataSource") DataSource inDataSource) throws Exception{
// 数据源设置
SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
sqlSessionFactoryBean.setDataSource(inDataSource);
//mapper的xml文件位置
PathMatchingResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
String locationPattern = "classpath*:in-mapper/*.xml";
sqlSessionFactoryBean.setMapperLocations(resolver.getResources(locationPattern));
//对应数据库的entity位置
String typeAliasesPackage = "com.zzg.entity.in";
sqlSessionFactoryBean.setTypeAliasesPackage(typeAliasesPackage);
return sqlSessionFactoryBean.getObject();
}
}
package com.zzg.common.datasource;
import javax.sql.DataSource;
import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.annotation.MapperScan;
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;
/**
* 迁出库的数据库配置
* @author zzg
*
*/
@Configuration
@MapperScan(basePackages="com.zzg.mapper.out",sqlSessionFactoryRef="outSqlSessionFactory")
public class OutMyBatisConfig {
@Primary
@Bean("outSqlSessionFactory")
public SqlSessionFactory outSqlSessionFactory(@Qualifier(value="outDataSource") DataSource outDataSource) throws Exception{
// 数据源设置
SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
sqlSessionFactoryBean.setDataSource(outDataSource);
//mapper的xml文件位置
PathMatchingResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
String locationPattern = "classpath*:out-mapper/*.xml";
sqlSessionFactoryBean.setMapperLocations(resolver.getResources(locationPattern));
//对应数据库的entity位置
String typeAliasesPackage = "com.zzg.entity.out";
sqlSessionFactoryBean.setTypeAliasesPackage(typeAliasesPackage);
return sqlSessionFactoryBean.getObject();
}
}
第三步:多套实体对象、多套Mapper、多套xml 配置
1、在 entity
包下分别设置 in和 out 包,存放两个库对应的表实体。
2、在 mapper
包下,分别添加 in和 out包,存放两个库对应的 Mapper 。
3、在 resourcez资源包下,分别添加 in-mapper和 out-mapper文件夹,存放两个库对应的 xml 。
功能展示:
遇到的问题:
Parameter 0 of method outSqlSessionFactory in com.zzg.common.datasource.OutMyBatisConfig required a single bean, but 2 were found:
- out: defined by method 'outDataSource' in class path resource [com/zzg/common/datasource/DataSourceConfig.class]
- in: defined by method 'inDataSource' in class path resource [com/zzg/common/datasource/DataSourceConfig.class]
报错的大致意思是:在OutMyBatisConfig 数据库配置对象中,DataSource 接口存在两个实例化的接口对象分别是outDataSource 和inDataSource ,应用系统不知道使用那个接口实例化对象。
解决办法:第一步:在DataSourceConfig.java 指定outDataSource对象为@Primary
第二步:针对多个数据源实例时,需要限定各个数据源实例化对象的DataSource ,通过使用@Qualifier 标签实现
第三步:针对多个SqlSessionFactory 接口对象实例化,也需要指定主要对象,通过@Primary 标签实现
代码地址: