package org.hibernate.engine.jdbc.internal;
import java.lang.reflect.InvocationTargetException;
import java.sql.Connection;
import java.sql.DatabaseMetaData;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Arrays;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Set;
import org.hibernate.HibernateException;
import org.hibernate.MultiTenancyStrategy;
import org.hibernate.cfg.Environment;
import org.hibernate.dialect.Dialect;
import org.hibernate.engine.jdbc.LobCreationContext;
import org.hibernate.engine.jdbc.LobCreator;
import org.hibernate.engine.jdbc.connections.spi.ConnectionProvider;
import org.hibernate.engine.jdbc.connections.spi.MultiTenantConnectionProvider;
import org.hibernate.engine.jdbc.cursor.internal.StandardRefCursorSupport;
import org.hibernate.engine.jdbc.dialect.spi.DatabaseMetaDataDialectResolutionInfoAdapter;
import org.hibernate.engine.jdbc.dialect.spi.DialectFactory;
import org.hibernate.engine.jdbc.dialect.spi.DialectResolutionInfo;
import org.hibernate.engine.jdbc.dialect.spi.DialectResolutionInfoSource;
import org.hibernate.engine.jdbc.spi.ExtractedDatabaseMetaData;
import org.hibernate.engine.jdbc.spi.JdbcConnectionAccess;
import org.hibernate.engine.jdbc.spi.JdbcServices;
import org.hibernate.engine.jdbc.spi.ResultSetWrapper;
import org.hibernate.engine.jdbc.spi.SchemaNameResolver;
import org.hibernate.engine.jdbc.spi.SqlExceptionHelper;
import org.hibernate.engine.jdbc.spi.SqlStatementLogger;
import org.hibernate.engine.jdbc.spi.TypeInfo;
import org.hibernate.exception.internal.SQLExceptionTypeDelegate;
import org.hibernate.exception.internal.SQLStateConversionDelegate;
import org.hibernate.exception.internal.StandardSQLExceptionConverter;
import org.hibernate.exception.spi.SQLExceptionConverter;
import org.hibernate.internal.CoreLogging;
import org.hibernate.internal.CoreMessageLogger;
import org.hibernate.internal.util.ReflectHelper;
import org.hibernate.internal.util.config.ConfigurationHelper;
import org.hibernate.service.spi.Configurable;
import org.hibernate.service.spi.ServiceRegistryAwareService;
import org.hibernate.service.spi.ServiceRegistryImplementor;
public class JdbcServicesImpl implements JdbcServices, ServiceRegistryAwareService, Configurable {
private static final CoreMessageLogger LOG = CoreLogging.messageLogger( JdbcServicesImpl.class );
private ServiceRegistryImplementor serviceRegistry;
private Dialect dialect;
private ConnectionProvider connectionProvider;
private SqlStatementLogger sqlStatementLogger;
private SqlExceptionHelper sqlExceptionHelper;
private ExtractedDatabaseMetaData ;
private LobCreatorBuilder lobCreatorBuilder;
@Override
public void injectServices(ServiceRegistryImplementor serviceRegistry) {
this.serviceRegistry = serviceRegistry;
}
@Override
public void configure(Map configValues) {
final JdbcConnectionAccess jdbcConnectionAccess = buildJdbcConnectionAccess( configValues );
final DialectFactory dialectFactory = serviceRegistry.getService( DialectFactory.class );
Dialect dialect = null;
LobCreatorBuilder lobCreatorBuilder = null;
boolean metaSupportsRefCursors = false;
boolean metaSupportsNamedParams = false;
boolean metaSupportsScrollable = false;
boolean metaSupportsGetGeneratedKeys = false;
boolean metaSupportsBatchUpdates = false;
boolean metaReportsDDLCausesTxnCommit = false;
boolean metaReportsDDLInTxnSupported = true;
String extraKeywordsString = "";
int sqlStateType = -1;
boolean lobLocatorUpdateCopy = false;
String catalogName = null;
String schemaName = null;
final LinkedHashSet<TypeInfo> typeInfoSet = new LinkedHashSet<TypeInfo>();
final boolean useJdbcMetadata = ConfigurationHelper.getBoolean( "hibernate.temp.use_jdbc_metadata_defaults", configValues, true );
if ( useJdbcMetadata ) {
try {
final Connection connection = jdbcConnectionAccess.obtainConnection();
try {
final DatabaseMetaData meta = connection.getMetaData();
if ( LOG.isDebugEnabled() ) {
LOG.debugf(
"Database ->\n"
+ " name : %s\n"
+ " version : %s\n"
+ " major : %s\n"
+ " minor : %s",
meta.getDatabaseProductName(),
meta.getDatabaseProductVersion(),
meta.getDatabaseMajorVersion(),
meta.getDatabaseMinorVersion()
);
LOG.debugf(
"Driver ->\n"
+ " name : %s\n"
+ " version : %s\n"
+ " major : %s\n"
+ " minor : %s",
meta.getDriverName(),
meta.getDriverVersion(),
meta.getDriverMajorVersion(),
meta.getDriverMinorVersion()
);
LOG.debugf( "JDBC version : %s.%s", meta.getJDBCMajorVersion(), meta.getJDBCMinorVersion() );
}
metaSupportsRefCursors = StandardRefCursorSupport.supportsRefCursors( meta );
metaSupportsNamedParams = meta.supportsNamedParameters();
metaSupportsScrollable = meta.supportsResultSetType( ResultSet.TYPE_SCROLL_INSENSITIVE );
metaSupportsBatchUpdates = meta.supportsBatchUpdates();
metaReportsDDLCausesTxnCommit = meta.dataDefinitionCausesTransactionCommit();
metaReportsDDLInTxnSupported = !meta.dataDefinitionIgnoredInTransactions();
metaSupportsGetGeneratedKeys = meta.supportsGetGeneratedKeys();
extraKeywordsString = meta.getSQLKeywords();
sqlStateType = meta.getSQLStateType();
lobLocatorUpdateCopy = meta.locatorsUpdateCopy();
typeInfoSet.addAll( TypeInfo.extractTypeInfo( meta ) );
dialect = dialectFactory.buildDialect(
configValues,
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
);
}
}
}
);
catalogName = connection.getCatalog();
final SchemaNameResolver schemaNameResolver = determineExplicitSchemaNameResolver( configValues );
if ( schemaNameResolver == null ) {
}
if ( schemaNameResolver != null ) {
schemaName = schemaNameResolver.resolveSchemaName( connection );
}
lobCreatorBuilder = new LobCreatorBuilder( configValues, connection );
}
catch ( SQLException sqle ) {
LOG.unableToObtainConnectionMetadata( sqle.getMessage() );
}
finally {
if ( connection != null ) {
jdbcConnectionAccess.releaseConnection( connection );
}
}
}
catch ( SQLException sqle ) {
LOG.unableToObtainConnectionToQueryMetadata( sqle.getMessage() );
dialect = dialectFactory.buildDialect( configValues, null );
}
catch ( UnsupportedOperationException uoe ) {
dialect = dialectFactory.buildDialect( configValues, null );
}
}
else {
dialect = dialectFactory.buildDialect( configValues, null );
}
final boolean showSQL = ConfigurationHelper.getBoolean( Environment.SHOW_SQL, configValues, false );
final boolean formatSQL = ConfigurationHelper.getBoolean( Environment.FORMAT_SQL, configValues, false );
this.dialect = dialect;
this.lobCreatorBuilder = (
lobCreatorBuilder == null ?
new LobCreatorBuilder( configValues, null ) :
lobCreatorBuilder
);
this.sqlStatementLogger = new SqlStatementLogger( showSQL, formatSQL );
this.extractedMetaDataSupport = new ExtractedDatabaseMetaDataImpl(
metaSupportsRefCursors,
metaSupportsNamedParams,
metaSupportsScrollable,
metaSupportsGetGeneratedKeys,
metaSupportsBatchUpdates,
metaReportsDDLInTxnSupported,
metaReportsDDLCausesTxnCommit,
parseKeywords( extraKeywordsString ),
parseSQLStateType( sqlStateType ),
lobLocatorUpdateCopy,
schemaName,
catalogName,
typeInfoSet
);
SQLExceptionConverter sqlExceptionConverter = dialect.buildSQLExceptionConverter();
if ( sqlExceptionConverter == null ) {
final StandardSQLExceptionConverter converter = new StandardSQLExceptionConverter();
sqlExceptionConverter = converter;
converter.addDelegate( dialect.buildSQLExceptionConversionDelegate() );
converter.addDelegate( new SQLExceptionTypeDelegate( dialect ) );
converter.addDelegate( new SQLStateConversionDelegate( dialect ) );
}
this.sqlExceptionHelper = new SqlExceptionHelper( sqlExceptionConverter );
}
private JdbcConnectionAccess buildJdbcConnectionAccess(Map configValues) {
final MultiTenancyStrategy multiTenancyStrategy = MultiTenancyStrategy.determineMultiTenancyStrategy( configValues );
if ( MultiTenancyStrategy.NONE == multiTenancyStrategy ) {
connectionProvider = serviceRegistry.getService( ConnectionProvider.class );
return new ConnectionProviderJdbcConnectionAccess( connectionProvider );
}
else {
connectionProvider = null;
final MultiTenantConnectionProvider multiTenantConnectionProvider = serviceRegistry.getService( MultiTenantConnectionProvider.class );
return new MultiTenantConnectionProviderJdbcConnectionAccess( multiTenantConnectionProvider );
}
}
private static class ConnectionProviderJdbcConnectionAccess implements JdbcConnectionAccess {
private final ConnectionProvider connectionProvider;
public ConnectionProviderJdbcConnectionAccess(ConnectionProvider connectionProvider) {
this.connectionProvider = connectionProvider;
}
@Override
public Connection obtainConnection() throws SQLException {
return connectionProvider.getConnection();
}
@Override
public void releaseConnection(Connection connection) throws SQLException {
connectionProvider.closeConnection( connection );
}
@Override
public boolean supportsAggressiveRelease() {
return connectionProvider.supportsAggressiveRelease();
}
}
private static class MultiTenantConnectionProviderJdbcConnectionAccess implements JdbcConnectionAccess {
private final MultiTenantConnectionProvider connectionProvider;
public MultiTenantConnectionProviderJdbcConnectionAccess(MultiTenantConnectionProvider connectionProvider) {
this.connectionProvider = connectionProvider;
}
@Override
public Connection obtainConnection() throws SQLException {
return connectionProvider.getAnyConnection();
}
@Override
public void releaseConnection(Connection connection) throws SQLException {
connectionProvider.releaseAnyConnection( connection );
}
@Override
public boolean supportsAggressiveRelease() {
return connectionProvider.supportsAggressiveRelease();
}
}
public static final String SCHEMA_NAME_RESOLVER = "hibernate.schema_name_resolver";
private SchemaNameResolver determineExplicitSchemaNameResolver(Map configValues) {
final Object setting = configValues.get( SCHEMA_NAME_RESOLVER );
if ( SchemaNameResolver.class.isInstance( setting ) ) {
return (SchemaNameResolver) setting;
}
final String resolverClassName = (String) setting;
if ( resolverClassName != null ) {
try {
final Class resolverClass = ReflectHelper.classForName( resolverClassName, getClass() );
return (SchemaNameResolver) ReflectHelper.getDefaultConstructor( resolverClass ).newInstance();
}
catch ( ClassNotFoundException e ) {
LOG.unableToLocateConfiguredSchemaNameResolver( resolverClassName, e.toString() );
}
catch ( InvocationTargetException e ) {
LOG.unableToInstantiateConfiguredSchemaNameResolver( resolverClassName, e.getTargetException().toString() );
}
catch ( Exception e ) {
LOG.unableToInstantiateConfiguredSchemaNameResolver( resolverClassName, e.toString() );
}
}
return null;
}
private Set<String> parseKeywords(String extraKeywordsString) {
final Set<String> keywordSet = new HashSet<String>();
keywordSet.addAll( Arrays.asList( extraKeywordsString.split( "," ) ) );
return keywordSet;
}
private ExtractedDatabaseMetaData.SQLStateType parseSQLStateType(int sqlStateType) {
switch ( sqlStateType ) {
case DatabaseMetaData.sqlStateSQL99 : {
return ExtractedDatabaseMetaData.SQLStateType.SQL99;
}
case DatabaseMetaData.sqlStateXOpen : {
return ExtractedDatabaseMetaData.SQLStateType.XOpen;
}
default : {
return ExtractedDatabaseMetaData.SQLStateType.UNKOWN;
}
}
}
private static class implements ExtractedDatabaseMetaData {
private final boolean ;
private final boolean ;
private final boolean ;
private final boolean ;
private final boolean ;
private final boolean ;
private final boolean ;
private final Set<String> ;
private final SQLStateType ;
private final boolean ;
private final String ;
private final String ;
private final LinkedHashSet<TypeInfo> ;
private (
boolean supportsRefCursors,
boolean supportsNamedParameters,
boolean supportsScrollableResults,
boolean supportsGetGeneratedKeys,
boolean supportsBatchUpdates,
boolean supportsDataDefinitionInTransaction,
boolean doesDataDefinitionCauseTransactionCommit,
Set<String> extraKeywords,
SQLStateType sqlStateType,
boolean lobLocatorUpdateCopy,
String connectionSchemaName,
String connectionCatalogName,
LinkedHashSet<TypeInfo> typeInfoSet) {
this.supportsRefCursors = supportsRefCursors;
this.supportsNamedParameters = supportsNamedParameters;
this.supportsScrollableResults = supportsScrollableResults;
this.supportsGetGeneratedKeys = supportsGetGeneratedKeys;
this.supportsBatchUpdates = supportsBatchUpdates;
this.supportsDataDefinitionInTransaction = supportsDataDefinitionInTransaction;
this.doesDataDefinitionCauseTransactionCommit = doesDataDefinitionCauseTransactionCommit;
this.extraKeywords = extraKeywords;
this.sqlStateType = sqlStateType;
this.lobLocatorUpdateCopy = lobLocatorUpdateCopy;
this.connectionSchemaName = connectionSchemaName;
this.connectionCatalogName = connectionCatalogName;
this.typeInfoSet = typeInfoSet;
}
@Override
public boolean () {
return supportsRefCursors;
}
@Override
public boolean () {
return supportsNamedParameters;
}
@Override
public boolean () {
return supportsScrollableResults;
}
@Override
public boolean () {
return supportsGetGeneratedKeys;
}
@Override
public boolean () {
return supportsBatchUpdates;
}
@Override
public boolean () {
return supportsDataDefinitionInTransaction;
}
@Override
public boolean () {
return doesDataDefinitionCauseTransactionCommit;
}
@Override
public Set<String> () {
return extraKeywords;
}
@Override
public SQLStateType () {
return sqlStateType;
}
@Override
public boolean () {
return lobLocatorUpdateCopy;
}
@Override
public String () {
return connectionSchemaName;
}
@Override
public String () {
return connectionCatalogName;
}
@Override
public LinkedHashSet<TypeInfo> () {
return typeInfoSet;
}
}
@Override
public ConnectionProvider getConnectionProvider() {
return connectionProvider;
}
@Override
public SqlStatementLogger getSqlStatementLogger() {
return sqlStatementLogger;
}
@Override
public SqlExceptionHelper getSqlExceptionHelper() {
return sqlExceptionHelper;
}
@Override
public Dialect getDialect() {
return dialect;
}
@Override
public ExtractedDatabaseMetaData () {
return extractedMetaDataSupport;
}
@Override
public LobCreator getLobCreator(LobCreationContext lobCreationContext) {
return lobCreatorBuilder.buildLobCreator( lobCreationContext );
}
@Override
public ResultSetWrapper getResultSetWrapper() {
return ResultSetWrapperImpl.INSTANCE;
}
}