001/* 002 * Copyright (c) 2022-2025, Mybatis-Flex (fuhai999@gmail.com). 003 * <p> 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * <p> 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * <p> 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016package com.mybatisflex.core.dialect; 017 018 019import com.mybatisflex.core.FlexGlobalConfig; 020import com.mybatisflex.core.exception.FlexExceptions; 021import com.mybatisflex.core.exception.locale.LocalizedFormats; 022import com.mybatisflex.core.util.StringUtil; 023import org.apache.ibatis.datasource.unpooled.UnpooledDataSource; 024import org.apache.ibatis.logging.LogFactory; 025 026import javax.sql.DataSource; 027import java.lang.reflect.Method; 028import java.sql.Connection; 029import java.sql.PreparedStatement; 030import java.sql.ResultSet; 031import java.util.regex.Pattern; 032 033/** 034 * DbType 解析 工具类 035 */ 036public class DbTypeUtil { 037 038 private DbTypeUtil() { 039 } 040 041 /** 042 * 获取当前数据库类型 043 * <p>首先从全局配置中获取数据库类型,如果全局配置中未设置,则尝试从方言工厂中获取线程局部变量设置的数据库类型 044 * 045 * @return 当前数据库类型,可能为null 046 */ 047 public static DbType getCurrentDbType() { 048 DbType dbType = FlexGlobalConfig.getDefaultConfig().getDbType(); 049 if (dbType == null) { 050 dbType = DialectFactory.getHintDbType(); 051 } 052 return dbType; 053 } 054 055 /** 056 * 获取当前配置的 DbType 057 */ 058 public static DbType getDbType(DataSource dataSource) { 059 String jdbcUrl = getJdbcUrl(dataSource); 060 if (StringUtil.hasText(jdbcUrl)) { 061 //FIX [Bug]: sqlserver2022下方言识别不对,手动set也无效 https://gitee.com/mybatis-flex/mybatis-flex/issues/IBIHW3 062 if (jdbcUrl.contains(":sqlserver:")) { 063 DbType sqlserverDbType = getSqlserverDbType(dataSource); 064 if (sqlserverDbType != null) { 065 return sqlserverDbType; 066 } 067 } 068 return parseDbType(jdbcUrl); 069 } 070 071 throw new IllegalStateException("Can not get dataSource jdbcUrl: " + dataSource.getClass().getName()); 072 } 073 074 /** 075 * 通过数据源获取 SQLserver 版本 076 * 077 * @return DbType 078 */ 079 private static DbType getSqlserverDbType(DataSource dataSource) { 080 try (Connection connection = dataSource.getConnection(); 081 PreparedStatement preparedStatement = connection.prepareStatement("SELECT @@VERSION"); 082 ResultSet resultSet = preparedStatement.executeQuery()) { 083 //SELECT @@VERSION 查询返回信息: 084 /* 085 Microsoft SQL Server 2019 (RTM) - 15.0.2000.5 (X64) 086 Sep 24 2019 13:48:23 087 Copyright (C) 2019 Microsoft Corporation 088 Enterprise Edition (64-bit) on Windows Server 2019 Datacenter 10.0 <X64> (Build 17763: ) (Hypervisor) 089 */ 090 if (resultSet.next()) { 091 String version = resultSet.getString(1); 092 if (StringUtil.hasText(version)) { 093 String year = version.substring(21, 25); 094 if (StringUtil.hasText(year) && year.compareTo("2008") <= 0) { 095 return DbType.SQLSERVER_2005; 096 } 097 } 098 } 099 return DbType.SQLSERVER; 100 } catch (Exception e) { 101 LogFactory.getLog(DbTypeUtil.class).warn("Failed to get SQLServer version. parse by url. " + e); 102 return null; 103 } 104 } 105 106 /** 107 * 通过数据源中获取 jdbc 的 url 配置 108 * 符合 HikariCP, druid, c3p0, DBCP, beecp 数据源框架 以及 MyBatis UnpooledDataSource 的获取规则 109 * UnpooledDataSource 参考 @{@link UnpooledDataSource#getUrl()} 110 * 111 * @return jdbc url 配置 112 */ 113 public static String getJdbcUrl(DataSource dataSource) { 114 String[] methodNames = new String[]{"getUrl", "getJdbcUrl"}; 115 for (String methodName : methodNames) { 116 try { 117 Method method = dataSource.getClass().getMethod(methodName); 118 return (String) method.invoke(dataSource); 119 } catch (Exception e) { 120 //ignore 121 } 122 } 123 124 try (Connection connection = dataSource.getConnection()) { 125 return connection.getMetaData().getURL(); 126 } catch (Exception e) { 127 throw FlexExceptions.wrap(e, LocalizedFormats.DATASOURCE_JDBC_URL); 128 } 129 } 130 131 132 /** 133 * 参考 druid 和 MyBatis-plus 的 JdbcUtils 134 * {@link com.alibaba.druid.util.JdbcUtils#getDbType(String, String)} 135 * {@link com.baomidou.mybatisplus.extension.toolkit.JdbcUtils#getDbType(String)} 136 * 137 * @param jdbcUrl jdbcURL 138 * @return 返回数据库类型 139 */ 140 public static DbType parseDbType(String jdbcUrl) { 141 jdbcUrl = jdbcUrl.toLowerCase(); 142 if (jdbcUrl.contains(":ch:") || jdbcUrl.contains(":clickhouse:")) { 143 return DbType.CLICK_HOUSE; 144 } else if (jdbcUrl.contains(":cobar:")) { 145 return DbType.MYSQL; 146 } else if (jdbcUrl.contains(":csiidb:")) { 147 return DbType.CSIIDB; 148 } else if (jdbcUrl.contains(":cubrid:")) { 149 return DbType.CUBRID; 150 } else if (jdbcUrl.contains(":db2:")) { 151 return DbType.DB2; 152 } else if (jdbcUrl.contains(":derby:")) { 153 return DbType.DERBY; 154 } else if (isMatchedRegex(":dm\\d*:", jdbcUrl)) { 155 return DbType.DM; 156 } else if (jdbcUrl.contains(":duckdb:")) { 157 return DbType.DUCKDB; 158 } else if (jdbcUrl.contains(":firebirdsql:")) { 159 return DbType.FIREBIRD; 160 } else if (jdbcUrl.contains(":gaussdb:") || jdbcUrl.contains(":zenith:")) { 161 return DbType.GAUSS; 162 } else if (jdbcUrl.contains(":gbase:")) { 163 return DbType.GBASE; 164 } else if (jdbcUrl.contains(":gbase8c:")) { 165 return DbType.GBASE_8C; 166 } else if (jdbcUrl.contains(":gbase8s-pg:")) { 167 return DbType.GBASE_8S_PG; 168 } else if (jdbcUrl.contains(":gbasedbt-sqli:") || jdbcUrl.contains(":informix-sqli:")) { 169 return DbType.GBASE_8S; 170 } else if (jdbcUrl.contains(":goldendb:")) { 171 return DbType.GOLDENDB; 172 } else if (jdbcUrl.contains(":goldilocks:")) { 173 return DbType.GOLDILOCKS; 174 } else if (jdbcUrl.contains(":greenplum:")) { 175 return DbType.GREENPLUM; 176 } else if (jdbcUrl.contains(":h2:")) { 177 return DbType.H2; 178 } else if (jdbcUrl.contains(":highgo:")) { 179 return DbType.HIGH_GO; 180 } else if (jdbcUrl.contains(":hive2:") || jdbcUrl.contains(":inceptor2:")) { 181 return DbType.HIVE; 182 } else if (jdbcUrl.contains(":hsqldb:")) { 183 return DbType.HSQL; 184 } else if (jdbcUrl.contains(":impala:")) { 185 return DbType.IMPALA; 186 } else if (jdbcUrl.contains(":informix")) { 187 return DbType.INFORMIX; 188 } else if (isMatchedRegex(":kingbase\\d*:", jdbcUrl)) { 189 return DbType.KINGBASE_ES; 190 } else if (jdbcUrl.contains(":lealone:")) { 191 return DbType.LEALONE; 192 } else if (jdbcUrl.contains(":mariadb:")) { 193 return DbType.MARIADB; 194 } else if (jdbcUrl.contains(":mysql:")) { 195 return DbType.MYSQL; 196 } else if (jdbcUrl.contains(":oceanbase:")) { 197 return DbType.OCEAN_BASE; 198 } else if (jdbcUrl.contains(":opengauss:")) { 199 return DbType.OPENGAUSS; 200 } else if (jdbcUrl.contains(":oracle:")) { 201 return DbType.ORACLE; 202 } else if (jdbcUrl.contains(":oscar:")) { 203 return DbType.OSCAR; 204 } else if (jdbcUrl.contains(":phoenix:")) { 205 return DbType.PHOENIX; 206 } else if (jdbcUrl.contains(":postgresql:")) { 207 return DbType.POSTGRE_SQL; 208 } else if (jdbcUrl.contains(":presto:")) { 209 return DbType.PRESTO; 210 } else if (jdbcUrl.contains(":redshift:")) { 211 return DbType.REDSHIFT; 212 } else if (jdbcUrl.contains(":sap:")) { 213 return DbType.SAP_HANA; 214 } else if (jdbcUrl.contains(":sinodb")) { 215 return DbType.SINODB; 216 } else if (jdbcUrl.contains(":sqlite:")) { 217 return DbType.SQLITE; 218 } else if (jdbcUrl.contains(":sqlserver:")) { 219 return DbType.SQLSERVER_2005; 220 } else if (jdbcUrl.contains(":sqlserver2012:")) { 221 return DbType.SQLSERVER; 222 } else if (jdbcUrl.contains(":sundb:")) { 223 return DbType.SUNDB; 224 } else if (jdbcUrl.contains(":sybase:")) { 225 return DbType.SYBASE; 226 } else if (jdbcUrl.contains(":taos:") || jdbcUrl.contains(":taos-rs:")) { 227 return DbType.TDENGINE; 228 } else if (jdbcUrl.contains(":trino:")) { 229 return DbType.TRINO; 230 } else if (jdbcUrl.contains(":uxdb:")) { 231 return DbType.UXDB; 232 } else if (jdbcUrl.contains(":vastbase:")) { 233 return DbType.VASTBASE; 234 } else if (jdbcUrl.contains(":vertica:")) { 235 return DbType.VERTICA; 236 } else if (jdbcUrl.contains(":xcloud:")) { 237 return DbType.XCloud; 238 } else if (jdbcUrl.contains(":xugu:")) { 239 return DbType.XUGU; 240 } else if (jdbcUrl.contains(":yasdb:")) { 241 return DbType.YASDB; 242 } else { 243 return DbType.OTHER; 244 } 245 } 246 247 /** 248 * 正则匹配,验证成功返回 true,验证失败返回 false 249 */ 250 public static boolean isMatchedRegex(String regex, String jdbcUrl) { 251 if (null == jdbcUrl) { 252 return false; 253 } 254 return Pattern.compile(regex).matcher(jdbcUrl).find(); 255 } 256 257}