spring boot專案配置阿里資料來源相容多種資料庫
一、部落格背景
最近在做的一個專案需要用到阿里資料來源和四種資料庫,分別為mysql,oracle,db2,sql server資料庫,專案中的阿里資料來源需要相容4套資料庫,且只寫一個總的配置檔案,而不是mysql對應一套,oracle對應一套
二、程式碼展示
application.properties
#datasource資料來源的配置資訊 spring.profiles.active=datasource ##資料庫配置 #資料庫型別(1.oracle,2.sqlServer,3.mysql,4.db2) java.db.database-type=3 database.ip=192.169.1.26 database.port=3306 database.name=javadb04 spring.datasource.druid.username=root spring.datasource.druid.password=ENC(5M5nrpgKIQBkqEcldhbK6A==)
application-datasource.properties
#連線池配置 #資料庫連線url spring.datasource.druid.url=jdbc:mysql://:${database.ip}:${database.port}:${database.name} #初始化連線大小 spring.datasource.druid.initial-size=5 #最大連線數 spring.datasource.druid.max-active=200 #最小空閒連線數 spring.datasource.druid.min-idle=50 #獲取連線最大等待時間(毫秒) spring.datasource.druid.max-wait=60000 #是否開啟PSCache,mysql5.5及以上版本支援 spring.datasource.druid.pool-prepared-statements=true #指定每個連線上PSCache的大小,開啟PSCache時,此配置必須大於0 spring.datasource.druid.max-pool-prepared-statement-per-connection-size=20 #檢測連線是否有效超時時間(毫秒) spring.datasource.druid.validation-query-timeout=60000 #申請連線時執行validationQuery檢測連線是否有效 spring.datasource.druid.test-on-borrow=false #歸還連線時執行validationQuery檢測連線是否有效 spring.datasource.druid.test-on-return=false #申請連線的時候檢測,如果空閒時間大於timeBetweenEvictionRunsMillis,執行validationQuery檢測連線是否有效 spring.datasource.druid.test-while-idle=true #Destroy執行緒會檢測連線的間隔時間,testWhileIdle的判斷依據 spring.datasource.druid.time-between-eviction-runs-millis=60000 #Destory執行緒中如果檢測到當前連線的最後活躍時間和當前時間的差值大於,minEvictableIdleTimeMillis,則關閉當前連線 spring.datasource.druid.min-evictable-idle-time-millis=100000 #啟用ConfigFilter spring.datasource.druid.filter.config.enabled=true
java配置檔案
import cn.hutool.core.util.StrUtil; import com.alibaba.druid.pool.DruidDataSource; import com.montnets.db.enums.DataBaseEnum; import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.boot.jdbc.DataSourceBuilder; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import java.util.HashMap; import java.util.Map; import java.util.Properties; /** * 資料來源配置資訊 * * @author : raymond * @version : V1.0 * @date : 2020-12-12 16:24 */ @Configuration public class DataSourceConfig { private final MyDataSourceConfig myConfig; public DataSourceConfig(MyDataSourceConfig myConfig) { this.myConfig = myConfig; } @Bean public DruidDataSource dataSource(@Value("${java.db.database-type:3}") int dataBaseType) { DruidDataSource build = DataSourceBuilder.create().type(DruidDataSource.class).build(); Properties properties = new Properties(); Map<String, String> datasource = myConfig.getDatasource(); DataBaseEnum dataBase = DataBaseEnum.getDataBase(dataBaseType); datasource.put("druid.driver-class-name", dataBase.getDriverClassName()); datasource.put("druid.validation-query", dataBase.getValidationQuery()); if (dataBase != DataBaseEnum.ORACLE) { datasource.put("druid.url", StrUtil.format(dataBase.getUrl(), myConfig.getIp(), myConfig.getPort(), myConfig.getName())); } datasource.forEach(properties::put); build.configFromPropety(properties); return build; } } @Configuration @ConfigurationProperties(prefix="spring") class MyDataSourceConfig { private Map<String, String> datasource = new HashMap<>(); @Value("${database.ip}") private String ip; @Value("${database.port}") private String port; @Value("${database.name}") private String name; public String getIp() { return ip; } public String getPort() { return port; } public String getName() { return name; } public Map<String, String> getDatasource() { return datasource; } }
資料庫列舉型別
/**
* 資料庫型別、
*
* @author : raymond
* @version : V1.0
* @date : 2020-11-05 13:46
*/
public enum DataBaseEnum {
/**
* ORACLE資料庫
*/
ORACLE(1, "oracle.jdbc.OracleDriver", "SELECT 1 FROM DUAL", "jdbc:oracle:thin:@{}:{}:{}"),
/**
* sqlServer資料庫
*/
SQL_SERVER(2, "com.microsoft.sqlserver.jdbc.SQLServerDriver", "SELECT 1", "jdbc:sqlserver://{}:{};DatabaseName={}"),
/**
* mysql資料庫
*/
MYSQL(3, "com.mysql.cj.jdbc.Driver", "SELECT 1 FROM DUAL", "jdbc:mysql://{}:{}/{}?serverTimezone=GMT%2B8&useUnicode=true&charaterEncoding=utf-8&useSSL=false"),
/**
* DB2資料庫
*/
DB2(4, "com.ibm.db2.jcc.DB2Driver", "SELECT 1 FROM SYSIBM.SYSDUMMY1", "jdbc:db2://{}:{}/{}");
DataBaseEnum(int type, String driverClassName, String validationQuery, String url) {
this.type = type;
this.driverClassName = driverClassName;
this.validationQuery = validationQuery;
this.url = url;
}
public static DataBaseEnum getDataBase(int type) {
for (DataBaseEnum dataBase : values()) {
if (type == dataBase.type) {
return dataBase;
}
}
return MYSQL;
}
/**
* 資料庫型別
*/
private int type;
/**
* 驅動類
*/
private String driverClassName;
/**
* 校驗連線sql
*/
private String validationQuery;
/**
* 連線url
*/
private String url;
public int getType() {
return type;
}
public String getDriverClassName() {
return driverClassName;
}
public String getValidationQuery() {
return validationQuery;
}
public String getUrl() {
return url;
}
}
三、程式碼講解
1.配置檔案
首先我們來看配置檔案。application.properties配置檔案中配置了資料庫連線的基本資訊,和需要載入的另一個配置檔案。application-datasource.properties則是配置了資料來源相關的一些資訊
程式碼之所以能夠適配四套不同的資料庫,主要還是在java程式碼層面上實現,java程式碼實現相關邏輯後,我們換資料庫時,需要修改的僅僅是application.properties配置檔案中配置的資料庫連線的基本資訊,如資料庫型別,ip,使用者名稱,埠等。
2.java程式碼
下面我們來看下java程式碼邏輯的實現。首先可以看到有一個數據庫相關的列舉類,在這個列舉類中定義了四種資料庫,我們自定義的資料庫型別,資料庫的驅動類的名字,檢驗是否連線的sql,和資料庫分別對應各自的url的編寫格式。
我們在看下配置類,需要注意的是DataSourceConfig和MyDataSourceConfig兩個類是寫在一個檔案裡,之所以寫在同一個檔案下,是因為MyDataSourceConfig類載入的配置檔案僅僅是需要給DataSourceConfig使用,一開始是想將MyDataSourceConfig定義成傳統意義內部類,
但是由於要spring的相關直接,直接將MyDataSourceConfig定義在DataSourceConfig中,在使用註解的時候會報錯,所以直接將兩個類寫在同一個檔案下,但是需要注意的是,僅僅只有一個類的修飾符是public的,想更多瞭解這種寫法的同學,可以自行上網搜尋。
MyDataSourceConfig:
MyDataSourceConfig類只要是拿來獲取application-datasource.properties資料來源相關資訊,並將其封裝到名字取為datasource的map中,以供DataSourceConfig使用,如何將配置檔案的配置載入map中,檢視我的另一篇部落格:
DataSourceConfig:
在DataSourceConfig類中主要乾的事就是通過基本配置檔案中獲取到的資料庫型別,從而從列舉類中獲取對應的驅動名,檢驗是否連線的sql,url的格式設定到properties中去,大家可能會疑問,平常我們設定資料來源資訊的時候不是把資訊設定到自己定義的properties中去的,而是設定到DruidDataSource中的
,為啥在程式碼裡就是設定到自定義的properties中呢,那是因為我們最後呼叫了configFromPropety方法,下面貼上下該方法的一點原始碼,我們就可以明白原因了
從原始碼裡可以看到其實,呼叫該方法還是設定到DruidDataSource中去了,之所以呼叫該方法,是為了省略判斷,不然我們自己平常自己設定到DruidDataSource的時候,還需要一個一個的對相關key和vaule判斷。