一、博客背景
最近在做的一个项目需要用到阿里数据源和四种数据库,分别为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中,查看我的另一篇博客:https://blog.csdn.net/bird_tp/article/details/111216892
DataSourceConfig:
在DataSourceConfig类中主要干的事就是通过基本配置文件中获取到的数据库类型,从而从枚举类中获取对应的驱动名,检验是否连接的sql,url的格式设置到properties中去,大家可能会疑问,平常我们设置数据源信息的时候不是把信息设置到自己定义的properties中去的,而是设置到DruidDataSource中的
,为啥在代码里就是设置到自定义的properties中呢,那是因为我们最后调用了configFromPropety方法,下面粘贴下该方法的一点源码,我们就可以明白原因了
从源码里可以看到其实,调用该方法还是设置到DruidDataSource中去了,之所以调用该方法,是为了省略判断,不然我们自己平常自己设置到DruidDataSource的时候,还需要一个一个的对相关key和vaule判断。