springboot使用Mybatis中兼容多数据源的databaseId(databaseIdProvider)的简单使用方法

embedded/2024/9/22 17:56:55/

最近有兼容多数据库的需求,原有数据库使用的mysql,现在需要同时兼容mysql和pgsql,后期可能会兼容更多。

mysql和pgsql很多语法和函数不同,所以有些sql需要写两份,于是在全网搜索如何在mapper中sql不通用的情况下兼容多数据库,中文网络下,能搜到的解决方案大概有两种:1.使用@DS注解的动态数据源;2.使用数据库厂商标识,即databaseIdProvider。第一种多用来同时连接多个数据源,且配置复杂,暂不考虑。第二种明显符合需求,只需要指定sql对应的数据库即可,不指定的即为通用sql。

常规方法

在全网搜索databaseIdProvider的使用方法,大概有两种:

1.在mybatis的xml中配置,大多数人都能搜到这个结果:

<databaseIdProvider type="DB_VENDOR"><property name="MySQL" value="mysql"/><property name="Oracle" value="oracle" />
</databaseIdProvider>

然后在mapper中:

<select id="selectStudent" databaseId="mysql">select * from student where username = #{username} limit 1
</select>
<select id="selectStudent" databaseId="oracle">select * from student where name = #{username} and rownum<2;
</select>

这种方法,包括在mybatis的github和官方文档的说明,都是看得一头雾水,因为前后无因果关系,DB_VENDOR这种约定好的字段也显得很奇怪,为什么要配置DB_VENDOR?为什么mysql需要写键值对?键值对的key是从那里来的?全网都没有太清晰的说明。而且需要全程使用xml进行配置,对使用spring cloud config类似的配置中心不太友好。

2.创建mybatis的配置类:

import org.apache.ibatis.mapping.DatabaseIdProvider;
import org.apache.ibatis.mapping.VendorDatabaseIdProvider;
import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;import javax.sql.DataSource;
import java.util.Properties;@Configuration
public class MyBatisConfig {@Beanpublic DatabaseIdProvider databaseIdProvider() {VendorDatabaseIdProvider provider = new VendorDatabaseIdProvider();Properties props = new Properties();props.setProperty("Oracle", "oracle");props.setProperty("MySQL", "mysql");props.setProperty("PostgreSQL", "postgresql");props.setProperty("DB2", "db2");props.setProperty("SQL Server", "sqlserver");provider.setProperties(props);return provider;}@Beanpublic SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception {SqlSessionFactoryBean factoryBean = new SqlSessionFactoryBean();factoryBean.setDataSource(dataSource);factoryBean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources("classpath*:mapper/*Mapper.xml"));factoryBean.setDatabaseIdProvider(databaseIdProvider());return factoryBean.getObject();}
}

这种方法给出的代码一般都是在你的项目中无法运行,因为代码中包括除了databaseIdProvider的其他配置,例如mapper扫描路径,dataSource配置,连接池配置等。配置类中需要把自己创建的databaseIdProvider置入到sqlSessionFactory中,必须要再创建一个sqlSessionFactory,这样就导致代码改动比较多。

一些发现

有没有更简单的办法?

mybatis的入口是SqlSessionFactory,如果要了解mybatis的运行原理,从这个类入手是最合适的,于是顺藤摸瓜找到了SqlSessionFactoryBuilder类,这个类有很多build方法,打断点之后发现当前配置走的是

  public SqlSessionFactory build(Configuration config) {return new DefaultSqlSessionFactory(config);}

这个Configuration类就非常显眼了,点进去之后发现这个类的成员变量就是可以在application.yml里直接设置值的变量

