SpringBoot + MyBatis + Oracle + Druid 实现多套数据源分析

熊孩纸 阅读:253 2021-03-31 13:28:40 评论:0

如何使用 Spring Boot 该如何处理多个数据库的读写,一般有以下几种策略:

第一种策略:

  • 多套数据源:即针对一个数据库建立一套数据处理逻辑,每套数据库都包括数据源配置、会话工厂( sessionFactory )、连接、SQL 操作、实体。各套数据库相互独立。

第二种策略:

  • 动态数据源:确定数量的多个数据源共用一个会话工厂,根据条件动态选取数据源进行连接、SQL 操作。

本文主讲:基于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 包下分别设置 inout 包,存放两个库对应的表实体。

2、在 mapper 包下,分别添加 inout包,存放两个库对应的 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 [email protected]

                

             第二步:针对多个数据源实例时,需要限定各个数据源实例化对象的DataSource ,[email protected] 标签实现

          第三步:针对多个SqlSessionFactory 接口对象实例化,也需要指定主要对象,[email protected] 标签实现

代码地址:

声明

1.本站遵循行业规范,任何转载的稿件都会明确标注作者和来源;2.本站的原创文章,请转载时务必注明文章作者和来源,不尊重原创的行为我们将追究责任;3.作者投稿可能会经我们编辑修改或补充。

发表评论
搜索
KIKK导航

KIKK导航

排行榜
关注我们

一个IT知识分享的公众号