001/* 002 * Copyright 2012-2018 the original author or authors. 003 * 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 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 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 */ 016 017package org.springframework.boot.jdbc; 018 019import java.util.Arrays; 020import java.util.Collection; 021import java.util.Collections; 022import java.util.Locale; 023 024import org.springframework.util.Assert; 025import org.springframework.util.StringUtils; 026 027/** 028 * Enumeration of common database drivers. 029 * 030 * @author Phillip Webb 031 * @author Maciej Walkowiak 032 * @author Marten Deinum 033 * @author Stephane Nicoll 034 * @since 1.4.0 035 */ 036public enum DatabaseDriver { 037 038 /** 039 * Unknown type. 040 */ 041 UNKNOWN(null, null), 042 043 /** 044 * Apache Derby. 045 */ 046 DERBY("Apache Derby", "org.apache.derby.jdbc.EmbeddedDriver", 047 "org.apache.derby.jdbc.EmbeddedXADataSource", 048 "SELECT 1 FROM SYSIBM.SYSDUMMY1"), 049 050 /** 051 * H2. 052 */ 053 H2("H2", "org.h2.Driver", "org.h2.jdbcx.JdbcDataSource", "SELECT 1"), 054 055 /** 056 * HyperSQL DataBase. 057 */ 058 HSQLDB("HSQL Database Engine", "org.hsqldb.jdbc.JDBCDriver", 059 "org.hsqldb.jdbc.pool.JDBCXADataSource", 060 "SELECT COUNT(*) FROM INFORMATION_SCHEMA.SYSTEM_USERS"), 061 062 /** 063 * SQL Lite. 064 */ 065 SQLITE("SQLite", "org.sqlite.JDBC"), 066 067 /** 068 * MySQL. 069 */ 070 MYSQL("MySQL", "com.mysql.cj.jdbc.Driver", "com.mysql.cj.jdbc.MysqlXADataSource", 071 "/* ping */ SELECT 1"), 072 073 /** 074 * Maria DB. 075 */ 076 MARIADB("MySQL", "org.mariadb.jdbc.Driver", "org.mariadb.jdbc.MariaDbDataSource", 077 "SELECT 1") { 078 079 @Override 080 public String getId() { 081 return "mysql"; 082 } 083 }, 084 085 /** 086 * Google App Engine. 087 */ 088 GAE(null, "com.google.appengine.api.rdbms.AppEngineDriver"), 089 090 /** 091 * Oracle. 092 */ 093 ORACLE("Oracle", "oracle.jdbc.OracleDriver", 094 "oracle.jdbc.xa.client.OracleXADataSource", "SELECT 'Hello' from DUAL"), 095 096 /** 097 * Postgres. 098 */ 099 POSTGRESQL("PostgreSQL", "org.postgresql.Driver", "org.postgresql.xa.PGXADataSource", 100 "SELECT 1"), 101 102 /** 103 * HANA - SAP HANA Database - HDB. 104 * @since 2.1.0 105 */ 106 HANA("HDB", "com.sap.db.jdbc.Driver", "com.sap.db.jdbcext.XADataSourceSAP", 107 "SELECT 1 FROM SYS.DUMMY") { 108 @Override 109 protected Collection<String> getUrlPrefixes() { 110 return Collections.singleton("sap"); 111 } 112 }, 113 114 /** 115 * jTDS. As it can be used for several databases, there isn't a single product name we 116 * could rely on. 117 */ 118 JTDS(null, "net.sourceforge.jtds.jdbc.Driver"), 119 120 /** 121 * SQL Server. 122 */ 123 SQLSERVER("Microsoft SQL Server", "com.microsoft.sqlserver.jdbc.SQLServerDriver", 124 "com.microsoft.sqlserver.jdbc.SQLServerXADataSource", "SELECT 1") { 125 126 @Override 127 protected boolean matchProductName(String productName) { 128 return super.matchProductName(productName) 129 || "SQL SERVER".equalsIgnoreCase(productName); 130 131 } 132 133 }, 134 135 /** 136 * Firebird. 137 */ 138 FIREBIRD("Firebird", "org.firebirdsql.jdbc.FBDriver", 139 "org.firebirdsql.ds.FBXADataSource", "SELECT 1 FROM RDB$DATABASE") { 140 141 @Override 142 protected Collection<String> getUrlPrefixes() { 143 return Collections.singleton("firebirdsql"); 144 } 145 146 @Override 147 protected boolean matchProductName(String productName) { 148 return super.matchProductName(productName) 149 || productName.toLowerCase(Locale.ENGLISH).startsWith("firebird"); 150 } 151 }, 152 153 /** 154 * DB2 Server. 155 */ 156 DB2("DB2", "com.ibm.db2.jcc.DB2Driver", "com.ibm.db2.jcc.DB2XADataSource", 157 "SELECT 1 FROM SYSIBM.SYSDUMMY1") { 158 159 @Override 160 protected boolean matchProductName(String productName) { 161 return super.matchProductName(productName) 162 || productName.toLowerCase(Locale.ENGLISH).startsWith("db2/"); 163 } 164 }, 165 166 /** 167 * DB2 AS400 Server. 168 */ 169 DB2_AS400("DB2 UDB for AS/400", "com.ibm.as400.access.AS400JDBCDriver", 170 "com.ibm.as400.access.AS400JDBCXADataSource", 171 "SELECT 1 FROM SYSIBM.SYSDUMMY1") { 172 173 @Override 174 public String getId() { 175 return "db2"; 176 } 177 178 @Override 179 protected Collection<String> getUrlPrefixes() { 180 return Collections.singleton("as400"); 181 } 182 183 @Override 184 protected boolean matchProductName(String productName) { 185 return super.matchProductName(productName) 186 || productName.toLowerCase(Locale.ENGLISH).contains("as/400"); 187 } 188 }, 189 190 /** 191 * Teradata. 192 */ 193 TERADATA("Teradata", "com.teradata.jdbc.TeraDriver"), 194 195 /** 196 * Informix. 197 */ 198 INFORMIX("Informix Dynamic Server", "com.informix.jdbc.IfxDriver", null, 199 "select count(*) from systables") { 200 201 @Override 202 protected Collection<String> getUrlPrefixes() { 203 return Arrays.asList("informix-sqli", "informix-direct"); 204 } 205 206 }; 207 208 private final String productName; 209 210 private final String driverClassName; 211 212 private final String xaDataSourceClassName; 213 214 private final String validationQuery; 215 216 DatabaseDriver(String productName, String driverClassName) { 217 this(productName, driverClassName, null); 218 } 219 220 DatabaseDriver(String productName, String driverClassName, 221 String xaDataSourceClassName) { 222 this(productName, driverClassName, xaDataSourceClassName, null); 223 } 224 225 DatabaseDriver(String productName, String driverClassName, 226 String xaDataSourceClassName, String validationQuery) { 227 this.productName = productName; 228 this.driverClassName = driverClassName; 229 this.xaDataSourceClassName = xaDataSourceClassName; 230 this.validationQuery = validationQuery; 231 } 232 233 /** 234 * Return the identifier of this driver. 235 * @return the identifier 236 */ 237 public String getId() { 238 return name().toLowerCase(Locale.ENGLISH); 239 } 240 241 protected boolean matchProductName(String productName) { 242 return this.productName != null && this.productName.equalsIgnoreCase(productName); 243 } 244 245 protected Collection<String> getUrlPrefixes() { 246 return Collections.singleton(this.name().toLowerCase(Locale.ENGLISH)); 247 } 248 249 /** 250 * Return the driver class name. 251 * @return the class name or {@code null} 252 */ 253 public String getDriverClassName() { 254 return this.driverClassName; 255 } 256 257 /** 258 * Return the XA driver source class name. 259 * @return the class name or {@code null} 260 */ 261 public String getXaDataSourceClassName() { 262 return this.xaDataSourceClassName; 263 } 264 265 /** 266 * Return the validation query. 267 * @return the validation query or {@code null} 268 */ 269 public String getValidationQuery() { 270 return this.validationQuery; 271 } 272 273 /** 274 * Find a {@link DatabaseDriver} for the given URL. 275 * @param url the JDBC URL 276 * @return the database driver or {@link #UNKNOWN} if not found 277 */ 278 public static DatabaseDriver fromJdbcUrl(String url) { 279 if (StringUtils.hasLength(url)) { 280 Assert.isTrue(url.startsWith("jdbc"), "URL must start with 'jdbc'"); 281 String urlWithoutPrefix = url.substring("jdbc".length()) 282 .toLowerCase(Locale.ENGLISH); 283 for (DatabaseDriver driver : values()) { 284 for (String urlPrefix : driver.getUrlPrefixes()) { 285 String prefix = ":" + urlPrefix + ":"; 286 if (driver != UNKNOWN && urlWithoutPrefix.startsWith(prefix)) { 287 return driver; 288 } 289 } 290 } 291 } 292 return UNKNOWN; 293 } 294 295 /** 296 * Find a {@link DatabaseDriver} for the given product name. 297 * @param productName product name 298 * @return the database driver or {@link #UNKNOWN} if not found 299 */ 300 public static DatabaseDriver fromProductName(String productName) { 301 if (StringUtils.hasLength(productName)) { 302 for (DatabaseDriver candidate : values()) { 303 if (candidate.matchProductName(productName)) { 304 return candidate; 305 } 306 } 307 } 308 return UNKNOWN; 309 } 310 311}