Spring Boot 初級入門教程(十九) —— 配置多數據源(附源碼)

距上篇文章《Spring Boot 初級入門教程(十八) —— 集成 MyBatis 另外一種開發方式》已跨了兩個年頭,時間確實有些久了!!!這麼久沒更新這個系列,一則由於自己確實忙了些,項目一個接一個的加班整;再者,由於項目需要,寫了些其它方面的文章,比如關於 kafka、redis、IDEA 等。

另外,自己又買了個共享虛機,開通了塵封已久的博客,由於 .com 域名被別人搶注了,所以換成了 menglanglang.cn,如果有興趣,也可以踩踩。

在開始這篇之前,首先要做的一件事,就是把原來的 demo 源碼包中,包名換一下。因爲原來一直是 com.menglanglang.xxx,現在換成 cn.menglanglang.xxx。如下:

   

另外,把代碼中的原博客地址 http://blog.csdn.net/tzhuwb 換成 http://www.menglanglang.cn,純屬個人強迫症,理解。

言歸正傳,在項目開發中,我們常常會碰到一個服務配置多個數據源的情況,有些項目同時配置四五個 oracle,還配置着 mysql,gbase 等等。所以這篇主要說說如何簡單的配置多數據源。

這裏就簡單以前面配置過的 oracle 和 mysql,同時配置到項目中,業務上同時操作不同的庫來舉例說明配置多數據源的步驟。

第一步,添加依賴包

在 pom 文件中,添加支持數據庫連接池的 druid 依賴包,代碼如下:

	<!-- 添加數據庫連接池插件,支持多數據源 -->
	<dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid-spring-boot-starter</artifactId>
            <version>1.1.17</version>
        </dependency>

 添加 Oracle 和 MySQL 依賴包,代碼如下:

		<!-- mysql jdbc 插件 -->
		<dependency>
			<groupId>mysql</groupId>
			<artifactId>mysql-connector-java</artifactId>
			<version>5.1.46</version>
		</dependency>
		
		<!-- oracle jdbc 插件 -->
		<dependency>
			<groupId>com.oracle</groupId>
			<artifactId>ojdbc14</artifactId>
			<version>10.2.0.1.0</version>
		</dependency>

第二步,修改配置文件

在 application.properties 中,同時添加 oracle 數據庫配置和 MySQL 數據庫配置,分別用 first 和 second 來標識第一個和第二個數據源,代碼如下所示:

#################################
## Oracle 數據庫配置
#################################
# Oracle數據庫連接
spring.datasource.first.url=jdbc:oracle:thin:@192.168.220.240:1521:orcl
# Oracle數據庫用戶名
spring.datasource.first.username=scott
# Oracle數據庫密碼
spring.datasource.first.password=123456
# Oracle數據庫驅動(該配置可以不用配置,因爲Spring Boot可以從url中爲大多數數據庫推斷出它)
spring.datasource.first.driver-class-name=oracle.jdbc.OracleDriver

#################################
## MySQL 數據庫配置
#################################
# MySQL數據庫連接
spring.datasource.second.url=jdbc:mysql://192.168.220.240:3306/test_springboot?characterEncoding=UTF-8
# MySQL數據庫用戶名
spring.datasource.second.username=root
# MySQL數據庫密碼
spring.datasource.second.password=123456
# MySQL數據庫驅動(該配置可以不用配置,因爲Spring Boot可以從url中爲大多數數據庫推斷出它)
spring.datasource.second.driver-class-name=com.mysql.jdbc.Driver

#################################
## 數據庫共通配置
#################################
spring.datasource.max-active=10
spring.datasource.max-wait=10000
spring.datasource.min-idle=5
spring.datasource.initial-size=5
#spring.datasource.validation-query=SELECT 1 FROM DUAL
#spring.datasource.test-on-borrow=true
#spring.datasource.test-while-idle=true
#spring.datasource.time-between-eviction-runs-millis=18800
#spring.datasource.jdbc-interceptors=ConnectionState;SlowQueryReport(threshold=10000)

第三步,添加配置類,分別讀取數據庫配置

創建一個包 config,分別創建 FirstDataSourceConfig 和 SecondDataSourceConfig,結構和代碼如下:

FirstDataSourceConfig.java:

package cn.menglanglang.test.springboot.config;

import java.util.Objects;

import javax.sql.DataSource;

import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.SqlSessionTemplate;
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.env.Environment;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;

import com.alibaba.druid.pool.DruidDataSource;

@Configuration
public class FirstDataSourceConfig {

