聊一聊信创环境下多数据库适配方案
# 前言
在响应国家信创的环境下,开发一款产品不仅需要适配Oracle,Mysql等主流数据库,还需要适配达梦,神通等国产数据库,适配多种数据库时,我们就需要考虑数据库兼容问题,如何让我们的代码兼容多种数据库或者说有些什么方案呢?
在开发过程中,有关数据库的操作,我们要么使用MyBatis,MyBatis-Plus,Hibernate等开源ORM框架,或者使用原生的jdbc等,那么针对不同的产品不同开发框架我们如何适配多数据库呢?
# 1 MyBatis适配多数据库
Mybatis 本身并不支持多种数据库,此时需要对 Mybatis 进行改造,这里列举两种方案:
- 通过Mapper定义不同类型数据库的SQL
- 通过插件机制动态代理SQL
# 1.1 通过Mapper定义不同类型数据库的SQL
Mapper 支持多数据库 SQL,即 Mapper 中即可以写支持 A 数据库的 SQL,又可以写支持 B 数据库的 SQL。
# 1. 定义多数据库支持的 bean
@Bean
public DatabaseIdProvider getDatabaseIdProvider() {
DatabaseIdProvider databaseIdProvider = new VendorDatabaseIdProvider();
Properties properties = new Properties();
properties.setProperty("Oracle", "oracle");
properties.setProperty("MySQL", "mysql");
properties.setProperty("DM", "dm");
databaseIdProvider.setProperties(properties);
return databaseIdProvider;
}
2
3
4
5
6
7
8
9
10
# 2. 配置文件中指定使用的具体数据库
database.type=oracle
mybatis-plus.configuration.database-id=${database.type}
2
# 3. 重定义 Mapper
<!-- 查看某表是否存在 -->
<select id="getMtHisTableExists" resultType="java.lang.Integer">
select count(*) ICOUNT from INFORMATION_SCHEMA.TABLES where TABLE_SCHEMA=#{dbName} and TABLE_NAME=upper(#{tableName})
</select>
<!-- oracle方言查看某表是否存在 -->
<select id="getMtHisTableExists" resultType="java.lang.Integer" databaseId="oracle">
SELECT COUNT(*) FROM USER_TABLES WHERE TABLE_NAME=upper(#{tableName})
</select>
<!-- mysql方言查看某表是否存在 -->
<select id="getMtHisTableExists" resultType="java.lang.Integer" databaseId="mysql">
SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA = #{dbName} AND TABLE_NAME=upper(#{tableName})
</select>
2
3
4
5
6
7
8
9
10
11
12
13
14
# 1.2 通过插件机制动态代理SQL
Mybatis 支持用插件对四大核心对象 (Executor、StatementHandler、ParameterHandler、ResultSetHandler)进行拦截,对 Mybatis 来说插件就是拦截器,用来增强核心对象的功能,Mybatis 插件提供了简单易用的扩展机制。
# 1.实现扩展插件
/**
* @Author Mr.Fire
* @Desc
* @Date 2022/12/4
*/
@Intercepts(value = {
@Signature(
type = StatementHandler.class,
method = "prepare",
args = {Connection.class, Integer.class})
}
)
@Component
@Slf4j
public class DbAdapterInterceptor implements Interceptor {
@Override
public Object intercept(Invocation invocation) throws Throwable {
try {
StatementHandler statementHandler = (StatementHandler) invocation.getTarget();
BoundSql boundSql = statementHandler.getBoundSql();
String oldSql = boundSql.getSql();
/**
* 解析SQL,按照不同数据库类型修改SQL
*/
} catch (Exception e) {
log.error(e.getMessage(), e);
return invocation.proceed();
}
return invocation.proceed();
}
@Override
public void setProperties(Properties properties) {
String dialect = properties.getProperty("dialect");
log.info("mybatis intercept dialect:{}", dialect);
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
注意:
如果使用的是MyBatis-Plus,只需实现
com.baomidou.mybatisplus.extension.plugins.inner.InnerInterceptor#beforePrepare
方法,并在注册插件即可,因为Mybatis-plus插件默认代理了StatementHandler#prepare方法。
# 2. 注册插件
<configuration>
<plugins>
<plugin interceptor="com.fire.DbAdapterInterceptor">
<property name="dialect" value="mysql" />
</plugin>
</plugins>
</configuration>
2
3
4
5
6
7
# 3. 修改SQL
实现按不同数据库解析SQL,并适配修改,代码省略...
# 2 自定义数据库适配组件
# 2.1 架构
# 2.2 实现思想
数据层统一定义接口,针对不同数据库分别实现不同的Starter,如果需要适配其他数据库,也能够做到灵活配置与扩展。结合SpringBoot自动装配机制,服务启动自动加载对应数据库组件,其他数据源不会初始化,也不会由此造成资源开销或冲突。至于底层如何实现适配,是用ORM框架还是原生JDBC等,可根据具体项目具体环境分析。
比如结合MyBatis实现,通过@ConditionalOnProperty注解判断进行按需自动装配对应组件:
@Configuration
@ComponentScan("com.fire.mysql")
@MapperScan(basePackages = "com.fire.mysql.dao")
@ConditionalOnProperty(name = "spring.datasource.driverClassNane", havingValue = "com.mysql.cj.jdbc.Driver")
public class AutoMySQLConfiguration {
/**
* sql session工厂配置
*
* @param dataSource
* @return
* @throws Exception
*/
@Bean
public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception {
SqlSessionFactoryBean bean = new SqlSessionFactoryBean();
bean.setDataSource(dataSource);
bean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources("classpath:com/fire/mysql/mapper/*Mapper.xml");
return bean.getObject();
}
@Bean
@ConfigurationProperties(prefix = "spring.datasource")
public DruidDataSource druidDataSource() {
return new DruidDataSource();
}
......
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
# 总结
随着国产化的推进,很多项目都采用国产服务器,国产数据库,兼容适配问题急需解决,本文主要讲解一些关于多数据库适配方案,具体实现还需根据需求具体分析。