写在前面
本次操作使用MyBatis动态数据源和多数据库的支持,仅仅记录自己理解的关键部分,如有错误,欢迎指正教导,我一定认真吸取!
动态数据源
引入依赖
<!-- mysql驱动包 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.48</version>
</dependency>
<!-- oracle驱动包 -->
<dependency>
<groupId>com.oracle</groupId>
<artifactId>ojdbc6</artifactId>
<version>11.2.0.3</version>
</dependency>
<!-- 动态数据源 -->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>dynamic-datasource-spring-boot-starter</artifactId>
<version>3.0.0</version>
</dependency>
数据源配置需要使用动态数据源格式,这里的master数据源是mysql数据库,slave数据源是oracle数据库
spring:
datasource:
type: com.zaxxer.hikari.HikariDataSource
dynamic:
#设置默认的数据源或者数据源组,默认值即为master
primary: master
datasource:
master:
username: user
password: password
driver-class-name: com.mysql.jdbc.Driver
url: jdbc:mysql://127.0.0.1:8904/COMMON_ORG?characterEncoding=UTF-8&serverTimezone=UTC
minimum-idle: 30
maximum-pool-size: 50
auto-commit: true
pool-name: DatebookHikariCP
idle-timeout: 1600000
max-lifetime: 180000
connection-timeout: 30000
connection-test-query: SELECT 1
slave:
username: username
password: password
driver-class-name: oracle.jdbc.OracleDriver
url: "jdbc:oracle:thin:@127.0.0.1:1521/uomqkjdb"
minimum-idle: 30
maximum-pool-size: 50
auto-commit: true
pool-name: DatebookHikariCP
idle-timeout: 1600000
max-lifetime: 180000
connection-timeout: 30000
connection-test-query: SELECT 1
微服务默认使用主数据源(primary),当需要使用其他从数据源时,可以使用如下@DS注释来切换到指定数据源
@DS("slave")
public interface TestMapper extends BaseMapper<TestEntity> {
List<TestVo> getTestVo(Page<TestVo> page, @Param("value") String value,@Param("isWhiteList") Integer isWhiteList);
}
需要注意的是,@DS注释最好使用在Mapper.java的类名或方法上,已经确认的是,在@Transactional涵盖下,使用@DS切换数据源将会失效。
多数据库
由于还没有实际操作,使用配置待补充,目前已知在Mybaitis配置中,配置支持的数据库ID
@Bean
public DatabaseIdProvider getDatabaseIdProvider() {
DatabaseIdProvider databaseIdProvider = new VendorDatabaseIdProvider();
Properties properties = new Properties();
properties.setProperty(ORACLE, "oracle");
properties.setProperty(MYSQL, "mysql");
databaseIdProvider.setProperties(properties);
return databaseIdProvider;
}
在Mapper.xml中使用,为标签指定databaseId。
例如:
<select id="selectString" resultType="java.lang.String">
select
<if test="_databaseId==mysql">
concat(a.name,a.age)
</if>
<if test="_databaseId==oracle">
a.name||a.age
</if>
from tb_user a
</select>
再例如:
<select id="selectString" resultType="java.lang.String" databaseId="mysql">
select concat(a.name,a.age)
from tb_user a
</select>
<select id="selectString" resultType="java.lang.String" databaseId="oracle">
select a.name||a.age
from tb_user a
</select>
无论是哪种方式指定databaseId,实际执行的时候使用哪个,取决于sqlSession中databaseId的属性,而该属性在应用服务启动初始化的时候,在org.mybatis.spring.SqlSessionFactoryBean的buildSqlSessionFactory()方法中根据当前数据源(dataSource)和databaseIdProvider配置确定,在服务运行时,不会再随着动态数据源的切换而切换。
--org.mybatis.spring.SqlSessionFactoryBean.buildSqlSessionFactory方法截取
if (this.databaseIdProvider != null) {
try {
configuration.setDatabaseId(this.databaseIdProvider.getDatabaseId(this.dataSource));
} catch (SQLException var24) {
throw new NestedIOException("Failed getting a databaseId", var24);
}
}
同时使用动态数据源与多数据库
根据上面的内容我们可以知道,动态数据源和多数据库支持同时使用的时候,并不如想象中的完美配合。
依照本章动态数据源部分的场景(即在Mapper.java中切换数据源为slave的oracle数据库),主数据源(primary)设置的是mysql数据库,那么在服务启动的时候就会初始化databaseId为mysql,那么如果我们如下同时指定多数据源格式查询语句时,会优先匹配到第一个databaseId=”mysql”的脚本执行,此时使用oracle数据源来执行mysql语句,势必导致执行异常。
<select id="selectString" resultType="java.lang.String" databaseId="mysql">
select concat(a.name,a.age)
from tb_user a
</select>
<select id="selectString" resultType="java.lang.String" databaseId="oracle">
select a.name||a.age
from tb_user a
</select>
即使注释掉databaseId=”mysql”的脚本,也不会使用databaseId=”oracle”的脚本,反而会报错 Invalid bound statement (not found)。
这其中涉及到mybatis的筛选机制,在初始化的时候(保留怀疑),会收集所有执行语句,如果有同名的,会保留指定databaseId的部分(或片段),如果同名执行语句中同时包含指定databaseId的和未指定的,那么会抛弃未指定的执行语句。
因此,如果在一个Mapper.java方法中切换了数据源,表明已经确定了该方法所用的数据库类型,因此在Mapper.xml中写sql语句的时候,就没有必要再指定databaseId属性,也可以避免不必要的麻烦。如下配置公共的执行语句,虽然sqlSession中databaseId为mysql,但不会再使用databaseId来作为执行语句的筛选条件
<select id="selectString" resultType="java.lang.String">
select a.name||a.age
from tb_user a
</select>
至于想要databaseId如何随着动态数据源的切换而切换,目前还没想到好的办法,暂时或者基本上也遇不到这样的需求场景。