	@Autowired
	private Environment environment;

	@Bean(name = "firstDataSource")
	@Primary
	public DataSource firstDataSource() {
		DruidDataSource dataSource = new DruidDataSource();
		String prefix = "spring.datasource";
		String prefixFirst = "spring.datasource.first";
		dataSource.setName("firstDataSource");
		dataSource.setDriverClassName(environment.getProperty(prefixFirst + ".driver-class-name").trim());
		dataSource.setUrl(environment.getProperty(prefixFirst + ".url").trim());
		dataSource.setUsername(environment.getProperty(prefixFirst + ".username").trim());
		dataSource.setPassword(environment.getProperty(prefixFirst + ".password").trim());
		dataSource.setMaxActive(Integer.parseInt(Objects.requireNonNull(environment.getProperty(prefix + ".max-active")).trim()));
		dataSource.setMinIdle(Integer.parseInt(Objects.requireNonNull(environment.getProperty(prefix + ".min-idle")).trim()));
		dataSource.setInitialSize(Integer.parseInt(Objects.requireNonNull(environment.getProperty(prefix + ".initial-size")).trim()));
		dataSource.setMaxWait(Long.parseUnsignedLong(Objects.requireNonNull(environment.getProperty(prefix + ".max-wait")).trim()));
//		dataSource.setTimeBetweenEvictionRunsMillis(Long.parseUnsignedLong(Objects.requireNonNull(environment.getProperty(prefix + ".time-between-eviction-runs-millis")).trim()));
//		dataSource.setMinEvictableIdleTimeMillis(Long.parseUnsignedLong(Objects.requireNonNull(environment.getProperty(prefix + ".min-evictable-idle-time-millis")).trim()));
//		dataSource.setTestWhileIdle(Boolean.parseBoolean(environment.getProperty(prefix + ".test-while-idle")));
//		dataSource.setTestOnBorrow(Boolean.parseBoolean(environment.getProperty(prefix + ".test-on-borrow")));
//		dataSource.setTestOnBorrow(Boolean.parseBoolean(environment.getProperty(prefix + ".test-on-return")));
//		dataSource.setTestOnBorrow(Boolean.parseBoolean(environment.getProperty(prefix + ".async-close-connection-enable")));
//		dataSource.setValidationQueryTimeout(Integer.parseInt(Objects.requireNonNull(environment.getProperty(prefix + ".validation-query-timeout")).trim()));
//		dataSource.setValidationQuery(environment.getProperty(prefix + ".validation-query"));
		return dataSource;
	}

	@Bean(name = "firstTransactionManager")
	@Primary
	public DataSourceTransactionManager firstTransactionManager() {
		return new DataSourceTransactionManager(firstDataSource());
	}

	@Bean(name = "firstSqlSessionFactory")
	@Primary
	public SqlSessionFactory firstSqlSessionFactory(@Qualifier("firstDataSource") DataSource dataSource) throws Exception {
		final SqlSessionFactoryBean sessionFactory = new SqlSessionFactoryBean() {
			{
				setDataSource(dataSource);
				setMapperLocations(new PathMatchingResourcePatternResolver().getResources("classpath*:/mapper/oracle/**/*.xml"));
			}
		};
		return sessionFactory.getObject();
	}

	@Bean(name = "firstSqlSessionTemplate")
	@Primary
	public SqlSessionTemplate firstSqlSessionTemplate(SqlSessionFactory sqlSessionFactory) {
		return new SqlSessionTemplate(sqlSessionFactory);
	}

}

SecondDataSourceConfig.java:

package cn.menglanglang.test.springboot.config;

import java.util.Objects;

import javax.sql.DataSource;

import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.SqlSessionTemplate;
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.core.env.Environment;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;

import com.alibaba.druid.pool.DruidDataSource;

@Configuration
public class SecondDataSourceConfig {

	@Autowired
	private Environment environment;