public class Configuration {protected Environment environment;protected boolean safeRowBoundsEnabled;protected boolean safeResultHandlerEnabled;protected boolean mapUnderscoreToCamelCase;protected boolean aggressiveLazyLoading;protected boolean multipleResultSetsEnabled;protected boolean useGeneratedKeys;protected boolean useColumnLabel;protected boolean cacheEnabled;protected boolean callSettersOnNulls;protected boolean useActualParamName;protected boolean returnInstanceForEmptyRow;protected String logPrefix;protected Class<? extends Log> logImpl;protected Class<? extends VFS> vfsImpl;protected LocalCacheScope localCacheScope;protected JdbcType jdbcTypeForNull;protected Set<String> lazyLoadTriggerMethods;protected Integer defaultStatementTimeout;protected Integer defaultFetchSize;protected ResultSetType defaultResultSetType;protected ExecutorType defaultExecutorType;protected AutoMappingBehavior autoMappingBehavior;protected AutoMappingUnknownColumnBehavior autoMappingUnknownColumnBehavior;protected Properties variables;protected ReflectorFactory reflectorFactory;protected ObjectFactory objectFactory;protected ObjectWrapperFactory objectWrapperFactory;protected boolean lazyLoadingEnabled;protected ProxyFactory proxyFactory;protected String databaseId;protected Class<?> configurationFactory;protected final MapperRegistry mapperRegistry;protected final InterceptorChain interceptorChain;protected final TypeHandlerRegistry typeHandlerRegistry;protected final TypeAliasRegistry typeAliasRegistry;protected final LanguageDriverRegistry languageRegistry;protected final Map<String, MappedStatement> mappedStatements;protected final Map<String, Cache> caches;protected final Map<String, ResultMap> resultMaps;protected final Map<String, ParameterMap> parameterMaps;protected final Map<String, KeyGenerator> keyGenerators;protected final Set<String> loadedResources;protected final Map<String, XNode> sqlFragments;protected final Collection<XMLStatementBuilder> incompleteStatements;protected final Collection<CacheRefResolver> incompleteCacheRefs;protected final Collection<ResultMapResolver> incompleteResultMaps;protected final Collection<MethodResolver> incompleteMethods;protected final Map<String, String> cacheRefMap;
……

这里面的配置有些非常眼熟,比如logImpl,可以使用mybatis.configuration.log-impl直接设置值,那么同理,databaseId是不是也可以使用mybatis.configuration.databaseId设置值?答案是肯定的,而且这样设置值,绕过了databaseIdProvider也可以生效。

最简单的方法

如果你的springboot偏向使用application.yml配置或者使用了spring cloud config,又要兼容多数据库,那么你可以加一条配置

mybatis.configuration.database-id: mysql
或者
mybatis.configuration.database-id: orcale

然后在你的mapper中

<select id="selectStudent" databaseId="mysql">select * from student where username = #{username} limit 1
</select>
<select id="selectStudent" databaseId="oracle">select * from student where name = #{username} and rownum<2;
</select>

即可切换数据库,不影响其他任何配置,而且也不用纠结databaseIdProvider里的key应该怎么填写了。


http://www.ppmy.cn/embedded/9865.html

相关文章

配置全局代理上网

#准备一台机器(160)&#xff0c;DNS注释掉 直接访问不行 curl https://www.baidu.com #代理访问OK curl -x 192.141.46.161:8888 https://www.baidu.com #CentOS设置全局代理 vi ~/.bashrc #加入以下内容 export http_proxyhttp://192.141.46.161:8888 export https_proxy…

为什么js无法通过contentDocument获取到iframe内容

最近在开发一个附件预览的页面&#xff0c;考虑到附件的安全性由后端对内容进行了加密&#xff0c;前端负责在页面引入iframe标签来加载数据&#xff0c;在这个过程中需要实时获取到iframe标签的渲染状态来判断什么时候loading结束&#xff1a; 这里我通过标签的方式来获取到i…

nacos配置mysql(windows)

nacos默认是使用的内置数据库derby ,可通过配置修改成mysql,修改成mysql之后&#xff0c;之前配置在derby的数据会丢失 本文使用mysql版本为8.0.22 nacos版本为2.3.1 在mysql里面先创建一个数据库test(名称自定义&#xff0c;和后面配置文件里面的一样就好了) 在上面创建的数据…

实验7-4:补全代码,插入操作

实验7-4&#xff1a;补全代码&#xff0c;插入操作 【问题描述】 初始化一维数组中的9个元素a[10]{2,5,6,8,11,15,17,20,25}&#xff0c;要求该数组已经按升序排列&#xff0c;从键盘输入一个整数num&#xff0c;并将其插入到数组a中&#xff0c;要求插入操作完成后&#xff0…

lazarus-ide简介

Lazarus是一个集成开发环境&#xff08;IDE&#xff09;&#xff0c;专为使用Free Pascal编译器的Pascal语言设计。它支持快速应用开发&#xff08;RAD&#xff09;&#xff0c;允许开发者创建跨平台的图形用户界面&#xff08;GUI&#xff09;应用程序。以下是关于Lazarus的来…

null和undefined区别

首先 Undefined 和 Null 都是基本数据类型&#xff0c;这两个基本数据类型分别都只有一个值&#xff0c;就是 undefined 和 null。 undefined 代表的含义是未定义&#xff0c;null 代表的含义是空对象。 一般变量声明了但还没有定义的时候会返回 undefined&#xff0c;null主要…

【C++ STL序列容器】array 数组

文章目录 【 1. 基本原理 】【 2. array 的创建 】2.1 不赋初值2.2 赋默认值2.3 赋指定值 【 3. array 的成员函数 】实例 【 1. 基本原理 】 array 是在 C 普通数组的基础上添加了一些成员函数和全局函数。在使用上&#xff0c;它 比普通数组更 安全&#xff0c;且效率并没…

海尔推行TPM管理的经验分享:打造高效制造新标杆

海尔集团&#xff0c;作为家电行业的佼佼者&#xff0c;其推行TPM&#xff08;全面生产维护&#xff09;管理的成功经验&#xff0c;无疑为众多寻求管理突破的企业提供了宝贵的借鉴。 一、TPM管理的核心理念 TPM&#xff08;Total Productive Maintenance&#xff09;即全面生…