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

如何使用 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 指定outDataSource对象为@Primary

                

             第二步:针对多个数据源实例时,需要限定各个数据源实例化对象的DataSource ,通过使用@Qualifier 标签实现

          第三步:针对多个SqlSessionFactory 接口对象实例化,也需要指定主要对象,通过@Primary 标签实现

代码地址:

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
好的,你的问题是关于如何在Spring Boot应用中实现数据动态切换,使用的技术包括Spring Boot、MyBatis、MySQL、OracleDruid数据连接池、自定义注解和切面,并且配置文件使用application.yml格式。 首先,需要在pom.xml文件中添加相应的依赖: ```xml <!--Spring Boot--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter</artifactId> </dependency> <!--MyBatis--> <dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> <version>2.2.0</version> </dependency> <!--MySQL--> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>8.0.23</version> </dependency> <!--Oracle--> <dependency> <groupId>com.oracle.database.jdbc</groupId> <artifactId>ojdbc8</artifactId> <version>19.3.0.0</version> </dependency> <!--Druid--> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid-spring-boot-starter</artifactId> <version>1.2.6</version> </dependency> ``` 接下来,需要在application.yml文件中配置数据MyBatis相关的属性,例如: ```yaml spring: datasource: druid: # 数据1 db1: driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://localhost:3306/db1?serverTimezone=UTC&useUnicode=true&characterEncoding=utf-8&useSSL=false username: root password: root # 数据2 db2: driver-class-name: oracle.jdbc.OracleDriver url: jdbc:oracle:thin:@localhost:1521:ORCL username: scott password: tiger # 默认数据 url: jdbc:mysql://localhost:3306/db1?serverTimezone=UTC&useUnicode=true&characterEncoding=utf-8&useSSL=false username: root password: root mybatis: mapper-locations: classpath:mapper/*.xml type-aliases-package: com.example.demo.entity ``` 然后,需要定义一个自定义注解,用于标识哪些方法需要使用哪个数据: ```java @Target({ElementType.METHOD, ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface DataSource { String value() default "db1"; } ``` 在数据切换的时候,我们需要获取注解上指定的数据名称,因此需要定义一个切面: ```java @Aspect @Component public class DataSourceAspect { @Around("@annotation(ds)") public Object around(ProceedingJoinPoint point, DataSource ds) throws Throwable { String dataSourceName = ds.value(); DynamicDataSource.setDataSource(dataSourceName); try { return point.proceed(); } finally { DynamicDataSource.clearDataSource(); } } } ``` 最后,需要定义一个动态数据,用于实现数据的切换: ```java public class DynamicDataSource extends AbstractRoutingDataSource { private static final ThreadLocal<String> dataSourceHolder = new ThreadLocal<>(); @Override protected Object determineCurrentLookupKey() { return dataSourceHolder.get(); } public static void setDataSource(String dataSourceName) { dataSourceHolder.set(dataSourceName); } public static void clearDataSource() { dataSourceHolder.remove(); } } ``` 至此,多数据动态切换的配置就完成了。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值