Last active
August 27, 2022 20:17
-
-
Save lbruun/9a7775b0bc3aca868c9500bfa2df42a0 to your computer and use it in GitHub Desktop.
DatabaseEngine enum and detection of database engine
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* Copyright 2021 lbruun.net. | |
* | |
* Licensed under the Apache License, Version 2.0 (the "License"); | |
* you may not use this file except in compliance with the License. | |
* You may obtain a copy of the License at | |
* | |
* http://www.apache.org/licenses/LICENSE-2.0 | |
* | |
* Unless required by applicable law or agreed to in writing, software | |
* distributed under the License is distributed on an "AS IS" BASIS, | |
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
* See the License for the specific language governing permissions and | |
* limitations under the License. | |
*/ | |
package net.lbruun.jdbc; | |
import java.sql.Connection; | |
import java.sql.DatabaseMetaData; | |
import java.sql.SQLException; | |
import java.util.Locale; | |
import java.util.Objects; | |
import javax.sql.DataSource; | |
/** | |
* Database RDBMS engines. | |
* | |
* <p> | |
* Provides enums for most widely uses RDBMSs and methods to detect | |
* these from their {@link java.sql.DatabaseMetaData} signature. | |
* | |
* @author lbruun | |
*/ | |
public enum DatabaseEngine { | |
/** | |
* PostgreSQL database | |
*/ | |
POSTGRESQL(Codes.POSTGRESQL_CODE), | |
/** | |
* Microsoft SQL Server database | |
*/ | |
MSSQL(Codes.MSSQL_CODE), | |
/** | |
* Oracle RDBMS database | |
*/ | |
ORACLE(Codes.ORACLE_CODE), | |
/** | |
* MySQL database | |
*/ | |
MYSQL(Codes.MYSQL_CODE), | |
/** | |
* MariaDB database | |
*/ | |
MARIADB(Codes.MARIADB_CODE), | |
/** | |
* Db2 on Linux, Unix and Windows systems | |
*/ | |
DB2_LUW(Codes.DB2_LUW_CODE), | |
/** | |
* Db2 on z/OS | |
*/ | |
DB2_ZOS(Codes.DB2_ZOS_CODE), | |
/** | |
* H2 database | |
*/ | |
H2(Codes.H2_CODE), | |
/** | |
* Amazon Redshift database | |
*/ | |
REDSHIFT(Codes.REDSHIFT_CODE), | |
/** | |
* Apache Derby database | |
*/ | |
DERBY(Codes.DERBY_CODE), | |
/** | |
* HyperSQL database | |
*/ | |
HSQLDB(Codes.HSQLDB_CODE); | |
/** | |
* Unique string codes for database engines. | |
* <p> | |
* By convention these codes are in lower-case. | |
*/ | |
public static class Codes { | |
/** | |
* PostgreSQL database | |
*/ | |
public static final String POSTGRESQL_CODE = "postgresql"; | |
/** | |
* Microsoft SQL Server database | |
*/ | |
public static final String MSSQL_CODE = "mssql"; | |
/** | |
* Oracle RDBMS database | |
*/ | |
public static final String ORACLE_CODE = "oracle"; | |
/** | |
* MySQL database | |
*/ | |
public static final String MYSQL_CODE = "mysql"; | |
/** | |
* MariaDB database | |
*/ | |
public static final String MARIADB_CODE = "mariadb"; | |
/** | |
* Db2 on Linux, Unix and Windows systems | |
*/ | |
public static final String DB2_LUW_CODE = "db2"; | |
/** | |
* Db2 on z/OS | |
*/ | |
public static final String DB2_ZOS_CODE = "db2z"; | |
/** | |
* H2 database | |
*/ | |
public static final String H2_CODE = "h2"; | |
/** | |
* Amazon Redshift | |
*/ | |
public static final String REDSHIFT_CODE = "redshift"; | |
/** | |
* Apache Derby | |
*/ | |
public static final String DERBY_CODE = "derby"; | |
/** | |
* HyperSQL database | |
*/ | |
public static final String HSQLDB_CODE = "hsqldb"; | |
} | |
private String code; | |
DatabaseEngine(String code) { | |
this.code = code; | |
} | |
/** | |
* Gets the string code for the Database Engine. | |
* @return | |
*/ | |
public String getCode() { | |
return code; | |
} | |
/** | |
* Gets the {@code DatabaseEngine} which corresponds to | |
* a code value such as "mysql". | |
* @param code string code representing a unique value for a | |
* database engine. | |
* @return the DatabaseEngine corresponding to the code value | |
* or {@code null} if no match for the code value was found. | |
*/ | |
public static DatabaseEngine getDatabaseEngineFromCode(String code) { | |
if (code == null) { | |
return null; | |
} | |
for(DatabaseEngine dbEngine : DatabaseEngine.values()) { | |
if (dbEngine.getCode().equals(code)) { | |
return dbEngine; | |
} | |
} | |
return null; | |
} | |
/** | |
* Find {@code DatabaseEngine} from a JDBC DatabaseMetaData | |
* object. | |
* | |
* @return the database engine or {@code null} if the database cannot | |
* be determined from the input. | |
* @throws SQLException if an error occurs while retrieving data from the | |
* {@code databaseMetaData} object. | |
*/ | |
public static DatabaseEngine getDatabaseEngine(DatabaseMetaData databaseMetaData) throws SQLException { | |
// Sanity check | |
Objects.requireNonNull(databaseMetaData, "databaseProductName cannot be null"); | |
String databaseProductName = databaseMetaData.getDatabaseProductName(); | |
String databaseProductVersion= databaseMetaData.getDatabaseProductVersion(); | |
String databaseProductNameLower = databaseProductName.toLowerCase(Locale.US); | |
// Detect MySQL | |
// Note: the MySQL JDBC driver (known as Connector/J) is open-sourced | |
// and available here: https://github.com/mysql/mysql-connector-j | |
// so pretty easy to see what the driver returns for ProductName. | |
if (databaseProductName.equals("MySQL")) { | |
return MYSQL; | |
} | |
// Detect MariaDB | |
if (databaseProductName.equals("MariaDB")) { | |
return MARIADB; | |
} | |
// Detect MS SQL Server | |
// Note: The MS SQL Server has been open-sourced and is now available | |
// here: https://github.com/microsoft/mssql-jdbc so it is pretty easy | |
// to see what the driver returns for ProductName. | |
if (databaseProductName.equals("Microsoft SQL Server")) { | |
return MSSQL; | |
} | |
// Detect Oracle RDBMS | |
if (databaseProductNameLower.startsWith("oracle")) { | |
return ORACLE; | |
} | |
// Detect PostgreSQL | |
// According to documentation on the PgDatabaseMetaData class the | |
// ProductName will always be "PostgreSQL". | |
if (databaseProductName.equals("PostgreSQL")) { | |
return POSTGRESQL; | |
} | |
// Detect H2 | |
// ProductName will always be "H2". | |
if (databaseProductName.equals("H2")) { | |
return H2; | |
} | |
// Detect Amazon Redshift | |
// ProductName will always be "Redshift". | |
// See https://github.com/aws/amazon-redshift-jdbc-driver/blob/master/src/main/java/com/amazon/redshift/jdbc/RedshiftDatabaseMetaData.java | |
// (Amazon Redshift used to ask users to use the PostgreSQL JDBC driver | |
// - which will always return "PostgreSQL" - but nowadays they have their | |
// own dedicated JDBC driver) | |
if (databaseProductName.equals("Redshift")) { | |
return REDSHIFT; | |
} | |
// Detect Apache Derby | |
if (databaseProductName.equals("Apache Derby")) { | |
return DERBY; | |
} | |
// Detect HyperSQL | |
// The HSQLDB JDBC driver always returns the value of | |
// org.hsqldb.persist.HsqlDatabaseProperties#PRODUCT_NAME | |
if (databaseProductNameLower.equals("hsql database engine")) { | |
return HSQLDB; | |
} | |
// Detect IBM Db2 | |
// According to IBM's documentation the database engine must be detected | |
// primarily from the ProductVersion value whereas IBM makes no real | |
// promise on the value of the ProductName. This is why we use the ProductName | |
// with some caution below. | |
// See : https://www.ibm.com/docs/en/db2-for-zos/11?topic=methods-databasemetadata-identifying-type-data-server | |
if ( databaseProductNameLower.equals("db2") | |
| databaseProductNameLower.matches("db2[.-/ ].*") ) { | |
if (databaseProductVersion != null && databaseProductVersion.length() > 3) { | |
String ppp = databaseProductVersion.substring(0, 3); | |
switch (ppp) { | |
case "DSN": | |
return DB2_ZOS; | |
case "SQL": | |
return DB2_LUW; | |
default: | |
} | |
} | |
} | |
// Cannot determine | |
return null; | |
} | |
/** | |
* Find {@code DatabaseEngine} from a JDBC Connection. | |
* The connection is left untouched (not closed by this method). | |
* | |
* @return the database engine or {@code null} if the database cannot | |
* be determined from the input. | |
* @throws SQLException if an error occurs while retrieving data from the | |
* {@code connection} object. | |
*/ | |
public static DatabaseEngine getDatabaseEngine(Connection connection) throws SQLException { | |
Objects.requireNonNull(connection, "connection cannot be null"); | |
return getDatabaseEngine(connection.getMetaData()); | |
} | |
/** | |
* Find {@code DatabaseEngine} from a JDBC DataSource. | |
* | |
* @param dataSource opens a connection from this DataSource. The connection | |
* is closed again before the the method returns. | |
* @param username username for connecting to database engine. May be | |
* {@code null} in which case a connection is obtained from the DataSource | |
* without explicitly specifying username/password. | |
* @param password password for connecting to database engine. May be | |
* {@code null} in which case a connection is obtained from the DataSource | |
* without explicitly specifying username/password. | |
* @return the database engine or {@code null} if the database cannot | |
* be determined from the input. | |
* @throws SQLException if an error occurs while retrieving data from the | |
* {@code dataSource} object. | |
*/ | |
public static DatabaseEngine getDatabaseEngine(DataSource dataSource, String username, String password) throws SQLException { | |
Objects.requireNonNull(dataSource, "dataSource cannot be null"); | |
try (Connection connection | |
= (username == null) ? dataSource.getConnection() : dataSource.getConnection(username, password)) { | |
return getDatabaseEngine(connection.getMetaData()); | |
} | |
} | |
/** | |
* Find {@code DatabaseEngine} from a JDBC DataSource. | |
* | |
* <p> | |
* Same as calling {@link #getDatabaseEngine(javax.sql.DataSource, java.lang.String, java.lang.String) | |
* getDatabaseEngine(dataSource, null, null)}. | |
* @param dataSource opens a connection from this DataSource. The connection | |
* is closed again before the the method returns. | |
* @return the database engine or {@code null} if the database cannot | |
* be determined from the input. | |
* @throws SQLException if an error occurs while retrieving data from the | |
* {@code dataSource} object. | |
* @see #getDatabaseEngine(javax.sql.DataSource, java.lang.String, java.lang.String) | |
*/ | |
public static DatabaseEngine getDatabaseEngine(DataSource dataSource) throws SQLException { | |
return getDatabaseEngine(dataSource, null, null); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment