HHH-5961 : Contextual LOB creator is used when the JDBC driver does not support JDBC4 Connection.createBlob()

This commit is contained in:
Gail Badner 2011-03-08 21:12:02 -08:00
parent 724cd1f95d
commit ad5f88c2d6
12 changed files with 156 additions and 397 deletions

View File

@ -429,8 +429,7 @@ public final class Hibernate {
public static LobCreator getLobCreator(SessionImplementor session) {
return session.getFactory()
.getSettings()
.getJdbcSupport()
.getJdbcServices()
.getLobCreator( ( LobCreationContext ) session );
}

View File

@ -28,7 +28,6 @@ import org.hibernate.ConnectionReleaseMode;
import org.hibernate.EntityMode;
import org.hibernate.cache.QueryCacheFactory;
import org.hibernate.cache.RegionFactory;
import org.hibernate.engine.jdbc.JdbcSupport;
import org.hibernate.hql.QueryTranslatorFactory;
import org.hibernate.service.jta.platform.spi.JtaPlatform;
import org.hibernate.tuple.entity.EntityTuplizerFactory;
@ -81,7 +80,6 @@ public final class Settings {
private boolean checkNullability;
// private ComponentTuplizerFactory componentTuplizerFactory; todo : HHH-3517 and HHH-1907
// private BytecodeProvider bytecodeProvider;
private JdbcSupport jdbcSupport;
private String importFiles;
private JtaPlatform jtaPlatform;
@ -258,11 +256,6 @@ public final class Settings {
// return componentTuplizerFactory;
// }
public JdbcSupport getJdbcSupport() {
return jdbcSupport;
}
// package protected setters ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
void setDefaultSchemaName(String string) {
@ -429,10 +422,6 @@ public final class Settings {
// this.componentTuplizerFactory = componentTuplizerFactory;
// }
void setJdbcSupport(JdbcSupport jdbcSupport) {
this.jdbcSupport = jdbcSupport;
}
// public BytecodeProvider getBytecodeProvider() {
// return bytecodeProvider;
// }

View File

@ -35,7 +35,6 @@ import org.hibernate.cache.QueryCacheFactory;
import org.hibernate.cache.RegionFactory;
import org.hibernate.cache.impl.NoCachingRegionFactory;
import org.hibernate.cache.impl.bridge.RegionFactoryCacheProviderBridge;
import org.hibernate.engine.jdbc.JdbcSupport;
import org.hibernate.engine.jdbc.spi.ExtractedDatabaseMetaData;
import org.hibernate.engine.jdbc.spi.JdbcServices;
import org.hibernate.engine.transaction.spi.TransactionFactory;
@ -85,8 +84,6 @@ public class SettingsFactory implements Serializable {
properties.putAll( jdbcServices.getDialect().getDefaultProperties() );
properties.putAll( props );
settings.setJdbcSupport( new JdbcSupport( ! ConfigurationHelper.getBoolean( Environment.NON_CONTEXTUAL_LOB_CREATION, properties ) ) );
// Transaction settings:
settings.setJtaPlatform( serviceRegistry.getService( JtaPlatform.class ) );

View File

@ -1,78 +0,0 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2009 by Red Hat Inc and/or its affiliates or by
* third-party contributors as indicated by either @author tags or express
* copyright attribution statements applied by the authors. All
* third-party contributions are distributed under license by Red Hat Inc.
*
* This copyrighted material is made available to anyone wishing to use, modify,
* copy, or redistribute it subject to the terms and conditions of the GNU
* Lesser General Public License, as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
* for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this distribution; if not, write to:
* Free Software Foundation, Inc.
* 51 Franklin Street, Fifth Floor
* Boston, MA 02110-1301 USA
*/
package org.hibernate.engine.jdbc;
import java.sql.ResultSet;
/**
* Central place for locating JDBC support elements.
*
* @author Steve Ebersole
*/
public class JdbcSupport {
public final boolean userContextualLobCreator;
/**
* Create a support object
*
* @param userContextualLobCreator Should we use contextual (using the JDBC {@link java.sql.Connection}) to
* create LOB instances. In almost all instances this should be the case. However if the underlying driver
* does not support the {@link java.sql.Connection#createBlob()}, {@link java.sql.Connection#createClob()} or
* {@link java.sql.Connection#createNClob()} methods this will need to be set to false.
*/
public JdbcSupport(boolean userContextualLobCreator) {
this.userContextualLobCreator = userContextualLobCreator;
}
/**
* Get an explcitly non-contextual LOB creator.
*
* @return The LOB creator
*/
public LobCreator getLobCreator() {
return NonContextualLobCreator.INSTANCE;
}
/**
* Get a LOB creator, based on the given context
*
* @return The LOB creator
*/
public LobCreator getLobCreator(LobCreationContext lobCreationContext) {
return userContextualLobCreator
? new ContextualLobCreator( lobCreationContext )
: NonContextualLobCreator.INSTANCE;
}
/**
* Wrap a result set in a "colun name cache" wrapper.
*
* @param resultSet The result set to wrap
* @param columnNameCache The column name cache.
*
* @return The wrapped result set.
*/
public ResultSet wrap(ResultSet resultSet, ColumnNameCache columnNameCache) {
return ResultSetWrapperProxy.generateProxy( resultSet, columnNameCache );
}
}

View File

@ -34,8 +34,11 @@ import java.util.Set;
import org.hibernate.HibernateLogger;
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.spi.ExtractedDatabaseMetaData;
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;
@ -74,9 +77,11 @@ public class JdbcServicesImpl implements JdbcServices, Configurable {
private SqlStatementLogger sqlStatementLogger;
private SqlExceptionHelper sqlExceptionHelper;
private ExtractedDatabaseMetaData extractedMetaDataSupport;
private LobCreatorBuilder lobCreatorBuilder;
public void configure(Map configValues) {
Dialect dialect = null;
LobCreatorBuilder lobCreatorBuilder = null;
boolean metaSupportsScrollable = false;
boolean metaSupportsGetGeneratedKeys = false;
@ -134,6 +139,7 @@ public class JdbcServicesImpl implements JdbcServices, Configurable {
if ( schemaNameResolver != null ) {
schemaName = schemaNameResolver.resolveSchemaName( conn );
}
lobCreatorBuilder = new LobCreatorBuilder( configValues, conn );
}
catch ( SQLException sqle ) {
LOG.unableToObtainConnectionMetadata(sqle.getMessage());
@ -159,6 +165,12 @@ public class JdbcServicesImpl implements JdbcServices, Configurable {
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.sqlExceptionHelper = new SqlExceptionHelper( dialect.buildSQLExceptionConverter() );
this.extractedMetaDataSupport = new ExtractedDatabaseMetaDataImpl(
@ -328,4 +340,18 @@ public class JdbcServicesImpl implements JdbcServices, Configurable {
public ExtractedDatabaseMetaData getExtractedMetaDataSupport() {
return extractedMetaDataSupport;
}
/**
* {@inheritDoc}
*/
public LobCreator getLobCreator(LobCreationContext lobCreationContext) {
return lobCreatorBuilder.buildLobCreator( lobCreationContext );
}
/**
* {@inheritDoc}
*/
public ResultSetWrapper getResultSetWrapper() {
return ResultSetWrapperImpl.INSTANCE;
}
}

View File

@ -0,0 +1,49 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2011, Red Hat Inc. or third-party contributors as
* indicated by the @author tags or express copyright attribution
* statements applied by the authors. All third-party contributions are
* distributed under license by Red Hat Inc.
*
* This copyrighted material is made available to anyone wishing to use, modify,
* copy, or redistribute it subject to the terms and conditions of the GNU
* Lesser General Public License, as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
* for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this distribution; if not, write to:
* Free Software Foundation, Inc.
* 51 Franklin Street, Fifth Floor
* Boston, MA 02110-1301 USA
*/
package org.hibernate.engine.jdbc.internal;
import java.sql.ResultSet;
import org.hibernate.engine.jdbc.ColumnNameCache;
import org.hibernate.engine.jdbc.ResultSetWrapperProxy;
import org.hibernate.engine.jdbc.spi.ResultSetWrapper;
/**
* Standard Hibernate implementation for wrapping a {@link ResultSet} in a
" column name cache" wrapper.
*
* @author Gail Badner
*/
public class ResultSetWrapperImpl implements ResultSetWrapper {
public static ResultSetWrapper INSTANCE = new ResultSetWrapperImpl();
private ResultSetWrapperImpl() {
}
/**
* {@inheritDoc}
*/
public ResultSet wrap(ResultSet resultSet, ColumnNameCache columnNameCache) {
return ResultSetWrapperProxy.generateProxy( resultSet, columnNameCache );
}
}

View File

@ -23,7 +23,13 @@
*/
package org.hibernate.engine.jdbc.spi;
import java.sql.Connection;
import java.sql.ResultSet;
import org.hibernate.dialect.Dialect;
import org.hibernate.engine.jdbc.ColumnNameCache;
import org.hibernate.engine.jdbc.LobCreationContext;
import org.hibernate.engine.jdbc.LobCreator;
import org.hibernate.engine.jdbc.ResultSetWrapperProxy;
import org.hibernate.service.jdbc.connections.spi.ConnectionProvider;
import org.hibernate.service.spi.Service;
@ -71,4 +77,19 @@ public interface JdbcServices extends Service {
* @return
*/
public ExtractedDatabaseMetaData getExtractedMetaDataSupport();
/**
* Create an instance of a {@link LobCreator} appropriate for the current environment, mainly meant to account for
* variance between JDBC 4 (<= JDK 1.6) and JDBC3 (>= JDK 1.5).
*
* @param lobCreationContext The context in which the LOB is being created
* @return The LOB creator.
*/
public LobCreator getLobCreator(LobCreationContext lobCreationContext);
/**
* Obtain service for wrapping a {@link ResultSet} in a "column name cache" wrapper.
* @return The ResultSet wrapper.
*/
public ResultSetWrapper getResultSetWrapper();
}

View File

@ -0,0 +1,44 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2011, Red Hat Inc. or third-party contributors as
* indicated by the @author tags or express copyright attribution
* statements applied by the authors. All third-party contributions are
* distributed under license by Red Hat Inc.
*
* This copyrighted material is made available to anyone wishing to use, modify,
* copy, or redistribute it subject to the terms and conditions of the GNU
* Lesser General Public License, as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
* for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this distribution; if not, write to:
* Free Software Foundation, Inc.
* 51 Franklin Street, Fifth Floor
* Boston, MA 02110-1301 USA
*/
package org.hibernate.engine.jdbc.spi;
import java.sql.ResultSet;
import org.hibernate.engine.jdbc.ColumnNameCache;
/**
* Contract for wrapping a {@link ResultSet} in a "column name cache" wrapper.
*
* @author Gail Badner
*/
public interface ResultSetWrapper {
/**
* Wrap a result set in a "column name cache" wrapper.
*
* @param resultSet The result set to wrap
* @param columnNameCache The column name cache.
*
* @return The wrapped result set.
*/
public ResultSet wrap(ResultSet resultSet, ColumnNameCache columnNameCache);
}

View File

@ -2229,7 +2229,7 @@ public final class SessionImpl
}
private LobCreator lobCreator() {
return session.getFactory().getSettings().getJdbcSupport().getLobCreator( session );
return session.getFactory().getJdbcServices().getLobCreator( session );
}
/**

View File

@ -2000,8 +2000,8 @@ public abstract class Loader {
try {
LOG.debugf("Wrapping result set [%s]", rs);
return session.getFactory()
.getSettings()
.getJdbcSupport().wrap( rs, retreiveColumnNameToIndexCache( rs ) );
.getJdbcServices()
.getResultSetWrapper().wrap( rs, retreiveColumnNameToIndexCache( rs ) );
}
catch(SQLException e) {
LOG.unableToWrapResultSet(e);

View File

@ -1,297 +0,0 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2010, Red Hat Inc. or third-party contributors as
* indicated by the @author tags or express copyright attribution
* statements applied by the authors. All third-party contributions are
* distributed under license by Red Hat Inc.
*
* This copyrighted material is made available to anyone wishing to use, modify,
* copy, or redistribute it subject to the terms and conditions of the GNU
* Lesser General Public License, as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
* for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this distribution; if not, write to:
* Free Software Foundation, Inc.
* 51 Franklin Street, Fifth Floor
* Boston, MA 02110-1301 USA
*/
package org.hibernate.jdbc;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.Reader;
import java.io.Writer;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.sql.Blob;
import java.sql.Clob;
import java.sql.Connection;
import java.sql.DatabaseMetaData;
import java.sql.NClob;
import java.sql.SQLException;
import junit.framework.TestCase;
import org.hibernate.engine.jdbc.BlobImplementer;
import org.hibernate.engine.jdbc.ClobImplementer;
import org.hibernate.engine.jdbc.ContextualLobCreator;
import org.hibernate.engine.jdbc.JdbcSupport;
import org.hibernate.engine.jdbc.LobCreationContext;
import org.hibernate.engine.jdbc.LobCreator;
import org.hibernate.engine.jdbc.NClobImplementer;
import org.hibernate.engine.jdbc.WrappedBlob;
import org.hibernate.engine.jdbc.WrappedClob;
/**
* TODO : javadoc
*
* @author Steve Ebersole
*/
public class JdbcSupportTest extends TestCase {
private static JdbcSupport jdbcSupport = new JdbcSupport( true );
public void testConnectedLobCreator() throws SQLException {
final Connection connection = createConnectionProxy(
4,
new JdbcLobBuilder() {
public Blob createBlob() {
return new JdbcBlob();
}
public Clob createClob() {
return new JdbcClob();
}
public NClob createNClob() {
return new JdbcNClob();
}
}
);
final LobCreationContext lobCreationContext = new LobCreationContext() {
public Object execute(Callback callback) {
try {
return callback.executeOnConnection( connection );
}
catch ( SQLException e ) {
throw new RuntimeException( "Unexpected SQLException", e );
}
}
};
LobCreator lobCreator = jdbcSupport.getLobCreator( lobCreationContext );
assertTrue( lobCreator instanceof ContextualLobCreator );
Blob blob = lobCreator.createBlob( new byte[] {} );
assertTrue( blob instanceof JdbcBlob );
blob = lobCreator.wrap( blob );
assertTrue( blob instanceof WrappedBlob );
Clob clob = lobCreator.createClob( "Hi" );
assertTrue( clob instanceof JdbcClob );
clob = lobCreator.wrap( clob );
assertTrue( clob instanceof WrappedClob );
Clob nclob = lobCreator.createNClob( "Hi" );
assertTrue( nclob instanceof JdbcNClob );
nclob = lobCreator.wrap( nclob );
assertTrue( nclob instanceof WrappedClob );
blob.free();
clob.free();
nclob.free();
connection.close();
}
public void testLegacyLobCreator() throws SQLException {
LobCreator lobCreator = jdbcSupport.getLobCreator();
Blob blob = lobCreator.createBlob( new byte[] {} );
assertTrue( blob instanceof BlobImplementer );
blob = lobCreator.wrap( blob );
assertTrue( blob instanceof WrappedBlob );
Clob clob = lobCreator.createClob( "Hi" );
assertTrue( clob instanceof ClobImplementer );
clob = lobCreator.wrap( clob );
assertTrue( clob instanceof WrappedClob );
Clob nclob = lobCreator.createNClob( "Hi" );
assertTrue( nclob instanceof NClobImplementer );
assertTrue( NClob.class.isInstance( nclob ) );
nclob = lobCreator.wrap( nclob );
assertTrue( nclob instanceof WrappedClob );
blob.free();
clob.free();
nclob.free();
}
private interface JdbcLobBuilder {
public Blob createBlob();
public Clob createClob();
public NClob createNClob();
}
private class ConnectionProxyHandler implements InvocationHandler {
private final JdbcLobBuilder lobBuilder;
private final DatabaseMetaData metadata;
private ConnectionProxyHandler(int version, JdbcLobBuilder lobBuilder) {
this.lobBuilder = lobBuilder;
this.metadata = createMetadataProxy( version );
}
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// the only methods we are interested in are the LOB creation methods...
if ( args == null || args.length == 0 ) {
final String methodName = method.getName();
if ( "createBlob".equals( methodName ) ) {
return lobBuilder.createBlob();
}
else if ( "createClob".equals( methodName ) ) {
return lobBuilder.createClob();
}
else if ( "createNClob".equals( methodName ) ) {
return lobBuilder.createNClob();
}
else if ( "getMetaData".equals( methodName ) ) {
return metadata;
}
}
return null;
}
}
private static Class[] CONN_PROXY_TYPES = new Class[] { Connection.class };
private Connection createConnectionProxy(int version, JdbcLobBuilder jdbcLobBuilder) {
ConnectionProxyHandler handler = new ConnectionProxyHandler( version, jdbcLobBuilder );
return ( Connection ) Proxy.newProxyInstance( getClass().getClassLoader(), CONN_PROXY_TYPES, handler );
}
private class MetadataProxyHandler implements InvocationHandler {
private final int jdbcVersion;
private MetadataProxyHandler(int jdbcVersion) {
this.jdbcVersion = jdbcVersion;
}
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
final String methodName = method.getName();
if ( "getJDBCMajorVersion".equals( methodName ) ) {
return jdbcVersion;
}
return null;
}
}
private static Class[] META_PROXY_TYPES = new Class[] { DatabaseMetaData.class };
private DatabaseMetaData createMetadataProxy(int version) {
MetadataProxyHandler handler = new MetadataProxyHandler( version );
return ( DatabaseMetaData ) Proxy.newProxyInstance( getClass().getClassLoader(), META_PROXY_TYPES, handler );
}
private class JdbcBlob implements Blob {
public long length() throws SQLException {
return 0;
}
public byte[] getBytes(long pos, int length) throws SQLException {
return new byte[0];
}
public InputStream getBinaryStream() throws SQLException {
return null;
}
public long position(byte[] pattern, long start) throws SQLException {
return 0;
}
public long position(Blob pattern, long start) throws SQLException {
return 0;
}
public int setBytes(long pos, byte[] bytes) throws SQLException {
return 0;
}
public int setBytes(long pos, byte[] bytes, int offset, int len) throws SQLException {
return 0;
}
public OutputStream setBinaryStream(long pos) throws SQLException {
return null;
}
public void truncate(long len) throws SQLException {
}
public void free() throws SQLException {
}
public InputStream getBinaryStream(long pos, long length) throws SQLException {
return null;
}
}
private class JdbcClob implements Clob {
public long length() throws SQLException {
return 0;
}
public String getSubString(long pos, int length) throws SQLException {
return null;
}
public Reader getCharacterStream() throws SQLException {
return null;
}
public InputStream getAsciiStream() throws SQLException {
return null;
}
public long position(String searchstr, long start) throws SQLException {
return 0;
}
public long position(Clob searchstr, long start) throws SQLException {
return 0;
}
public int setString(long pos, String str) throws SQLException {
return 0;
}
public int setString(long pos, String str, int offset, int len) throws SQLException {
return 0;
}
public OutputStream setAsciiStream(long pos) throws SQLException {
return null;
}
public Writer setCharacterStream(long pos) throws SQLException {
return null;
}
public void truncate(long len) throws SQLException {
}
public void free() throws SQLException {
}
public Reader getCharacterStream(long pos, long length) throws SQLException {
return null;
}
}
private class JdbcNClob extends JdbcClob implements NClob {
}
}

View File

@ -23,14 +23,19 @@
*/
package org.hibernate.test.common;
import java.sql.ResultSet;
import java.util.Collections;
import java.util.LinkedHashSet;
import java.util.Set;
import org.hibernate.dialect.Dialect;
import org.hibernate.engine.jdbc.JdbcSupport;
import org.hibernate.engine.jdbc.ColumnNameCache;
import org.hibernate.engine.jdbc.LobCreationContext;
import org.hibernate.engine.jdbc.LobCreator;
import org.hibernate.engine.jdbc.internal.ResultSetWrapperImpl;
import org.hibernate.engine.jdbc.internal.TypeInfo;
import org.hibernate.engine.jdbc.spi.ExtractedDatabaseMetaData;
import org.hibernate.engine.jdbc.spi.JdbcServices;
import org.hibernate.engine.jdbc.spi.ResultSetWrapper;
import org.hibernate.engine.jdbc.spi.SqlExceptionHelper;
import org.hibernate.engine.jdbc.spi.SqlStatementLogger;
import org.hibernate.service.jdbc.connections.spi.ConnectionProvider;
@ -49,7 +54,7 @@ public class BasicTestingJdbcServiceImpl implements JdbcServices {
private SqlStatementLogger sqlStatementLogger;
private SqlExceptionHelper exceptionHelper;
private final ExtractedDatabaseMetaData metaDataSupport = new MetaDataSupportImpl();
private final ResultSetWrapper resultSetWrapper = ResultSetWrapperImpl.INSTANCE;
public void start() {
}
@ -80,7 +85,11 @@ public class BasicTestingJdbcServiceImpl implements JdbcServices {
return dialect;
}
public JdbcSupport getJdbcSupport() {
public LobCreator getLobCreator(LobCreationContext lobCreationContext) {
return null;
}
public ResultSetWrapper getResultSetWrapper() {
return null;
}