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


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

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

    常规方法

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

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

    1. <databaseIdProvider type="DB_VENDOR">
    2. <property name="MySQL" value="mysql"/>
    3. <property name="Oracle" value="oracle" />
    4. databaseIdProvider>

    然后在mapper中:

    1. <select id="selectStudent" databaseId="mysql">
    2. select * from student where name = #{name} limit 1
    3. select>
    4. <select id="selectStudent" databaseId="oracle">
    5. select * from student where name = #{name} and rownum < 2
    6. select>

    2.创建mybatis的配置类:

    1. import org.apache.ibatis.mapping.DatabaseIdProvider;
    2. import org.apache.ibatis.mapping.VendorDatabaseIdProvider;
    3. import org.apache.ibatis.session.SqlSessionFactory;
    4. import org.mybatis.spring.SqlSessionFactoryBean;
    5. import org.springframework.context.annotation.Bean;
    6. import org.springframework.context.annotation.Configuration;
    7. import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
    8. import javax.sql.DataSource;
    9. import java.util.Properties;
    10. @Configuration
    11. public class MyBatisConfig {
    12. @Bean
    13. public DatabaseIdProvider databaseIdProvider() {
    14. VendorDatabaseIdProvider provider = new VendorDatabaseIdProvider();
    15. Properties props = new Properties();
    16. props.setProperty("Oracle", "oracle");
    17. props.setProperty("MySQL", "mysql");
    18. props.setProperty("PostgreSQL", "postgresql");
    19. props.setProperty("DB2", "db2");
    20. props.setProperty("SQL Server", "sqlserver");
    21. provider.setProperties(props);
    22. return provider;
    23. }
    24. @Bean
    25. public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception {
    26. SqlSessionFactoryBean factoryBean = new SqlSessionFactoryBean();
    27. factoryBean.setDataSource(dataSource);
    28. factoryBean.setMapperLocations(
    29. new PathMatchingResourcePatternResolver().getResources("classpath*:mapper/*Mapper.xml"));
    30. factoryBean.setDatabaseIdProvider(databaseIdProvider());
    31. return factoryBean.getObject();
    32. }
    33. }

    这两种方法,包括在mybatis的github和官方文档的说明,都是看得一头雾水,因为前后无因果关系,DB_VENDOR这种约定好的字段也显得很奇怪,为什么要配置DB_VENDOR?为什么mysql需要写键值对?键值对的key是从那里来的?全网都没有太清晰的说明。

    一些发现

    有没有更简单的办法?

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

    1. public SqlSessionFactory build(Configuration config) {
    2. return new DefaultSqlSessionFactory(config);
    3. }

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

    1. public class Configuration {
    2. protected Environment environment;
    3. protected boolean safeRowBoundsEnabled;
    4. protected boolean safeResultHandlerEnabled;
    5. protected boolean mapUnderscoreToCamelCase;
    6. protected boolean aggressiveLazyLoading;
    7. protected boolean multipleResultSetsEnabled;
    8. protected boolean useGeneratedKeys;
    9. protected boolean useColumnLabel;
    10. protected boolean cacheEnabled;
    11. protected boolean callSettersOnNulls;
    12. protected boolean useActualParamName;
    13. protected boolean returnInstanceForEmptyRow;
    14. protected String logPrefix;
    15. protected Classextends Log> logImpl;
    16. protected Classextends VFS> vfsImpl;
    17. protected LocalCacheScope localCacheScope;
    18. protected JdbcType jdbcTypeForNull;
    19. protected Set lazyLoadTriggerMethods;
    20. protected Integer defaultStatementTimeout;
    21. protected Integer defaultFetchSize;
    22. protected ResultSetType defaultResultSetType;
    23. protected ExecutorType defaultExecutorType;
    24. protected AutoMappingBehavior autoMappingBehavior;
    25. protected AutoMappingUnknownColumnBehavior autoMappingUnknownColumnBehavior;
    26. protected Properties variables;
    27. protected ReflectorFactory reflectorFactory;
    28. protected ObjectFactory objectFactory;
    29. protected ObjectWrapperFactory objectWrapperFactory;
    30. protected boolean lazyLoadingEnabled;
    31. protected ProxyFactory proxyFactory;
    32. protected String databaseId;
    33. protected Class configurationFactory;
    34. protected final MapperRegistry mapperRegistry;
    35. protected final InterceptorChain interceptorChain;
    36. protected final TypeHandlerRegistry typeHandlerRegistry;
    37. protected final TypeAliasRegistry typeAliasRegistry;
    38. protected final LanguageDriverRegistry languageRegistry;
    39. protected final Map mappedStatements;
    40. protected final Map caches;
    41. protected final Map resultMaps;
    42. protected final Map parameterMaps;
    43. protected final Map keyGenerators;
    44. protected final Set loadedResources;
    45. protected final Map sqlFragments;
    46. protected final Collection incompleteStatements;
    47. protected final Collection incompleteCacheRefs;
    48. protected final Collection incompleteResultMaps;
    49. protected final Collection incompleteMethods;
    50. protected final Map cacheRefMap;
    51. ……

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

    最简单的方法

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

    1. mybatis.configuration.database-id: mysql
    2. 或者
    3. mybatis.configuration.database-id: orcale

    然后在你的mapper中

    1. <select id="selectStudent" databaseId="mysql">
    2. select * from student where name = #{name} limit 1
    3. select>
    4. <select id="selectStudent" databaseId="oracle">
    5. select * from student where name = #{name} and rownum < 2
    6. select>
    7. 或者
    8. <select id="selectStudent">
    9. select * from student where
    10. <if test="_databaseId=='mysql'">
    11. name = #{name} limit 1
    12. if>
    13. <if test="_databaseId=='oracle'">
    14. name = #{name} and rownum < 2
    15. if>
    16. select>

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

  • 相关阅读:
    SIMULIA现实仿真解决方案 SIMULIA仿真模拟应用程序
    《HelloGitHub》第 90 期
    生产实用Shell脚本合集
    异步编程:CompletableFuture详解使用
    项目分析(嵌入式产品Web化)
    vector模拟(C++)和注意问题
    【Excel】如何画不同时序交叉的百分比堆积柱状图
    2271. 毯子覆盖的最多白色砖块数-快速排序+滑动窗口,力扣双百代码
    Java - 根据输入的月份数字,转成开始时间和结束时间,比如输入12个月,得到2023-5和2024-5
    用Unity同时开发【微信小游戏】【安卓】【IOS】游戏#5.5.2 组件拓展
  • 原文地址:https://blog.csdn.net/Variazioni/article/details/138091008