集成(复用)mybatis动态数据源
最后更新:2025-07-24 11:08:52
|
状态:未完成
anyline启动时会加载spring上下文中已经注册好的数据源。但是如果数据源是基于DynamicDataSource实现的可切换数据源,会有问题。需要实现一个DataSourceMonitor 来监听数据源的切换,也要不用anyline的方式切换数据源,对anyline来说只有一个数据源
非DynamicDataSource或者自己另外注册数据源 就不用管这些了
默认情况下anyline中的一个数据源 只会绑定一个DriverAdapter(用来生成一类数据库方言)
但有第三方数据源会通过DynamicDataSource实现数据源切换,这样就会造成一个数据源对应多种数据库如(mysql, oracle)
而adapter只会检测一次,这样就会造成如果第一次操作的是mysql那么这个数据源就会绑定MySQLAdapter ,下一次操作oracle时就不检测了直接使用MySQLAdapter
为了处理这个问题可以通过ConfigTable.KEEP_ADAPTER来设置adapter检测方式
非DynamicDataSource或者自己另外注册数据源 就不用管这些了
默认情况下anyline中的一个数据源 只会绑定一个DriverAdapter(用来生成一类数据库方言)
但有第三方数据源会通过DynamicDataSource实现数据源切换,这样就会造成一个数据源对应多种数据库如(mysql, oracle)
而adapter只会检测一次,这样就会造成如果第一次操作的是mysql那么这个数据源就会绑定MySQLAdapter ,下一次操作oracle时就不检测了直接使用MySQLAdapter
为了处理这个问题可以通过ConfigTable.KEEP_ADAPTER来设置adapter检测方式
/**
* 同一个数据源是否保持相同的adapter
* 1: 全部保持
* 0: 全部不保持 每次操作数据库都检测一次
* 2: 由DataSourceMonitor接口实现
* 如果项目中出现 一个数据源对应多类数据库的情况, 同时出现 一个数据源只对应一类数据库的情况 这时才需要设置为2或0
* 这里设置了false后每个
*/
public static int KEEP_ADAPTER= 1;// 同一个数据源是否保持相同的adapter
通常设置成0就可以了,每次都检测,这个过程很快,1毫秒内就可以完成
也可以设置成2 通过DataSourceMonitor接口实现缓存【 示例源码】
package org.anyline.simple.datasource;
import com.baomidou.dynamic.datasource.DynamicRoutingDataSource;
import com.baomidou.dynamic.datasource.toolkit.DynamicDataSourceContextHolder;
import org.anyline.data.adapter.DriverAdapter;
import org.anyline.data.datasource.DataSourceMonitor;
import org.anyline.data.runtime.DataRuntime;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.datasource.DataSourceUtils;
import org.springframework.stereotype.Component;
import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.DatabaseMetaData;
import java.util.HashMap;
import java.util.Hashtable;
import java.util.Map;
@Component
public class MyBatisDataSourceMonitor implements DataSourceMonitor {
private Map<String, String> features = new HashMap<>();
private Map<String, DriverAdapter> adapters = new Hashtable<>();
/**
* 数据源特征 用来定准 adapter 包含数据库或JDBC协议关键字<br/>
* 一般会通过 产品名_url 合成 如果返回null 上层方法会通过driver_产品名_url合成
* @param datasource 数据源
* @return String 返回null由上层自动提取
*/
@Override
public String feature(Object datasource) {
String feature = null;
if(datasource instanceof JdbcTemplate){
JdbcTemplate jdbc = (JdbcTemplate)datasource;
DataSource ds = jdbc.getDataSource();
if(ds instanceof DynamicRoutingDataSource){
String key = DynamicDataSourceContextHolder.peek();
feature = features.get(key);
if(null == feature){
Connection con = null;
try {
con = DataSourceUtils.getConnection(ds);
DatabaseMetaData meta = con.getMetaData();
String url = meta.getURL();
feature = meta.getDatabaseProductName().toLowerCase().replace(" ","") + "_" + url;
features.put(key, feature);
} catch (Exception e) {
e.printStackTrace();
} finally {
if (null != con && !DataSourceUtils.isConnectionTransactional(con, ds)) {
DataSourceUtils.releaseConnection(con, ds);
}
}
}
}
}
return feature;
}
/**
* ConfigTable.KEEP_ADAPTER=2 : 根据当前接口判断是否保持同一个数据源绑定同一个adapter<br/>
* DynamicRoutingDataSource类型的返回false,因为同一个DynamicRoutingDataSource可能对应多类数据库, 如果项目中只有一种数据库 应该直接返回true
* @param datasource 数据源
* @return boolean
*/
@Override
public boolean keepAdapter(Object datasource) {
if(datasource instanceof JdbcTemplate){
JdbcTemplate jdbc = (JdbcTemplate)datasource;
DataSource ds = jdbc.getDataSource();
if(ds instanceof DynamicRoutingDataSource){
return false;
}
}
return true;
}
/* ********************************************************************************************************************
*
*
* 下面是为了缓存adapter 实际测试发现 定位一次基本会在1毫秒内完成 缓存没什么太大意义 可以不实现
*
*
******************************************************************************************************************** */
/**
* 如果有根据feature识别不了的情况,可以在这里实现,如果这一步返回了adapter则以这一步为准
* @param datasource 数据源
* @return DriverAdapter
*/
@Override
public DriverAdapter adapter(Object datasource) {
DriverAdapter adapter = null;
if(datasource instanceof JdbcTemplate){
JdbcTemplate jdbc = (JdbcTemplate)datasource;
DataSource ds = jdbc.getDataSource();
if(ds instanceof DynamicRoutingDataSource){
String key = DynamicDataSourceContextHolder.peek();
adapter = adapters.get(key);
}
}
return adapter;
}
/**
* 上层方法完成adapter定位后调用,可以在这里缓存,下一次定位提供给adapter(Object datasource)
* @param runtime 运行环境主要包含驱动适配器 数据源或客户端
* @param datasource 数据源
* @param adapter DriverAdapter
* @return 如果没有问题原样返回,如果有问题可以修正或返回null, 如果返回null上层方法会抛出adapter定位失败的异常
*/
@Override
public DriverAdapter after(DataRuntime runtime, Object datasource, DriverAdapter adapter) {
if(datasource instanceof JdbcTemplate){
JdbcTemplate jdbc = (JdbcTemplate)datasource;
DataSource ds = jdbc.getDataSource();
if(ds instanceof DynamicRoutingDataSource){
String key = DynamicDataSourceContextHolder.peek();
adapters.put(key, adapter);
}
}
return adapter;
}
}