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