Skip to content

Commit

Permalink
HHH-14478 : Allow DialectResolvers to be discovered by ServiceLoader
Browse files Browse the repository at this point in the history
  • Loading branch information
sebersole committed Mar 8, 2021
1 parent 47f7ca7 commit 69564cd
Show file tree
Hide file tree
Showing 18 changed files with 272 additions and 164 deletions.
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,15 @@ 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) {
this.resolvers.addAll( 0, Arrays.asList( resolvers ) );
}

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

0 comments on commit 69564cd

Please sign in to comment.