使用 dynamic-datasource 多数据源,@DS切换后如何在代码中判断当前数据源类型
我们知道在mapper下可以使用 Mybatis 的内置变量 databaseid 来动态组装sql,但是在多数据源情况下,系统中会有多个`DataSource` ,使用 `@DS("master")` 注解动态切换数据源,调用的后续的查询都是切换后的数据源中的数据,在代码中能不能友好的判断当前数据源是 `MySQL` 还是 `Oracle` 类型?有,还真有,直接上结果:`DSUtils.get
前言
我们知道在mapper下可以使用 Mybatis 的内置变量 databaseid 来动态组装sql,但是在多数据源情况下,系统中会有多个 DataSource ,使用 @DS("master") 注解动态切换数据源,调用的后续的查询都是切换后的数据源中的数据,在代码中能不能友好的判断当前数据源是 MySQL 还是 Oracle 类型?有,还真有,直接上结果:DSUtils.getDbType() == DbType.MYSQL
最终工具类
该工具类避免了注入 DataSource 去查询 databaseProductName 导致的事务中断。
import com.baomidou.dynamic.datasource.DynamicRoutingDataSource;
import com.baomidou.mybatisplus.annotation.DbType;
import com.xxx.common.util.SpringContextUtils;
import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.DatabaseMetaData;
import java.sql.SQLException;
/**
* @description DatabaseUtils 为了跟@DS对齐,所以命名 DSUtils
* 该方法需要 dynamic-datasource-spring-boot-starter 插件
* @className DSUtils
* @author monkeyhi
* @date 2025-01-08 15:40
* @version v1.0
*/
public class DSUtils {
private DSUtils() {
throw new IllegalStateException("Utility class");
}
/**
* 获取当前的数据库类型
* @return 不支持的,返回 OTHER
*/
public static DbType getDbType() {
DynamicRoutingDataSource routingDataSource = SpringContextUtils.getBean(DynamicRoutingDataSource.class);
DataSource dataSource = routingDataSource.determineDataSource();
try (Connection connection = dataSource.getConnection()) {
DatabaseMetaData metaData = connection.getMetaData();
String databaseProductName = metaData.getDatabaseProductName();
return DbType.getDbType(databaseProductName);
} catch (SQLException e) {
return DbType.OTHER;
}
}
}
抛砖引玉
获取数据源类型这件事,一般有如下两种情况:
- 获取配置的类似 databaseId 这样固定的标签判断
通常使用dynamic-datasource等框架,使用@DS("mysql")等注解动态切换数据源时,SqlSessionFactory的databaseId是固定的,无法动态反映当前数据源的数据库类型,通常有团队使用固定的标识来标明使用的数据库,如@DS("mysql")中的 “mysql”,这怎么说呢,需要团队和后续其他交互团队的协商或者默契。 - 使用 DataSource 获取数据库元数据判断
另外一种情况在动态数据源切换的场景下,可以通过DataSource获取当前连接的数据库元数据(DatabaseMetaData),从而动态识别数据库类型
@Autowired
private DataSource dataSource;
try (Connection connection = dataSource.getConnection()) {
DatabaseMetaData metaData = connection.getMetaData();
String databaseProductName = metaData.getDatabaseProductName();
return resolveDatabaseId(databaseProductName);
} catch (SQLException e) {
throw new RuntimeException("Failed to get database metadata", e);
}
但是如上这种依靠 @Autowired 情况不适用于动态切换数据源的方法逻辑片段,在使用事务的时候,注入的 dataSource 可能和当前方法执行的数据源不一致(请理解一下这句话),数据源不一致会中断当前的事务,这坑可能在编译期根本发现不了。
总结
如果你使用的是 dynamic-datasource 等动态数据源框架,可以通过框架提供的工具类或上下文获取当前数据源的数据库类型。
假设你使用的是 dynamic-datasource-spring-boot-starter,可以通过 DataSourceContextHolder 获取当前数据源的 key,然后根据 key 映射到数据库类型。
最优雅的方式当然是不依赖key的定义和在不破坏逻辑的情况下获取到同一个 DataSource 去获取数据库的元数据去判断的方式,就是上面工具类中的 DynamicRoutingDataSource 获取 DatabaseMetaData 的形式。
最后,祝大家玩的愉快~~~
DAMO开发者矩阵,由阿里巴巴达摩院和中国互联网协会联合发起,致力于探讨最前沿的技术趋势与应用成果,搭建高质量的交流与分享平台,推动技术创新与产业应用链接,围绕“人工智能与新型计算”构建开放共享的开发者生态。
更多推荐
所有评论(0)