Fix up Dialect auto-detection

- make it work for DriverManagerConnectionProviderImpl
- improve logging and some confusing exceptions
- make it respect explicit database setting properties
This commit is contained in:
gavinking 2020-01-28 14:17:39 +01:00 committed by Steve Ebersole
parent 95930820af
commit f6eaaca824
6 changed files with 156 additions and 41 deletions

View File

@ -9,6 +9,7 @@
/**
* @author Steve Ebersole
*/
@FunctionalInterface
public interface StrategyCreator<T> {
T create(Class<? extends T> strategyClass);
}

View File

@ -8,7 +8,9 @@
import java.sql.Connection;
import java.sql.Driver;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.util.Enumeration;
import java.util.Map;
import java.util.Properties;
import java.util.concurrent.ConcurrentLinkedQueue;
@ -22,6 +24,7 @@
import org.hibernate.HibernateException;
import org.hibernate.boot.registry.classloading.spi.ClassLoaderService;
import org.hibernate.cfg.AvailableSettings;
import org.hibernate.dialect.Database;
import org.hibernate.engine.jdbc.connections.spi.ConnectionProvider;
import org.hibernate.internal.CoreLogging;
import org.hibernate.internal.CoreMessageLogger;
@ -101,10 +104,51 @@ private PooledConnections buildPool(Map configurationValues, ServiceRegistryImpl
private static ConnectionCreator buildCreator(Map configurationValues, ServiceRegistryImplementor serviceRegistry) {
final ConnectionCreatorBuilder connectionCreatorBuilder = new ConnectionCreatorBuilder( serviceRegistry );
final String driverClassName = (String) configurationValues.get( AvailableSettings.DRIVER );
connectionCreatorBuilder.setDriver( loadDriverIfPossible( driverClassName, serviceRegistry ) );
final String url = (String) configurationValues.get( AvailableSettings.URL );
String driverClassName = (String) configurationValues.get( AvailableSettings.DRIVER );
boolean success = false;
if ( driverClassName != null ) {
connectionCreatorBuilder.setDriver( loadDriverIfPossible( driverClassName, serviceRegistry ) );
success = true;
}
else if ( url != null ) {
//try to guess the driver class from the JDBC URL
for ( Database database: Database.values() ) {
if ( database.matchesUrl( url ) ) {
driverClassName = database.getDriverClassName( url );
if ( driverClassName != null ) {
try {
connectionCreatorBuilder.setDriver( loadDriverIfPossible(driverClassName, serviceRegistry) );
success = true;
}
catch (Exception e) {
//swallow it, since this was not
//an explicit setting by the user
}
break;
}
}
}
}
if ( success ) {
log.loadedDriver( driverClassName );
}
else {
//we're hoping that the driver is already loaded
log.noDriver( AvailableSettings.DRIVER );
StringBuilder list = new StringBuilder();
Enumeration<Driver> drivers = DriverManager.getDrivers();
while ( drivers.hasMoreElements() ) {
if ( list.length() != 0) {
list.append(", ");
}
list.append( drivers.nextElement().getClass().getName() );
}
log.loadedDrivers( list.toString() );
}
if ( url == null ) {
final String msg = log.jdbcUrlNotSpecified( AvailableSettings.URL );
log.error( msg );
@ -112,7 +156,7 @@ private static ConnectionCreator buildCreator(Map configurationValues, ServiceRe
}
connectionCreatorBuilder.setUrl( url );
log.usingDriver( driverClassName, url );
log.usingUrl( url );
final Properties connectionProps = ConnectionProviderInitiator.getConnectionProperties( configurationValues );
@ -217,7 +261,6 @@ protected void finalize() throws Throwable {
/**
* Exposed to facilitate testing only.
* @return
*/
public Properties getConnectionProperties() {
BasicConnectionCreator connectionCreator = (BasicConnectionCreator) this.state.pool.connectionCreator;

View File

@ -6,10 +6,12 @@
*/
package org.hibernate.engine.jdbc.dialect.internal;
import java.lang.reflect.InvocationTargetException;
import java.util.Map;
import org.hibernate.HibernateException;
import org.hibernate.annotations.common.util.StringHelper;
import org.hibernate.boot.registry.selector.spi.StrategySelectionException;
import org.hibernate.boot.registry.selector.spi.StrategySelector;
import org.hibernate.cfg.AvailableSettings;
import org.hibernate.dialect.Dialect;
@ -48,7 +50,7 @@ public void setDialectResolver(DialectResolver dialectResolver) {
public Dialect buildDialect(Map configValues, DialectResolutionInfoSource resolutionInfoSource) throws HibernateException {
final Object dialectReference = configValues.get( AvailableSettings.DIALECT );
if ( !isEmpty( dialectReference ) ) {
return constructDialect( dialectReference );
return constructDialect( dialectReference, resolutionInfoSource );
}
else {
return determineDialect( resolutionInfoSource );
@ -68,10 +70,32 @@ private boolean isEmpty(Object dialectReference) {
return true;
}
private Dialect constructDialect(Object dialectReference) {
final Dialect dialect;
private Dialect constructDialect(Object dialectReference, DialectResolutionInfoSource resolutionInfoSource) {
try {
dialect = strategySelector.resolveStrategy( Dialect.class, dialectReference );
Dialect dialect = strategySelector.resolveStrategy(
Dialect.class,
dialectReference,
(Dialect) null,
(dialectClass) -> {
try {
try {
if (resolutionInfoSource != null) {
return dialectClass.getConstructor(DialectResolutionInfo.class).newInstance(
resolutionInfoSource.getDialectResolutionInfo()
);
}
}
catch (NoSuchMethodException nsme) {}
return dialectClass.newInstance();
}
catch (InstantiationException | IllegalAccessException | InvocationTargetException e) {
throw new StrategySelectionException(
String.format( "Could not instantiate named dialect class [%s]", dialectClass.getName() ),
e
);
}
}
);
if ( dialect == null ) {
throw new HibernateException( "Unable to construct requested dialect [" + dialectReference + "]" );
}
@ -100,7 +124,6 @@ private Dialect determineDialect(DialectResolutionInfoSource resolutionInfoSourc
throw new HibernateException(
"Unable to determine Dialect without JDBC metadata "
+ "(please set 'javax.persistence.jdbc.url', 'hibernate.connection.url', or 'hibernate.dialect')"
);
}
@ -109,9 +132,9 @@ private Dialect determineDialect(DialectResolutionInfoSource resolutionInfoSourc
if ( dialect == null ) {
throw new HibernateException(
"Unable to determine Dialect to use [name=" + info.getDatabaseName() +
", majorVersion=" + info.getDatabaseMajorVersion() +
"]; user must register resolver or explicitly set 'hibernate.dialect'"
"Unable to determine Dialect for " + info.getDatabaseName() + " "
+ info.getDatabaseMajorVersion() + "." + info.getDatabaseMinorVersion()
+ " (please set 'hibernate.dialect' or register a Dialect resolver)"
);
}

View File

@ -9,7 +9,6 @@
import java.sql.DatabaseMetaData;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.LinkedHashSet;
@ -22,7 +21,6 @@
import org.hibernate.engine.jdbc.env.spi.JdbcEnvironment;
import org.hibernate.engine.jdbc.env.spi.SQLStateType;
import org.hibernate.engine.jdbc.spi.TypeInfo;
import org.hibernate.internal.util.StringHelper;
import org.hibernate.tool.schema.extract.spi.SequenceInformation;
/**
@ -201,8 +199,13 @@ public Builder apply(DatabaseMetaData databaseMetaData) throws SQLException {
doesDataDefinitionCauseTransactionCommit = databaseMetaData.dataDefinitionCausesTransactionCommit();
extraKeywords = parseKeywords( databaseMetaData.getSQLKeywords() );
sqlStateType = SQLStateType.interpretReportedSQLStateType( databaseMetaData.getSQLStateType() );
lobLocatorUpdateCopy = databaseMetaData.locatorsUpdateCopy();
typeInfoSet = new LinkedHashSet<TypeInfo>();
try {
lobLocatorUpdateCopy = databaseMetaData.locatorsUpdateCopy();
}
catch (SQLException sql) {
//ignore, the database might not support this at all
}
typeInfoSet = new LinkedHashSet<>();
typeInfoSet.addAll( TypeInfo.extractTypeInfo( databaseMetaData ) );
return this;
}

View File

@ -14,6 +14,7 @@
import org.hibernate.HibernateException;
import org.hibernate.MultiTenancyStrategy;
import org.hibernate.boot.registry.StandardServiceInitiator;
import org.hibernate.cfg.AvailableSettings;
import org.hibernate.dialect.Dialect;
import org.hibernate.engine.jdbc.connections.spi.ConnectionProvider;
import org.hibernate.engine.jdbc.connections.spi.JdbcConnectionAccess;
@ -62,7 +63,48 @@ public JdbcEnvironment initiateService(Map configurationValues, ServiceRegistryI
true
);
if ( useJdbcMetadata ) {
if ( configurationValues.containsKey( AvailableSettings.HBM2DDL_DB_NAME ) ) {
return new JdbcEnvironmentImpl( registry, dialectFactory.buildDialect(
configurationValues,
() -> new DialectResolutionInfo() {
@Override
public String getDatabaseName() {
return (String) configurationValues.get( AvailableSettings.HBM2DDL_DB_NAME );
}
@Override
public String getDatabaseVersion() {
return (String) configurationValues.getOrDefault( AvailableSettings.HBM2DDL_DB_VERSION, "0" );
}
@Override
public int getDatabaseMajorVersion() {
return (Integer) configurationValues.getOrDefault( AvailableSettings.HBM2DDL_DB_MAJOR_VERSION, 0 );
}
@Override
public int getDatabaseMinorVersion() {
return (Integer) configurationValues.getOrDefault( AvailableSettings.HBM2DDL_DB_MINOR_VERSION, 0 );
}
@Override
public String getDriverName() {
throw new UnsupportedOperationException();
}
@Override
public int getDriverMajorVersion() {
throw new UnsupportedOperationException();
}
@Override
public int getDriverMinorVersion() {
throw new UnsupportedOperationException();
}
}
) );
}
else if ( useJdbcMetadata ) {
final JdbcConnectionAccess jdbcConnectionAccess = buildJdbcConnectionAccess( configurationValues, registry );
try {
final Connection connection = jdbcConnectionAccess.obtainConnection();
@ -96,26 +138,19 @@ public JdbcEnvironment initiateService(Map configurationValues, ServiceRegistryI
Dialect dialect = dialectFactory.buildDialect(
configurationValues,
new DialectResolutionInfoSource() {
@Override
public DialectResolutionInfo getDialectResolutionInfo() {
try {
return new DatabaseMetaDataDialectResolutionInfoAdapter( connection.getMetaData() );
}
catch ( SQLException sqlException ) {
throw new HibernateException(
"Unable to access java.sql.DatabaseMetaData to determine appropriate Dialect to use",
sqlException
);
}
() -> {
try {
return new DatabaseMetaDataDialectResolutionInfoAdapter( connection.getMetaData() );
}
catch ( SQLException sqlException ) {
throw new HibernateException(
"Unable to access java.sql.DatabaseMetaData to determine appropriate Dialect to use",
sqlException
);
}
}
);
return new JdbcEnvironmentImpl(
registry,
dialect,
dbmd
);
return new JdbcEnvironmentImpl( registry, dialect, dbmd );
}
catch (SQLException e) {
log.unableToObtainConnectionMetadata( e.getMessage() );

View File

@ -9,8 +9,6 @@
import java.sql.SQLException;
import java.util.Properties;
import org.hibernate.engine.jdbc.connections.spi.JdbcConnectionAccess;
import org.jboss.logging.BasicLogger;
import org.jboss.logging.Logger;
import org.jboss.logging.annotations.Cause;
@ -48,12 +46,24 @@ public interface ConnectionPoolingLogger extends BasicLogger {
@Message(value = "Autocommit mode: %s", id = 10001003)
void autoCommitMode(boolean autocommit);
@Message(value = "JDBC URL was not specified by property %s", id = 10001004)
String jdbcUrlNotSpecified(String url);
@Message(value = "No JDBC URL specified by property %s", id = 10001004)
String jdbcUrlNotSpecified(String property);
@LogMessage(level = INFO)
@Message(value = "using driver [%s] at URL [%s]", id = 10001005)
void usingDriver(String driverClassName, String url);
@Message(value = "Loaded JDBC driver class: %s", id = 10001005)
void loadedDriver(String driverClassName);
@LogMessage(level = INFO)
@Message(value = "No JDBC driver class specified by %s", id = 10001010)
void noDriver(String property);
@LogMessage(level = INFO)
@Message(value = "Loaded JDBC drivers: %s", id = 10001011)
void loadedDrivers(String loadedDrivers);
@LogMessage(level = INFO)
@Message(value = "Connecting with JDBC URL [%s]", id = 10001012)
void usingUrl(String url);
@LogMessage(level = WARN)
@Message(value = "No JDBC Driver class was specified by property %s", id = 10001006)