	@Bean(name = "secondDataSource")
	public DataSource secondDataSource() {
		DruidDataSource dataSource = new DruidDataSource();
		String prefix = "spring.datasource";
		String prefixSecond = "spring.datasource.second";
		dataSource.setName("secondDataSource");
		dataSource.setDriverClassName(environment.getProperty(prefixSecond + ".driver-class-name").trim());
		dataSource.setUrl(environment.getProperty(prefixSecond + ".url").trim());
		dataSource.setUsername(environment.getProperty(prefixSecond + ".username").trim());
		dataSource.setPassword(environment.getProperty(prefixSecond + ".password").trim());
		dataSource.setMaxActive(Integer.parseInt(Objects.requireNonNull(environment.getProperty(prefix + ".max-active")).trim()));
		dataSource.setMinIdle(Integer.parseInt(Objects.requireNonNull(environment.getProperty(prefix + ".min-idle")).trim()));
		dataSource.setInitialSize(Integer.parseInt(Objects.requireNonNull(environment.getProperty(prefix + ".initial-size")).trim()));
		dataSource.setMaxWait(Long.parseUnsignedLong(Objects.requireNonNull(environment.getProperty(prefix + ".max-wait")).trim()));
//		dataSource.setTimeBetweenEvictionRunsMillis(Long.parseUnsignedLong(Objects.requireNonNull(environment.getProperty(prefix + ".time-between-eviction-runs-millis")).trim()));
//		dataSource.setMinEvictableIdleTimeMillis(Long.parseUnsignedLong(Objects.requireNonNull(environment.getProperty(prefix + ".min-evictable-idle-time-millis")).trim()));
//		dataSource.setTestWhileIdle(Boolean.parseBoolean(environment.getProperty(prefix + ".test-while-idle")));
//		dataSource.setTestOnBorrow(Boolean.parseBoolean(environment.getProperty(prefix + ".test-on-borrow")));
//		dataSource.setTestOnBorrow(Boolean.parseBoolean(environment.getProperty(prefix + ".test-on-return")));
//		dataSource.setTestOnBorrow(Boolean.parseBoolean(environment.getProperty(prefix + ".async-close-connection-enable")));
//		dataSource.setValidationQueryTimeout(Integer.parseInt(Objects.requireNonNull(environment.getProperty(prefix + ".validation-query-timeout")).trim()));
//		dataSource.setValidationQuery(environment.getProperty(prefix + ".validation-query"));
		return dataSource;
	}

	@Bean(name = "secondTransactionManager")
	public DataSourceTransactionManager secondTransactionManager() {
		return new DataSourceTransactionManager(secondDataSource());
	}

	@Bean(name = "secondSqlSessionFactory")
	public SqlSessionFactory secondSqlSessionFactory(@Qualifier("secondDataSource") DataSource dataSource) throws Exception {
		final SqlSessionFactoryBean sessionFactory = new SqlSessionFactoryBean() {
			{
				setDataSource(dataSource);
				setMapperLocations(new PathMatchingResourcePatternResolver().getResources("classpath*:/mapper/mysql/**/*.xml"));
			}
		};
		return sessionFactory.getObject();
	}

	@Bean(name = "secondSqlSessionTemplate")
	public SqlSessionTemplate secondSqlSessionTemplate(@Qualifier("secondSqlSessionFactory") SqlSessionFactory sqlSessionFactory) {
		return new SqlSessionTemplate(sqlSessionFactory);
	}

}

第四步,擴展數據庫接口查詢封裝類

接口查詢封裝類 DaoHelper.java,放在 common 包下,接口如下:

FirstDaoHelper.java:

package cn.menglanglang.test.springboot.common;

import java.util.List;
import java.util.Map;

import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Component;

/**
 * @desc 數據庫查詢封裝
 *
 * @see 封裝了基本的查詢方法,包括增刪改查分頁查等。 使用此類時,使用 @Resource 或 @Autowired 註解注入。
 * @author 孟郎郎
 * @blog http://www.menglanglang.cn
 * @version 1.0
 * @date 2020年3月19日下午11:02:03
 */
@Component
public class FirstDaoHelper {

	@Autowired
	@Qualifier("firstSqlSessionTemplate")
	private SqlSessionTemplate sqlSessionTemplate;

	public FirstDaoHelper(SqlSessionTemplate sqlSessionTemplate) {
		this.sqlSessionTemplate = sqlSessionTemplate;
	}

	/**
	 * 獲取sqlsession factory
	 */
	public SqlSessionFactory getSqlSessionFactory() {
		return this.sqlSessionTemplate.getSqlSessionFactory();
	}

	// 這部分和前篇文章中的DaoHelper完全一樣,在此省略。。。

}

SecondDaoHelper.java:

package cn.menglanglang.test.springboot.common;

import java.util.List;
import java.util.Map;

import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Component;

/**
 * @desc 數據庫查詢封裝
 *
 * @see 封裝了基本的查詢方法,包括增刪改查分頁查等。 使用此類時,使用 @Resource 或 @Autowired 註解注入。
 * @author 孟郎郎
 * @blog http://www.menglanglang.cn
 * @version 1.0
 * @date 2020年3月19日下午11:01:34
 */
