Skip to content

HHH-14478 : Allow DialectResolvers to be discovered by ServiceLoader #3792

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
106 changes: 65 additions & 41 deletions hibernate-core/src/main/java/org/hibernate/cfg/AvailableSettings.java
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
import org.hibernate.boot.registry.classloading.internal.TcclLookupPrecedence;
import org.hibernate.boot.spi.SessionFactoryOptions;
import org.hibernate.cache.spi.TimestampsCacheFactory;
import org.hibernate.engine.jdbc.dialect.spi.DialectResolver;
import org.hibernate.jpa.spi.JpaCompliance;
import org.hibernate.query.ImmutableEntityUpdateQueryHandlingMode;
import org.hibernate.query.hql.HqlTranslator;
Expand Down Expand Up @@ -427,6 +428,59 @@ public interface AvailableSettings extends org.hibernate.jpa.AvailableSettings {
*/
String DIALECT_RESOLVERS = "hibernate.dialect_resolvers";

/**
* Specifies the name of the database provider in cases where a Connection to the underlying database is
* not available (aka, mainly in generating scripts). In such cases, a value for this setting
* *must* be specified.
* <p/>
* The value of this setting is expected to match the value returned by
* {@link java.sql.DatabaseMetaData#getDatabaseProductName()} for the target database.
* <p/>
* Additionally specifying {@value #DIALECT_DB_MAJOR_VERSION} and/or {@value #DIALECT_DB_MINOR_VERSION}
* may be required to understand exactly how to generate the required schema commands.
*
* @see #DIALECT_DB_VERSION
* @see #DIALECT_DB_MAJOR_VERSION
* @see #DIALECT_DB_MINOR_VERSION
*/
String DIALECT_DB_NAME = "javax.persistence.database-product-name";

/**
* Specifies the name of the database provider in cases where a Connection to the underlying database is
* not available (aka, mainly in generating scripts). This value is used to help more precisely determine
* how to perform schema generation tasks for the underlying database in cases where
* {@value #DIALECT_DB_NAME} does not provide enough distinction.
* <p/>
* The value of this setting is expected to match the value returned by
* {@link java.sql.DatabaseMetaData#getDatabaseProductVersion()} for the target database.
*
* @see #DIALECT_DB_NAME
*/
String DIALECT_DB_VERSION = "javax.persistence.database-product-version";

/**
* Specifies the major version of the underlying database, as would be returned by
* {@link java.sql.DatabaseMetaData#getDatabaseMajorVersion} for the target database. This value is used to
* help more precisely determine how to perform schema generation tasks for the underlying database in cases
* where {@value #DIALECT_DB_NAME} does not provide enough distinction.

* @see #DIALECT_DB_NAME
* @see #DIALECT_DB_MINOR_VERSION
*/
String DIALECT_DB_MAJOR_VERSION = "javax.persistence.database-major-version";

/**
* Specifies the minor version of the underlying database, as would be returned by
* {@link java.sql.DatabaseMetaData#getDatabaseMinorVersion} for the target database.
*
* This setting is used in Dialect resolution
*
* @see #DIALECT_DB_NAME
* @see #DIALECT_DB_MAJOR_VERSION
* @see DialectResolver
*/
String DIALECT_DB_MINOR_VERSION = "javax.persistence.database-minor-version";

/**
* Defines the default storage engine for the relational databases that support multiple storage engines.
* This property must be set either as an Environment variable or JVM System Property.
Expand Down Expand Up @@ -1356,63 +1410,33 @@ public interface AvailableSettings extends org.hibernate.jpa.AvailableSettings {
/**
* Allows passing a specific {@link java.sql.Connection} instance to be used by SchemaManagementTool.
* <p/>
* May also be used to determine the values for {@value #HBM2DDL_DB_NAME},
* {@value #HBM2DDL_DB_MAJOR_VERSION} and {@value #HBM2DDL_DB_MINOR_VERSION}.
* May also be used to determine the values for {@value #DIALECT_DB_NAME},
* {@value #DIALECT_DB_MAJOR_VERSION} and {@value #DIALECT_DB_MINOR_VERSION}.
*/
String HBM2DDL_CONNECTION = "javax.persistence.schema-generation-connection";

/**
* Specifies the name of the database provider in cases where a Connection to the underlying database is
* not available (aka, mainly in generating scripts). In such cases, a value for this setting
* *must* be specified.
* <p/>
* The value of this setting is expected to match the value returned by
* {@link java.sql.DatabaseMetaData#getDatabaseProductName()} for the target database.
* <p/>
* Additionally specifying {@value #HBM2DDL_DB_MAJOR_VERSION} and/or {@value #HBM2DDL_DB_MINOR_VERSION}
* may be required to understand exactly how to generate the required schema commands.
*
* @see #HBM2DDL_DB_MAJOR_VERSION
* @see #HBM2DDL_DB_MINOR_VERSION
* @deprecated Use {@link #DIALECT_DB_NAME} instead
*/
@SuppressWarnings("JavaDoc")
String HBM2DDL_DB_NAME = "javax.persistence.database-product-name";
@Deprecated
String HBM2DDL_DB_NAME = DIALECT_DB_NAME;

/**
* Specifies the name of the database provider in cases where a Connection to the underlying database is
* not available (aka, mainly in generating scripts). This value is used to help more precisely determine
* how to perform schema generation tasks for the underlying database in cases where
* {@value #HBM2DDL_DB_NAME} does not provide enough distinction.
* <p/>
* The value of this setting is expected to match the value returned by
* {@link java.sql.DatabaseMetaData#getDatabaseProductVersion()} for the target database.
*
* @see #HBM2DDL_DB_NAME
* @deprecated Use {@link #DIALECT_DB_VERSION} instead
*/
@SuppressWarnings("JavaDoc")
@Deprecated
String HBM2DDL_DB_VERSION = "javax.persistence.database-product-version";

/**
* Specifies the major version of the underlying database, as would be returned by
* {@link java.sql.DatabaseMetaData#getDatabaseMajorVersion} for the target database. This value is used to
* help more precisely determine how to perform schema generation tasks for the underlying database in cases
* where {@value #HBM2DDL_DB_NAME} does not provide enough distinction.

* @see #HBM2DDL_DB_NAME
* @see #HBM2DDL_DB_MINOR_VERSION
* @deprecated Use {@link #DIALECT_DB_MAJOR_VERSION} instead
*/
@Deprecated
String HBM2DDL_DB_MAJOR_VERSION = "javax.persistence.database-major-version";

/**
* Specifies the minor version of the underlying database, as would be returned by
* {@link java.sql.DatabaseMetaData#getDatabaseMinorVersion} for the target database. This value is used to
* help more precisely determine how to perform schema generation tasks for the underlying database in cases
* where the combination of {@value #HBM2DDL_DB_NAME} and {@value #HBM2DDL_DB_MAJOR_VERSION} does not provide
* enough distinction.
*
* @see #HBM2DDL_DB_NAME
* @see #HBM2DDL_DB_MAJOR_VERSION
* @deprecated Use {@link #DIALECT_DB_MINOR_VERSION} instead
*/
@Deprecated
String HBM2DDL_DB_MINOR_VERSION = "javax.persistence.database-minor-version";

/**
Expand Down
50 changes: 30 additions & 20 deletions hibernate-core/src/main/java/org/hibernate/dialect/Database.java
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@
import org.hibernate.engine.jdbc.dialect.spi.BasicSQLExceptionConverter;
import org.hibernate.engine.jdbc.dialect.spi.DialectResolutionInfo;

import static org.hibernate.engine.jdbc.dialect.spi.DialectResolutionInfo.NO_VERSION;

/**
* A list of relational database systems for which Hibernate can resolve a {@link Dialect}.
*
Expand Down Expand Up @@ -55,20 +57,25 @@ public String getDriverClassName(String jdbcUrl) {
@Override
public Dialect createDialect(DialectResolutionInfo info) {
String databaseVersion = info.getDatabaseVersion();
if ( databaseVersion == null ) {
return new DB2Dialect( info );
}
//See https://www.ibm.com/support/knowledgecenter/SSEPEK_12.0.0/java/src/tpc/imjcc_c0053013.html
switch ( databaseVersion.substring( 0, 3 ) ) {
case "SQL": // Linux, UNIX, Windows
return new DB2Dialect( info );
case "DSN": // z/OS
return new DB2zDialect( info );
case "QSQ": // i
return new DB2iDialect( info );
default:
return null;
if ( databaseVersion != null ) {
//See https://www.ibm.com/support/knowledgecenter/SSEPEK_12.0.0/java/src/tpc/imjcc_c0053013.html
switch ( databaseVersion.substring( 0, 3 ) ) {
case "SQL": {
// Linux, UNIX, Windows
return new DB2Dialect( info );
}
case "DSN": {
// z/OS
return new DB2zDialect( info );
}
case "QSQ": {
// i
return new DB2iDialect( info );
}
}
}

return new DB2Dialect( info );
}
@Override
public boolean productNameMatches(String databaseName) {
Expand Down Expand Up @@ -332,15 +339,18 @@ public String getDriverClassName(String jdbcUrl) {
return "org.postgresql.Driver";
}
private String getVersion(DatabaseMetaData databaseMetaData) {
try (Statement statement = databaseMetaData.getConnection().createStatement() ) {
final ResultSet rs = statement.executeQuery( "select version()" );
if ( rs.next() ) {
return rs.getString( 1 );
if ( databaseMetaData != null ) {
try ( Statement statement = databaseMetaData.getConnection().createStatement() ) {
final ResultSet rs = statement.executeQuery( "select version()" );
if ( rs.next() ) {
return rs.getString( 1 );
}
}
catch (SQLException e) {
throw BasicSQLExceptionConverter.INSTANCE.convert( e );
}
}
catch (SQLException e) {
throw BasicSQLExceptionConverter.INSTANCE.convert( e );
}

return "";
}
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,12 @@ public H2Dialect(int version) {
}

private static int parseBuildId(DialectResolutionInfo info) {
String[] bits = info.getDatabaseVersion().split("[. ]");
final String databaseVersion = info.getDatabaseVersion();
if ( databaseVersion == null ) {
return 0;
}

final String[] bits = databaseVersion.split("[. ]");
return bits.length > 2 ? Integer.parseInt( bits[2] ) : 0;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
*/
package org.hibernate.engine.jdbc.dialect.internal;

import java.util.Collection;
import java.util.Map;

import org.hibernate.HibernateException;
Expand All @@ -18,7 +19,7 @@
import org.hibernate.service.spi.ServiceRegistryImplementor;

/**
* Standard initiator for the standard {@link DialectResolver} service
* Standard initiator for the {@link DialectResolver} service
*
* @author Steve Ebersole
*/
Expand All @@ -36,25 +37,25 @@ public Class<DialectResolver> getServiceInitiated() {

@Override
public DialectResolver initiateService(Map configurationValues, ServiceRegistryImplementor registry) {
final DialectResolverSet resolver = new DialectResolverSet();
final DialectResolverSet resolverSet = new DialectResolverSet();

applyCustomerResolvers( resolver, registry, configurationValues );
resolver.addResolver( new StandardDialectResolver() );
applyCustomerResolvers( resolverSet, registry, configurationValues );
resolverSet.addResolver( new StandardDialectResolver() );

return resolver;
return resolverSet;
}

private void applyCustomerResolvers(
DialectResolverSet resolver,
DialectResolverSet resolverSet,
ServiceRegistryImplementor registry,
Map configurationValues) {
Map<?,?> configurationValues) {
final String resolverImplNames = (String) configurationValues.get( AvailableSettings.DIALECT_RESOLVERS );

final ClassLoaderService classLoaderService = registry.getService( ClassLoaderService.class );
if ( StringHelper.isNotEmpty( resolverImplNames ) ) {
final ClassLoaderService classLoaderService = registry.getService( ClassLoaderService.class );
for ( String resolverImplName : StringHelper.split( ", \n\r\f\t", resolverImplNames ) ) {
try {
resolver.addResolver(
resolverSet.addResolver(
(DialectResolver) classLoaderService.classForName( resolverImplName ).newInstance()
);
}
Expand All @@ -66,5 +67,8 @@ private void applyCustomerResolvers(
}
}
}

final Collection<DialectResolver> resolvers = classLoaderService.loadJavaServices( DialectResolver.class );
resolverSet.addDiscoveredResolvers( resolvers );
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;

import org.hibernate.dialect.Dialect;
Expand Down Expand Up @@ -60,23 +61,19 @@ public Dialect resolveDialect(DialectResolutionInfo info) {
return null;
}

/**
* Add a resolver at the end of the underlying resolver list. The resolver added by this method is at lower
* priority than any other existing resolvers.
*
* @param resolver The resolver to add.
*/
public void addResolver(DialectResolver resolver) {
resolvers.add( resolver );
public void addResolver(DialectResolver... resolvers) {
this.resolvers.addAll( Arrays.asList( resolvers ) );
}

/**
* Add a resolver at the beginning of the underlying resolver list. The resolver added by this method is at higher
* priority than any other existing resolvers.
*
* @param resolver The resolver to add.
*/
public void addResolverAtFirst(DialectResolver resolver) {
resolvers.add( 0, resolver );
public void addResolverAtFirst(DialectResolver... resolvers) {
// in reverse so that the first item in the incoming list is added
// to the first position in `this.resolutions`
for ( int i = resolvers.length - 1; i >= 0; i-- ) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Minor, but could be done by using this.resolvers.addAll( 0, Arrays.asList( resolvers ) );

this.resolvers.add( 0, resolvers[i] );
}
}

public void addDiscoveredResolvers(Collection<DialectResolver> resolvers) {
this.resolvers.addAll( 0, resolvers );
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@
* @author Steve Ebersole
*/
public final class StandardDialectResolver implements DialectResolver {
public StandardDialectResolver() {
}

@Override
public Dialect resolveDialect(DialectResolutionInfo info) {
Expand Down
Loading