@Component
public class SecondDaoHelper {

	@Autowired
	@Qualifier("secondSqlSessionTemplate")
	private SqlSessionTemplate sqlSessionTemplate;

	public SecondDaoHelper(SqlSessionTemplate sqlSessionTemplate) {
		this.sqlSessionTemplate = sqlSessionTemplate;
	}

	/**
	 * 獲取sqlsession factory
	 */
	public SqlSessionFactory getSqlSessionFactory() {
		return this.sqlSessionTemplate.getSqlSessionFactory();
	}

	// 這部分和前篇文章中的DaoHelper完全一樣,在此省略。。。

}

第五步,修改測試類、接口類、實現類及 xml 文件

這步驟相當於把《Spring Boot 初級入門教程(十五) —— 集成 MyBatis》和《Spring Boot 初級入門教程(十八) —— 集成 MyBatis 另外一種開發方式》中的代碼合併一起,這裏就不再詳細貼了,可以直接下載打包好的源碼查看即可。

分別爲:MyBatisController3.java、MyBatisService3.java、MyBatisServiceImpl3.java、mybati-mapper3.xml。

第六步,測試

啓動項目,分別訪問以下路徑,可以看到原來 Oracle 和 MySQL 的接口都可以拿到數據。

可以看到與原來的測試結果完全一致,所以可以同時使用兩個數據源配置。

到此,多數據源簡單配置步驟結束。^ ^

但一般項目中用到最多,也是最難的部分,是各個數據源參數的配置,以達到訪問效率最優,需要結合服務部署機器資源配置,數據庫配置,併發訪問量等各個指標,來調節數據源參數。這裏把常常用到的參數順便羅列一下,便於服務調優查詢。

yml 配置文件格式:

druid:
      access-to-underlying-connection-allowed: false #允許訪問底層連接
      active-connection-stack-trace:  #活躍連接堆跟蹤
      active-connections: #活躍連接列表
      aop-patterns: #AOP模式
      async-close-connection-enable: false #啓用異步關閉連接
      async-init: false #異步初始化
      break-after-acquire-failure: false #失敗後跳過
      clear-filters-enable: false #啓動清除過濾器
      connect-properties:   #連接配置
      connection-error-retry-attempts: 1 #連接出錯嘗試幾次重新連接
      connection-init-sqls:   #連接初始化語句
      connection-properties:  #連接屬性
      create-scheduler:   #創建程序
      db-type:  #DB類型
      default-auto-commit: false #默認自動提交
      default-catalog:  #默認目錄
      default-read-only: false #默認只讀
      default-transaction-isolation: 1 #默認事務隔離
      destroy-scheduler:  #銷燬程序
      driver:   #驅動類
      driver-class-name:  #驅動類名
      dup-close-log-enable: true #啓用DUP關閉日誌
      enable: true #啓動連接池
      exception-sorter: 
      exception-sorter-class-name: 
      fail-fast: true #快速失敗?
      filter-class-names: #過濾器類名稱

properties 配置文件格式:

#連接池屬性

spring.datasource.druid.initial-size=15
spring.datasource.druid.max-active=100
spring.datasource.druid.min-idle=15
spring.datasource.druid.max-wait=60000
spring.datasource.druid.time-between-eviction-runs-millis=60000
spring.datasource.druid.min-evictable-idle-time-millis=300000
spring.datasource.druid.test-on-borrow=false
spring.datasource.druid.test-on-return=false
spring.datasource.druid.test-while-idle=true
spring.datasource.druid.validation-query=SELECT 1 FROM DUAL
spring.datasource.druid.validation-query-timeout=1000
spring.datasource.druid.keep-alive=true
spring.datasource.druid.remove-abandoned=true
spring.datasource.druid.remove-abandoned-timeout=180
spring.datasource.druid.log-abandoned=true
spring.datasource.druid.pool-prepared-statements=true
spring.datasource.druid.max-pool-prepared-statement-per-connection-size=20
spring.datasource.druid.filters=stat,wall,slf4j
spring.datasource.druid.use-global-data-source-stat=true
spring.datasource.druid.preparedStatement=true
spring.datasource.druid.maxOpenPreparedStatements=100
spring.datasource.druid.connect-properties.mergeSql=true
spring.datasource.druid.connect-properties.slowSqlMillis=5000

屬性說明:

源碼下載:https://pan.baidu.com/s/1jA_EDzoUeXhgNA-e7wsnYA (提取碼:4i84)

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章