diff --git a/annotations/src/main/java/org/hibernate/type/ByteArrayBlobType.java b/annotations/src/main/java/org/hibernate/type/ByteArrayBlobType.java index 8ef0180a13..2373d64a94 100644 --- a/annotations/src/main/java/org/hibernate/type/ByteArrayBlobType.java +++ b/annotations/src/main/java/org/hibernate/type/ByteArrayBlobType.java @@ -34,11 +34,10 @@ import java.util.Map; import org.dom4j.Node; import org.hibernate.EntityMode; import org.hibernate.HibernateException; -import org.hibernate.MappingException; +import org.hibernate.Hibernate; import org.hibernate.engine.Mapping; import org.hibernate.engine.SessionFactoryImplementor; import org.hibernate.engine.SessionImplementor; -import org.hibernate.lob.BlobImpl; import org.hibernate.util.ArrayHelper; /** @@ -51,9 +50,10 @@ import org.hibernate.util.ArrayHelper; */ @Deprecated public class ByteArrayBlobType extends AbstractLobType { + private static final int[] TYPES = new int[] { Types.BLOB }; - public int[] sqlTypes(Mapping mapping) throws MappingException { - return new int[]{Types.BLOB}; + public int[] sqlTypes(Mapping mapping) { + return TYPES; } @Override @@ -128,7 +128,7 @@ public class ByteArrayBlobType extends AbstractLobType { st.setBinaryStream( index, new ByteArrayInputStream( toSet ), toSet.length ); } else { - st.setBlob( index, new BlobImpl( toSet ) ); + st.setBlob( index, Hibernate.getLobCreator( session ).createBlob( toSet ) ); } } } diff --git a/annotations/src/main/java/org/hibernate/type/SerializableToBlobType.java b/annotations/src/main/java/org/hibernate/type/SerializableToBlobType.java index 6d9f061b04..4f24647c94 100644 --- a/annotations/src/main/java/org/hibernate/type/SerializableToBlobType.java +++ b/annotations/src/main/java/org/hibernate/type/SerializableToBlobType.java @@ -37,10 +37,10 @@ import org.dom4j.Node; import org.hibernate.EntityMode; import org.hibernate.HibernateException; import org.hibernate.MappingException; +import org.hibernate.Hibernate; import org.hibernate.engine.Mapping; import org.hibernate.engine.SessionFactoryImplementor; import org.hibernate.engine.SessionImplementor; -import org.hibernate.lob.BlobImpl; import org.hibernate.usertype.ParameterizedType; import org.hibernate.util.ReflectHelper; import org.hibernate.util.SerializationHelper; @@ -99,7 +99,7 @@ public class SerializableToBlobType extends AbstractLobType implements Parameter st.setBinaryStream( index, new ByteArrayInputStream( toSet ), toSet.length ); } else { - st.setBlob( index, new BlobImpl( toSet ) ); + st.setBlob( index, Hibernate.getLobCreator( session ).createBlob( toSet ) ); } } else { diff --git a/core/src/main/java/org/hibernate/Hibernate.java b/core/src/main/java/org/hibernate/Hibernate.java index 116f720745..9343b5aa8c 100644 --- a/core/src/main/java/org/hibernate/Hibernate.java +++ b/core/src/main/java/org/hibernate/Hibernate.java @@ -28,6 +28,7 @@ import java.io.IOException; import java.io.InputStream; import java.io.Reader; import java.io.Serializable; +import java.io.ByteArrayOutputStream; import java.sql.Blob; import java.sql.Clob; import java.util.Iterator; @@ -35,12 +36,13 @@ import java.util.Properties; import org.hibernate.collection.PersistentCollection; import org.hibernate.engine.HibernateIterator; +import org.hibernate.engine.SessionImplementor; +import org.hibernate.engine.jdbc.NonContextualLobCreator; +import org.hibernate.engine.jdbc.LobCreationContext; +import org.hibernate.engine.jdbc.LobCreator; +import org.hibernate.engine.jdbc.StreamUtils; import org.hibernate.intercept.FieldInterceptionHelper; import org.hibernate.intercept.FieldInterceptor; -import org.hibernate.lob.BlobImpl; -import org.hibernate.lob.ClobImpl; -import org.hibernate.lob.SerializableBlob; -import org.hibernate.lob.SerializableClob; import org.hibernate.proxy.HibernateProxy; import org.hibernate.proxy.LazyInitializer; import org.hibernate.type.AnyType; @@ -386,54 +388,163 @@ public final class Hibernate { } /** - * Create a new Blob. The returned object will be initially immutable. + * Create a new {@link Blob}. The returned object will be initially immutable. * * @param bytes a byte array * @return the Blob + * @deprecated Use {@link #createBlob(byte[], Session)} instead */ public static Blob createBlob(byte[] bytes) { - return new SerializableBlob( new BlobImpl( bytes ) ); + return NonContextualLobCreator.INSTANCE.wrap( + NonContextualLobCreator.INSTANCE.createBlob( bytes ) + ); } /** - * Create a new Blob. The returned object will be initially immutable. + * Create a new {@link Blob}. + * + * @param bytes a byte array + * @param session The session in which the {@link Blob} will be used. + * @return the Blob + */ + public static Blob createBlob(byte[] bytes, Session session) { + // todo : wrap? + return getLobCreator( session ).createBlob( bytes ); + } + + public static LobCreator getLobCreator(Session session) { + return getLobCreator( ( SessionImplementor ) session ); + } + + public static LobCreator getLobCreator(SessionImplementor session) { + return session.getFactory() + .getSettings() + .getJdbcSupport() + .getLobCreator( ( LobCreationContext ) session ); + } + + /** + * Create a new {@link Blob}. The returned object will be initially immutable. * * @param stream a binary stream * @param length the number of bytes in the stream * @return the Blob + * @deprecated Use {@link #createBlob(InputStream, long, Session)} instead */ public static Blob createBlob(InputStream stream, int length) { - return new SerializableBlob( new BlobImpl( stream, length ) ); + return NonContextualLobCreator.INSTANCE.wrap( + NonContextualLobCreator.INSTANCE.createBlob( stream, length ) + ); } /** - * Create a new Blob. The returned object will be initially immutable. + * Create a new {@link Blob}. The returned object will be initially immutable. + * + * @param stream a binary stream + * @param length the number of bytes in the stream + * @return the Blob + * @deprecated Use {@link #createBlob(InputStream, long, Session)} instead + */ + public static Blob createBlob(InputStream stream, long length) { + return NonContextualLobCreator.INSTANCE.wrap( + NonContextualLobCreator.INSTANCE.createBlob( stream, length ) + ); + } + + /** + * Create a new {@link Blob}. + * + * @param stream a binary stream + * @param length the number of bytes in the stream + * @param session The session in which the {@link Blob} will be used. + * @return the Blob + */ + public static Blob createBlob(InputStream stream, long length, Session session) { + // todo : wrap? + return getLobCreator( session ).createBlob( stream, length ); + } + + /** + * Create a new {@link Blob}. The returned object will be initially immutable. + *
+ * NOTE: this method will read the entire contents of the incoming stream in order to properly + * handle the {@link Blob#length()} method. If you do not want the stream read, use the + * {@link #createBlob(InputStream,long)} version instead. * * @param stream a binary stream * @return the Blob - * @throws IOException + * @throws IOException Indicates an I/O problem accessing the stream + * @deprecated Use {@link #createBlob(InputStream, long, Session)} instead */ public static Blob createBlob(InputStream stream) throws IOException { - return new SerializableBlob( new BlobImpl( stream, stream.available() ) ); + ByteArrayOutputStream buffer = new ByteArrayOutputStream( stream.available() ); + StreamUtils.copy( stream, buffer ); + return createBlob( buffer.toByteArray() ); } /** - * Create a new Clob. The returned object will be initially immutable. + * Create a new {@link Clob}. The returned object will be initially immutable. * - * @param string a String + * @param string The string data + * @return The created {@link Clob} + * @deprecated Use {@link #createClob(String, Session)} instead */ public static Clob createClob(String string) { - return new SerializableClob( new ClobImpl( string ) ); + return NonContextualLobCreator.INSTANCE.wrap( + NonContextualLobCreator.INSTANCE.createClob( string ) + ); } /** - * Create a new Clob. The returned object will be initially immutable. + * Create a new {@link Clob}. + * + * @param string The string data + * @param session The session in which the {@link Clob} will be used. + * @return The created {@link Clob} + */ + public static Clob createClob(String string, Session session) { + // todo : wrap? + return getLobCreator( session ).createClob( string ); + } + + /** + * Create a new {@link Clob}. The returned object will be initially immutable. * * @param reader a character stream * @param length the number of characters in the stream + * @return The created {@link Clob} + * @deprecated Use {@link #createClob(Reader,long,Session)} instead */ public static Clob createClob(Reader reader, int length) { - return new SerializableClob( new ClobImpl( reader, length ) ); + return NonContextualLobCreator.INSTANCE.wrap( + NonContextualLobCreator.INSTANCE.createClob( reader, length ) + ); + } + + /** + * Create a new {@link Clob}. The returned object will be initially immutable. + * + * @param reader a character stream + * @param length the number of characters in the stream + * @return The created {@link Clob} + * @deprecated Use {@link #createClob(Reader,long,Session)} instead + */ + public static Clob createClob(Reader reader, long length) { + return NonContextualLobCreator.INSTANCE.wrap( + NonContextualLobCreator.INSTANCE.createClob( reader, length ) + ); + } + + /** + * Create a new {@link Clob}. + * + * @param reader a character stream + * @param length the number of characters in the stream + * @param session The session in which the {@link Clob} will be used. + * @return The created {@link Clob} + */ + public static Clob createClob(Reader reader, long length, Session session) { + return getLobCreator( session ).createClob( reader, length ); } /** @@ -460,8 +571,7 @@ public final class Hibernate { * * @param proxy The potential proxy * @param propertyName the name of a persistent attribute of the object - * @return true if the named property of the object is not listed as uninitialized - * @return false if the object is an uninitialized proxy, or the named property is uninitialized + * @return true if the named property of the object is not listed as uninitialized; false otherwise */ public static boolean isPropertyInitialized(Object proxy, String propertyName) { diff --git a/core/src/main/java/org/hibernate/cfg/Settings.java b/core/src/main/java/org/hibernate/cfg/Settings.java index 3292e53f2b..a7d8b7428f 100644 --- a/core/src/main/java/org/hibernate/cfg/Settings.java +++ b/core/src/main/java/org/hibernate/cfg/Settings.java @@ -28,6 +28,7 @@ import java.util.Map; import org.hibernate.ConnectionReleaseMode; import org.hibernate.EntityMode; +import org.hibernate.engine.jdbc.JdbcSupport; import org.hibernate.tuple.entity.EntityTuplizerFactory; import org.hibernate.tuple.component.ComponentTuplizerFactory; import org.hibernate.cache.QueryCacheFactory; @@ -98,6 +99,7 @@ public final class Settings { private boolean checkNullability; // private ComponentTuplizerFactory componentTuplizerFactory; todo : HHH-3517 and HHH-1907 // private BytecodeProvider bytecodeProvider; + private JdbcSupport jdbcSupport; /** * Package protected constructor @@ -299,8 +301,12 @@ public final class Settings { // return componentTuplizerFactory; // } + public JdbcSupport getJdbcSupport() { + return jdbcSupport; + } -// package protected setters ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + // package protected setters ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // void setShowSqlEnabled(boolean b) { // showSql = b; @@ -502,8 +508,11 @@ public final class Settings { // this.componentTuplizerFactory = componentTuplizerFactory; // } + void setJdbcSupport(JdbcSupport jdbcSupport) { + this.jdbcSupport = jdbcSupport; + } -// public BytecodeProvider getBytecodeProvider() { + // public BytecodeProvider getBytecodeProvider() { // return bytecodeProvider; // } // diff --git a/core/src/main/java/org/hibernate/cfg/SettingsFactory.java b/core/src/main/java/org/hibernate/cfg/SettingsFactory.java index 59e60fe518..3b2fc8197b 100644 --- a/core/src/main/java/org/hibernate/cfg/SettingsFactory.java +++ b/core/src/main/java/org/hibernate/cfg/SettingsFactory.java @@ -38,6 +38,8 @@ import org.slf4j.LoggerFactory; import org.hibernate.ConnectionReleaseMode; import org.hibernate.EntityMode; import org.hibernate.HibernateException; +import org.hibernate.engine.jdbc.JdbcSupport; +import org.hibernate.engine.jdbc.JdbcSupportLoader; import org.hibernate.bytecode.BytecodeProvider; import org.hibernate.cache.QueryCacheFactory; import org.hibernate.cache.RegionFactory; @@ -97,6 +99,7 @@ public class SettingsFactory implements Serializable { boolean metaReportsDDLCausesTxnCommit = false; boolean metaReportsDDLInTxnSupported = true; Dialect dialect = null; + JdbcSupport jdbcSupport = null; // 'hibernate.temp.use_jdbc_metadata_defaults' is a temporary magic value. // The need for it is intended to be alleviated with future developement, thus it is @@ -115,6 +118,7 @@ public class SettingsFactory implements Serializable { log.info( "JDBC driver: " + meta.getDriverName() + ", version: " + meta.getDriverVersion() ); dialect = DialectFactory.buildDialect( props, conn ); + jdbcSupport = JdbcSupportLoader.loadJdbcSupport( conn ); metaSupportsScrollable = meta.supportsResultSetType( ResultSet.TYPE_SCROLL_INSENSITIVE ); metaSupportsBatchUpdates = meta.supportsBatchUpdates(); @@ -145,6 +149,10 @@ public class SettingsFactory implements Serializable { settings.setDataDefinitionImplicitCommit( metaReportsDDLCausesTxnCommit ); settings.setDataDefinitionInTransactionSupported( metaReportsDDLInTxnSupported ); settings.setDialect( dialect ); + if ( jdbcSupport == null ) { + jdbcSupport = JdbcSupportLoader.loadJdbcSupport( null ); + } + settings.setJdbcSupport( jdbcSupport ); //use dialect default properties final Properties properties = new Properties(); diff --git a/core/src/main/java/org/hibernate/engine/jdbc/AbstractLobCreator.java b/core/src/main/java/org/hibernate/engine/jdbc/AbstractLobCreator.java new file mode 100644 index 0000000000..6f76e4a0ba --- /dev/null +++ b/core/src/main/java/org/hibernate/engine/jdbc/AbstractLobCreator.java @@ -0,0 +1,53 @@ +/* + * 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.Blob; +import java.sql.Clob; + +/** + * Convenient base class for proxy-based LobCreator for handling wrapping. + * + * @author Steve Ebersole + */ +public abstract class AbstractLobCreator implements LobCreator { + /** + * {@inheritDoc} + */ + public Blob wrap(Blob blob) { + return SerializableBlobProxy.generateProxy( blob ); + } + + /** + * {@inheritDoc} + */ + public Clob wrap(Clob clob) { + if ( SerializableNClobProxy.isNClob( clob ) ) { + return SerializableNClobProxy.generateProxy( clob ); + } + else { + return SerializableClobProxy.generateProxy( clob ); + } + } +} diff --git a/core/src/main/java/org/hibernate/engine/jdbc/BlobImplementer.java b/core/src/main/java/org/hibernate/engine/jdbc/BlobImplementer.java new file mode 100644 index 0000000000..76dd4a8e4b --- /dev/null +++ b/core/src/main/java/org/hibernate/engine/jdbc/BlobImplementer.java @@ -0,0 +1,32 @@ +/* + * 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; + +/** + * Marker interface for non-contextually created {@link java.sql.Blob} instances.. + * + * @author Steve Ebersole + */ +public interface BlobImplementer { +} diff --git a/core/src/main/java/org/hibernate/engine/jdbc/BlobProxy.java b/core/src/main/java/org/hibernate/engine/jdbc/BlobProxy.java new file mode 100644 index 0000000000..579efa0262 --- /dev/null +++ b/core/src/main/java/org/hibernate/engine/jdbc/BlobProxy.java @@ -0,0 +1,154 @@ +/* + * 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.lang.reflect.InvocationHandler; +import java.lang.reflect.Method; +import java.lang.reflect.Proxy; +import java.sql.Blob; +import java.sql.SQLException; +import java.io.InputStream; +import java.io.ByteArrayInputStream; +import java.io.IOException; + +/** + * Manages aspects of proxying {@link Blob Blobs} for non-contextual creation, including proxy creation and + * handling proxy invocations. + * + * @author Gavin King + * @author Steve Ebersole + * @author Gail Badner + */ +public class BlobProxy implements InvocationHandler { + private static final Class[] PROXY_INTERFACES = new Class[] { Blob.class, BlobImplementer.class }; + + private InputStream stream; + private long length; + private boolean needsReset = false; + + /** + * Ctor used to build {@link Blob} from byte array. + * + * @param bytes The byte array + * @see #generateProxy(byte[]) + */ + private BlobProxy(byte[] bytes) { + this.stream = new ByteArrayInputStream( bytes ); + this.length = bytes.length; + } + + /** + * Ctor used to build {@link Blob} from a stream. + * + * @param stream The binary stream + * @param length The length of the stream + * @see #generateProxy(java.io.InputStream, long) + */ + private BlobProxy(InputStream stream, long length) { + this.stream = stream; + this.length = length; + } + + private long getLength() { + return length; + } + + private InputStream getStream() throws SQLException { + try { + if (needsReset) { + stream.reset(); + } + } + catch ( IOException ioe) { + throw new SQLException("could not reset reader"); + } + needsReset = true; + return stream; + } + + /** + * {@inheritDoc} + * + * @throws UnsupportedOperationException if any methods other than {@link Blob#length()} + * or {@link Blob#getBinaryStream} are invoked. + */ + public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { + if ( "length".equals( method.getName() ) ) { + return new Long( getLength() ); + } + if ( "getBinaryStream".equals( method.getName() ) && method.getParameterTypes().length == 0 ) { + return getStream(); + } + if ( "free".equals( method.getName() ) ) { + stream.close(); + return null; + } + throw new UnsupportedOperationException( "Blob may not be manipulated from creating session" ); + } + + /** + * Generates a BlobImpl proxy using byte data. + * + * @param bytes The data to be created as a Blob. + * + * @return The generated proxy. + */ + public static Blob generateProxy(byte[] bytes) { + return ( Blob ) Proxy.newProxyInstance( + getProxyClassLoader(), + PROXY_INTERFACES, + new BlobProxy( bytes ) + ); + } + + /** + * Generates a BlobImpl proxy using a given number of bytes from an InputStream. + * + * @param stream The input stream of bytes to be created as a Blob. + * @param length The number of bytes from stream to be written to the Blob. + * + * @return The generated proxy. + */ + public static Blob generateProxy(InputStream stream, long length) { + return ( Blob ) Proxy.newProxyInstance( + getProxyClassLoader(), + PROXY_INTERFACES, + new BlobProxy( stream, length ) + ); + } + + /** + * Determines the appropriate class loader to which the generated proxy + * should be scoped. + * + * @return The class loader appropriate for proxy construction. + */ + private static ClassLoader getProxyClassLoader() { + ClassLoader cl = Thread.currentThread().getContextClassLoader(); + if ( cl == null ) { + cl = BlobImplementer.class.getClassLoader(); + } + return cl; + } +} diff --git a/core/src/main/java/org/hibernate/engine/jdbc/ClobImplementer.java b/core/src/main/java/org/hibernate/engine/jdbc/ClobImplementer.java new file mode 100644 index 0000000000..5bf378a8b1 --- /dev/null +++ b/core/src/main/java/org/hibernate/engine/jdbc/ClobImplementer.java @@ -0,0 +1,32 @@ +/* + * 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; + +/** + * Marker interface for non-contextually created {@link java.sql.Clob} instances.. + * + * @author Steve Ebersole + */ +public interface ClobImplementer { +} diff --git a/core/src/main/java/org/hibernate/engine/jdbc/ClobProxy.java b/core/src/main/java/org/hibernate/engine/jdbc/ClobProxy.java new file mode 100644 index 0000000000..ecf888ba29 --- /dev/null +++ b/core/src/main/java/org/hibernate/engine/jdbc/ClobProxy.java @@ -0,0 +1,168 @@ +/* + * 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.lang.reflect.InvocationHandler; +import java.lang.reflect.Method; +import java.lang.reflect.Proxy; +import java.sql.Clob; +import java.sql.SQLException; +import java.io.Reader; +import java.io.StringReader; +import java.io.InputStream; +import java.io.IOException; + +/** + * Manages aspects of proxying {@link Clob Clobs} for non-contextual creation, including proxy creation and + * handling proxy invocations. + * + * @author Gavin King + * @author Steve Ebersole + * @author Gail Badner + */ +public class ClobProxy implements InvocationHandler { + private static final Class[] PROXY_INTERFACES = new Class[] { Clob.class, ClobImplementer.class }; + + private Reader reader; + private long length; + private boolean needsReset = false; + + + /** + * Ctor used to build {@link Clob} from string data. + * + * @param string The byte array + * @see #generateProxy(String) + */ + protected ClobProxy(String string) { + reader = new StringReader(string); + length = string.length(); + } + + /** + * Ctor used to build {@link Clob} from a reader. + * + * @param reader The character reader. + * @param length The length of the reader stream. + * @see #generateProxy(java.io.Reader, long) + */ + protected ClobProxy(Reader reader, long length) { + this.reader = reader; + this.length = length; + } + + protected long getLength() { + return length; + } + + protected InputStream getAsciiStream() throws SQLException { + resetIfNeeded(); + return new ReaderInputStream( reader ); + } + + protected Reader getCharacterStream() throws SQLException { + resetIfNeeded(); + return reader; + } + + /** + * {@inheritDoc} + * + * @throws UnsupportedOperationException if any methods other than {@link Clob#length()}, + * {@link Clob#getAsciiStream()}, or {@link Clob#getCharacterStream()} are invoked. + */ + public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { + if ( "length".equals( method.getName() ) ) { + return new Long( getLength() ); + } + if ( "getAsciiStream".equals( method.getName() ) ) { + return getAsciiStream(); + } + if ( "getCharacterStream".equals( method.getName() ) ) { + return getCharacterStream(); + } + if ( "free".equals( method.getName() ) ) { + reader.close(); + return null; + } + throw new UnsupportedOperationException( "Clob may not be manipulated from creating session" ); + } + + protected void resetIfNeeded() throws SQLException { + try { + if ( needsReset ) { + reader.reset(); + } + } + catch ( IOException ioe ) { + throw new SQLException( "could not reset reader" ); + } + needsReset = true; + } + + /** + * Generates a {@link Clob} proxy using the string data. + * + * @param string The data to be wrapped as a {@link Clob}. + * + * @return The generated proxy. + */ + public static Clob generateProxy(String string) { + return ( Clob ) Proxy.newProxyInstance( + getProxyClassLoader(), + PROXY_INTERFACES, + new ClobProxy( string ) + ); + } + + /** + * Generates a {@link Clob} proxy using a character reader of given length. + * + * @param reader The character reader + * @param length The length of the character reader + * + * @return The generated proxy. + */ + public static Clob generateProxy(Reader reader, long length) { + return ( Clob ) Proxy.newProxyInstance( + getProxyClassLoader(), + PROXY_INTERFACES, + new ClobProxy( reader, length ) + ); + } + + /** + * Determines the appropriate class loader to which the generated proxy + * should be scoped. + * + * @return The class loader appropriate for proxy construction. + */ + protected static ClassLoader getProxyClassLoader() { + ClassLoader cl = Thread.currentThread().getContextClassLoader(); + if ( cl == null ) { + cl = ClobImplementer.class.getClassLoader(); + } + return cl; + } +} diff --git a/core/src/main/java/org/hibernate/jdbc/ColumnNameCache.java b/core/src/main/java/org/hibernate/engine/jdbc/ColumnNameCache.java similarity index 79% rename from core/src/main/java/org/hibernate/jdbc/ColumnNameCache.java rename to core/src/main/java/org/hibernate/engine/jdbc/ColumnNameCache.java index 73125be80e..5b65757708 100644 --- a/core/src/main/java/org/hibernate/jdbc/ColumnNameCache.java +++ b/core/src/main/java/org/hibernate/engine/jdbc/ColumnNameCache.java @@ -20,35 +20,36 @@ * Free Software Foundation, Inc. * 51 Franklin Street, Fifth Floor * Boston, MA 02110-1301 USA - * */ -package org.hibernate.jdbc; +package org.hibernate.engine.jdbc; import java.sql.SQLException; +import java.sql.ResultSet; import java.util.HashMap; import java.util.Map; /** - * Implementation of ColumnNameCache. + * Cache of column-name -> column-index resolutions * * @author Steve Ebersole */ public class ColumnNameCache { + public static final float LOAD_FACTOR = .75f; private final Map columnNameToIndexCache; public ColumnNameCache(int columnCount) { // should *not* need to grow beyond the size of the total number of columns in the rs - this.columnNameToIndexCache = new HashMap( columnCount ); + this.columnNameToIndexCache = new HashMap( columnCount + (int)( columnCount * LOAD_FACTOR ) + 1, LOAD_FACTOR ); } - public int getIndexForColumnName(String columnName, ResultSetWrapper rs)throws SQLException { + public int getIndexForColumnName(String columnName, ResultSet rs) throws SQLException { Integer cached = ( Integer ) columnNameToIndexCache.get( columnName ); if ( cached != null ) { return cached.intValue(); } else { - int index = rs.getTarget().findColumn( columnName ); + int index = rs.findColumn( columnName ); columnNameToIndexCache.put( columnName, new Integer(index) ); return index; } diff --git a/core/src/main/java/org/hibernate/engine/jdbc/ContextualLobCreator.java b/core/src/main/java/org/hibernate/engine/jdbc/ContextualLobCreator.java new file mode 100644 index 0000000000..69835d37cc --- /dev/null +++ b/core/src/main/java/org/hibernate/engine/jdbc/ContextualLobCreator.java @@ -0,0 +1,237 @@ +/* + * 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.Blob; +import java.sql.SQLException; +import java.sql.Clob; +import java.sql.Connection; +import java.io.InputStream; +import java.io.OutputStream; +import java.io.IOException; +import java.io.Reader; +import java.io.Writer; +import java.lang.reflect.Method; +import java.lang.reflect.InvocationTargetException; + +import org.hibernate.HibernateException; + +/** + * {@link LobCreator} implementation using contextual creation against the JDBC {@link java.sql.Connection} class's LOB creation + * methods. + * + * @author Steve Ebersole + * @author Gail Badner + */ +public class ContextualLobCreator extends AbstractLobCreator implements LobCreator { + private LobCreationContext lobCreationContext; + + public ContextualLobCreator(LobCreationContext lobCreationContext) { + this.lobCreationContext = lobCreationContext; + } + + /** + * Create the basic contextual BLOB reference. + * + * @return The created BLOB reference. + */ + public Blob createBlob() { + return ( Blob ) lobCreationContext.execute( CREATE_BLOB_CALLBACK ); + } + + /** + * {@inheritDoc} + */ + public Blob createBlob(byte[] bytes) { + try { + Blob blob = createBlob(); + blob.setBytes( 1, bytes ); + return blob; + } + catch ( SQLException e ) { + throw new IllegalStateException( "Unable to set BLOB bytes after creation", e ); + } + } + + /** + * {@inheritDoc} + */ + public Blob createBlob(InputStream inputStream, long length) { + try { + Blob blob = createBlob(); + OutputStream byteStream = blob.setBinaryStream( 1 ); + StreamUtils.copy( inputStream, byteStream ); + byteStream.flush(); + byteStream.close(); + // todo : validate length written versus length given? + return blob; + } + catch ( SQLException e ) { + throw new IllegalStateException( "Unable to prepare BLOB binary stream for writing", e ); + } + catch ( IOException e ) { + throw new IllegalStateException( "Unable to write stream contents to BLOB", e ); + } + } + + /** + * Create the basic contextual CLOB reference. + * + * @return The created CLOB reference. + */ + public Clob createClob() { + return ( Clob ) lobCreationContext.execute( CREATE_CLOB_CALLBACK ); + } + + /** + * {@inheritDoc} + */ + public Clob createClob(String string) { + try { + Clob clob = createClob(); + clob.setString( 1, string ); + return clob; + } + catch ( SQLException e ) { + throw new IllegalStateException( "Unable to set CLOB string after creation", e ); + } + } + + /** + * {@inheritDoc} + */ + public Clob createClob(Reader reader, long length) { + try { + Clob clob = createClob(); + Writer writer = clob.setCharacterStream( 1 ); + StreamUtils.copy( reader, writer ); + writer.flush(); + writer.close(); + return clob; + } + catch ( SQLException e ) { + throw new IllegalStateException( "Unable to prepare CLOB stream for writing", e ); + } + catch ( IOException e ) { + throw new IllegalStateException( "Unable to write CLOB stream content", e ); + } + } + + /** + * Create the basic contextual NCLOB reference. + * + * @return The created NCLOB reference. + */ + public Clob createNClob() { + return ( Clob ) lobCreationContext.execute( CREATE_NCLOB_CALLBACK ); + } + + /** + * {@inheritDoc} + */ + public Clob createNClob(String string) { + try { + Clob clob = createNClob(); + clob.setString( 1, string ); + return clob; + } + catch ( SQLException e ) { + throw new IllegalStateException( "Unable to set NCLOB string after creation", e ); + } + } + + /** + * {@inheritDoc} + */ + public Clob createNClob(Reader reader, long length) { + try { + Clob clob = createNClob(); + Writer writer = clob.setCharacterStream( 1 ); + StreamUtils.copy( reader, writer ); + writer.flush(); + writer.close(); + return clob; + } + catch ( SQLException e ) { + throw new IllegalStateException( "Unable to prepare NCLOB stream for writing", e ); + } + catch ( IOException e ) { + throw new IllegalStateException( "Unable to write NCLOB stream content", e ); + } + } + + + private static final Class[] CREATION_METHOD_SIG = new Class[0]; + private static final Object[] CREATION_METHOD_ARGS = new Object[0]; + + private static final LobCreationContext.Callback CREATE_BLOB_CALLBACK; + private static final LobCreationContext.Callback CREATE_CLOB_CALLBACK; + private static final LobCreationContext.Callback CREATE_NCLOB_CALLBACK; + + static { + CREATE_BLOB_CALLBACK = new CallbackImpl( getConnectionlobCreationMethod( "createBlob" ) ); + CREATE_CLOB_CALLBACK = new CallbackImpl( getConnectionlobCreationMethod( "createClob" ) ); + CREATE_NCLOB_CALLBACK = new CallbackImpl( getConnectionlobCreationMethod( "createNClob" ) ); + } + + private static class CallbackImpl implements LobCreationContext.Callback { + private final Method creationMethod; + + private CallbackImpl(Method creationMethod) { + this.creationMethod = creationMethod; + } + + public Object executeOnConnection(Connection connection) throws SQLException { + try { + return creationMethod.invoke( connection, CREATION_METHOD_ARGS ); + } + catch ( InvocationTargetException e ) { + if ( e.getTargetException() instanceof SQLException ) { + throw ( SQLException ) e.getTargetException(); + } + else { + throw new HibernateException( "Exception invoking " + creationMethod.getName(), e.getTargetException() ); + } + } + catch ( AbstractMethodError e ) { + // this again is a big big error... + throw new IllegalStateException( "Useable implementation of " + creationMethod.getName() + " not found.", e ); + } + catch ( IllegalAccessException e ) { + // this again is a big big error... + throw new IllegalStateException( "Illegal access attempt on JDBC method " + creationMethod.getName(), e ); + } + } + } + + private static Method getConnectionlobCreationMethod(String methodName) { + try { + return Connection.class.getMethod( methodName, CREATION_METHOD_SIG ); + } + catch ( NoSuchMethodException e ) { + // this is a big big error if we get here and these methods are not part of the Connection interface... + throw new IllegalStateException( "JDBC driver did not implement " + methodName, e ); + } + } +} diff --git a/core/src/main/java/org/hibernate/engine/jdbc/JdbcSupportImpl.java b/core/src/main/java/org/hibernate/engine/jdbc/JdbcSupportImpl.java new file mode 100644 index 0000000000..75567debfb --- /dev/null +++ b/core/src/main/java/org/hibernate/engine/jdbc/JdbcSupportImpl.java @@ -0,0 +1,59 @@ +/* + * 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; + +/** + * TODO : javadoc + * + * @author Steve Ebersole + */ +public class JdbcSupportImpl implements JdbcSupport { + private final boolean useContextualLobCreation; + + public JdbcSupportImpl(boolean useContextualLobCreation) { + this.useContextualLobCreation = useContextualLobCreation; + } + + /** + * {@inheritDoc} + */ + public LobCreator getLobCreator() { + return NonContextualLobCreator.INSTANCE; + } + + public LobCreator getLobCreator(LobCreationContext lobCreationContext) { + if ( useContextualLobCreation ) { + return new ContextualLobCreator( lobCreationContext ); + } + else { + return NonContextualLobCreator.INSTANCE; + } + } + + public ResultSet wrap(ResultSet resultSet, ColumnNameCache columnNameCache) { + return ResultSetWrapperProxy.generateProxy( resultSet, columnNameCache ); + } +} diff --git a/core/src/main/java/org/hibernate/engine/jdbc/JdbcSupportLoader.java b/core/src/main/java/org/hibernate/engine/jdbc/JdbcSupportLoader.java new file mode 100644 index 0000000000..b09e00d2ee --- /dev/null +++ b/core/src/main/java/org/hibernate/engine/jdbc/JdbcSupportLoader.java @@ -0,0 +1,97 @@ +/* + * 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.Connection; +import java.lang.reflect.Method; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Builds {@link JdbcSupport} instances based on the capabilities of the environment. + * + * @author Steve Ebersole + */ +public class JdbcSupportLoader { + private static final Logger log = LoggerFactory.getLogger( JdbcSupportLoader.class ); + + /** + * The public factory method for obtaining the appropriate (accoring to given JDBC {@link java.sql.Connection}) + * {@link JdbcSupport}. + * + * @param jdbcConnection A JDBC {@link java.sql.Connection} which can be used to gauge the drivers level of support, + * specifically for creating LOB references. + * @return An appropriate {@link JdbcSupport} instance. + */ + public static JdbcSupport loadJdbcSupport(Connection jdbcConnection) { + return new JdbcSupportImpl( useContextualLobCreation( jdbcConnection ) ); + } + + private static final Class[] NO_ARG_SIG = new Class[0]; + private static final Object[] NO_ARGS = new Object[0]; + + /** + * Basically here we are simply checking whether we can call the {@link Connection} methods for + * LOB creation added in JDBC 4. We not only check whether the {@link Connection} declares these methods, + * but also whether the actual {@link Connection} instance implements them (i.e. can be called without simply + * throwing an exception). + * + * @param jdbcConnection The connection whcih can be used in level-of-support testing. + * @return True if the connection can be used to create LOBs; false otherwise. + */ + private static boolean useContextualLobCreation(Connection jdbcConnection) { + if ( jdbcConnection == null ) { + return false; + } + + try { + Class connectionClass = Connection.class; + Method createClobMethod = connectionClass.getMethod( "createClob", NO_ARG_SIG ); + if ( createClobMethod.getDeclaringClass().equals( Connection.class ) ) { + // If we get here we are running in a jdk 1.6 (jdbc 4) environment... + // Further check to make sure the driver actually implements the LOB creation methods. We + // check against createClob() as indicative of all; should we check against all 3 explicitly? + try { + Object clob = createClobMethod.invoke( jdbcConnection, NO_ARGS ); + try { + Method freeMethod = clob.getClass().getMethod( "free", NO_ARG_SIG ); + freeMethod.invoke( clob, NO_ARGS ); + } + catch ( Throwable ignore ) { + log.trace( "Unable to free CLOB created to test createClob() implementation : " + ignore ); + } + return true; + } + catch ( Throwable t ) { + log.info( "createClob() method threw error : " + t ); + } + } + } + catch ( NoSuchMethodException ignore ) { + } + + return false; + } +} diff --git a/core/src/main/java/org/hibernate/engine/jdbc/LobCreationContext.java b/core/src/main/java/org/hibernate/engine/jdbc/LobCreationContext.java new file mode 100644 index 0000000000..073c4e2106 --- /dev/null +++ b/core/src/main/java/org/hibernate/engine/jdbc/LobCreationContext.java @@ -0,0 +1,60 @@ +/* + * 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.Connection; +import java.sql.SQLException; + +import org.hibernate.JDBCException; +import org.hibernate.exception.SQLExceptionConverter; + +/** + * Provides callback access into the context in which the LOB is to be created. Mainly this is useful + * for gaining access to the JDBC {@link Connection} for use in JDBC 4 environments. + * + * @author Steve Ebersole + */ +public interface LobCreationContext { + /** + * The callback contract for making use of the JDBC {@link Connection}. + */ + public static interface Callback { + /** + * Perform whatever actions are necessary using the provided JDBC {@link Connection}. + * + * @param connection The JDBC {@link Connection}. + * @return The created LOB. + * @throws SQLException + */ + public Object executeOnConnection(Connection connection) throws SQLException; + } + + /** + * Execute the given callback, making sure it has access to a viable JDBC {@link Connection}. + * + * @param callback The callback to execute . + * @return The LOB created by the callback. + */ + public Object execute(Callback callback); +} diff --git a/core/src/main/java/org/hibernate/engine/jdbc/NClobImplementer.java b/core/src/main/java/org/hibernate/engine/jdbc/NClobImplementer.java new file mode 100644 index 0000000000..5d992095bb --- /dev/null +++ b/core/src/main/java/org/hibernate/engine/jdbc/NClobImplementer.java @@ -0,0 +1,34 @@ +/* + * 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; + +/** + * Marker interface for non-contextually created java.sql.NClob instances.. + * + * java.sql.NClob is a new type introduced in JDK 1.6 (JDBC 4) + * + * @author Steve Ebersole + */ +public interface NClobImplementer { +} diff --git a/core/src/main/java/org/hibernate/engine/jdbc/NClobProxy.java b/core/src/main/java/org/hibernate/engine/jdbc/NClobProxy.java new file mode 100644 index 0000000000..e5a1739a14 --- /dev/null +++ b/core/src/main/java/org/hibernate/engine/jdbc/NClobProxy.java @@ -0,0 +1,105 @@ +/* + * 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.Clob; +import java.io.Reader; +import java.lang.reflect.Proxy; + +/** + * Manages aspects of proxying java.sql.NClobs for non-contextual creation, including proxy creation and + * handling proxy invocations. + * + * Generated proxies are typed as {@link java.sql.Clob} (java.sql.NClob extends {@link java.sql.Clob}) and in JDK 1.6 environments, they + * are also typed to java.sql.NClob + * + * @author Steve Ebersole + */ +public class NClobProxy extends ClobProxy { + public static final Class[] PROXY_INTERFACES = new Class[] { determineNClobInterface(), NClobImplementer.class }; + + private static Class determineNClobInterface() { + // java.sql.NClob is a simple marker interface extending java.sql.Clob. So if java.sql.NClob is not available + // on the classloader, just use java.sql.Clob + try { + return getProxyClassLoader().loadClass( "java.sql.NClob" ); + } + catch ( ClassNotFoundException e ) { + return Clob.class; + } + } + + protected NClobProxy(String string) { + super( string ); + } + + protected NClobProxy(Reader reader, long length) { + super( reader, length ); + } + + /** + * Generates a {@link java.sql.Clob} proxy using the string data. + * + * @param string The data to be wrapped as a {@link java.sql.Clob}. + * + * @return The generated proxy. + */ + public static Clob generateProxy(String string) { + return ( Clob ) Proxy.newProxyInstance( + getProxyClassLoader(), + PROXY_INTERFACES, + new ClobProxy( string ) + ); + } + + /** + * Generates a {@link Clob} proxy using a character reader of given length. + * + * @param reader The character reader + * @param length The length of the character reader + * + * @return The generated proxy. + */ + public static Clob generateProxy(Reader reader, long length) { + return ( Clob ) Proxy.newProxyInstance( + getProxyClassLoader(), + PROXY_INTERFACES, + new ClobProxy( reader, length ) + ); + } + + /** + * Determines the appropriate class loader to which the generated proxy + * should be scoped. + * + * @return The class loader appropriate for proxy construction. + */ + protected static ClassLoader getProxyClassLoader() { + ClassLoader cl = Thread.currentThread().getContextClassLoader(); + if ( cl == null ) { + cl = NClobImplementer.class.getClassLoader(); + } + return cl; + } +} diff --git a/core/src/main/java/org/hibernate/engine/jdbc/NonContextualLobCreator.java b/core/src/main/java/org/hibernate/engine/jdbc/NonContextualLobCreator.java new file mode 100644 index 0000000000..ee7d938501 --- /dev/null +++ b/core/src/main/java/org/hibernate/engine/jdbc/NonContextualLobCreator.java @@ -0,0 +1,85 @@ +/* + * 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.Blob; +import java.sql.Clob; +import java.io.InputStream; +import java.io.Reader; + +/** + * {@link LobCreator} implementation using non-contextual or local creation, meaning that we generate the LOB + * references ourselves as opposed to delegating to the JDBC {@link java.sql.Connection}. + * + * @author Steve Ebersole + * @author Gail Badner + */ +public class NonContextualLobCreator extends AbstractLobCreator implements LobCreator { + public static final NonContextualLobCreator INSTANCE = new NonContextualLobCreator(); + + private NonContextualLobCreator() { + } + + /** + * {@inheritDoc} + */ + public Blob createBlob(byte[] bytes) { + return BlobProxy.generateProxy( bytes ); + } + + /** + * {@inheritDoc} + */ + public Blob createBlob(InputStream stream, long length) { + return BlobProxy.generateProxy( stream, length ); + } + + /** + * {@inheritDoc} + */ + public Clob createClob(String string) { + return ClobProxy.generateProxy( string ); + } + + /** + * {@inheritDoc} + */ + public Clob createClob(Reader reader, long length) { + return ClobProxy.generateProxy( reader, length ); + } + + /** + * {@inheritDoc} + */ + public Clob createNClob(String string) { + return NClobProxy.generateProxy( string ); + } + + /** + * {@inheritDoc} + */ + public Clob createNClob(Reader reader, long length) { + return NClobProxy.generateProxy( reader, length ); + } +} diff --git a/core/src/main/java/org/hibernate/lob/ReaderInputStream.java b/core/src/main/java/org/hibernate/engine/jdbc/ReaderInputStream.java similarity index 93% rename from core/src/main/java/org/hibernate/lob/ReaderInputStream.java rename to core/src/main/java/org/hibernate/engine/jdbc/ReaderInputStream.java index 6157934e63..02a65f95d0 100755 --- a/core/src/main/java/org/hibernate/lob/ReaderInputStream.java +++ b/core/src/main/java/org/hibernate/engine/jdbc/ReaderInputStream.java @@ -22,18 +22,18 @@ * Boston, MA 02110-1301 USA * */ -package org.hibernate.lob; +package org.hibernate.engine.jdbc; import java.io.IOException; import java.io.InputStream; import java.io.Reader; /** - * Exposes a Reader as an InputStream + * Exposes a {@link Reader} as an {@link InputStream}. + * * @author Gavin King */ public class ReaderInputStream extends InputStream { - private Reader reader; public ReaderInputStream(Reader reader) { diff --git a/core/src/main/java/org/hibernate/engine/jdbc/ResultSetWrapperProxy.java b/core/src/main/java/org/hibernate/engine/jdbc/ResultSetWrapperProxy.java new file mode 100644 index 0000000000..7dd457fa3e --- /dev/null +++ b/core/src/main/java/org/hibernate/engine/jdbc/ResultSetWrapperProxy.java @@ -0,0 +1,194 @@ +/* + * 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.lang.reflect.InvocationHandler; +import java.lang.reflect.Proxy; +import java.lang.reflect.Method; +import java.lang.reflect.InvocationTargetException; +import java.sql.ResultSet; +import java.sql.SQLException; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import org.hibernate.util.JDBCExceptionReporter; + +/** + * A proxy for a ResultSet delegate, responsible for locally caching the columnName-to-columnIndex resolution that + * has been found to be inefficient in a few vendor's drivers (i.e., Oracle and Postgres). + * + * @author Steve Ebersole + * @author Gail Badner + */ +public class ResultSetWrapperProxy implements InvocationHandler { + private static final Logger log = LoggerFactory.getLogger( ResultSetWrapperProxy.class ); + private static final Class[] PROXY_INTERFACES = new Class[] { ResultSet.class }; + + private final ResultSet rs; + private final ColumnNameCache columnNameCache; + + private ResultSetWrapperProxy(ResultSet rs, ColumnNameCache columnNameCache) { + this.rs = rs; + this.columnNameCache = columnNameCache; + } + + /** + * Generates a proxy wrapping the ResultSet. + * + * @param resultSet The resultSet to wrap. + * @param columnNameCache The cache storing data for converting column names to column indexes. + * @return The generated proxy. + */ + public static ResultSet generateProxy(ResultSet resultSet, ColumnNameCache columnNameCache) { + return ( ResultSet ) Proxy.newProxyInstance( + getProxyClassLoader(), + PROXY_INTERFACES, + new ResultSetWrapperProxy( resultSet, columnNameCache ) + ); + } + + /** + * Determines the appropriate class loader to which the generated proxy + * should be scoped. + * + * @return The class loader appropriate for proxy construction. + */ + public static ClassLoader getProxyClassLoader() { + ClassLoader cl = Thread.currentThread().getContextClassLoader(); + if ( cl == null ) { + cl = ResultSet.class.getClassLoader(); + } + return cl; + } + + /** + * {@inheritDoc} + */ + public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { + if ( "findColumn".equals( method.getName() ) ) { + return new Integer( findColumn( ( String ) args[0] ) ); + } + + if ( isFirstArgColumnLabel( method, args ) ) { + try { + int columnIndex = findColumn( ( String ) args[0] ); + return invokeMethod( + locateCorrespondingColumnIndexMethod( method ), buildColumnIndexMethodArgs( args, columnIndex ) + ); + } + catch ( SQLException ex ) { + StringBuffer buf = new StringBuffer() + .append( "Exception getting column index for column: [" ) + .append( args[0] ) + .append( "].\nReverting to using: [" ) + .append( args[0] ) + .append( "] as first argument for method: [" ) + .append( method ) + .append( "]" ); + JDBCExceptionReporter.logExceptions( ex, buf.toString() ); + } + catch ( NoSuchMethodException ex ) { + StringBuffer buf = new StringBuffer() + .append( "Exception switching from method: [" ) + .append( method ) + .append( "] to a method using the column index. Reverting to using: [" ) + .append( method ) + .append( "]" ); + if ( log.isWarnEnabled() ) { + log.warn( buf.toString() ); + } + } + } + return invokeMethod( method, args ); + } + + /** + * Locate the column index corresponding to the given column name via the cache. + * + * @param columnName The column name to resolve into an index. + * @return The column index corresponding to the given column name. + * @throws SQLException if the ResultSet object does not contain columnName or a database access error occurs + */ + private int findColumn(String columnName) throws SQLException { + return columnNameCache.getIndexForColumnName( columnName, rs ); + } + + private boolean isFirstArgColumnLabel(Method method, Object args[]) { + // method name should start with either get or update + if ( ! ( method.getName().startsWith( "get" ) || method.getName().startsWith( "update" ) ) ) { + return false; + } + + // method should have arguments, and have same number as incoming arguments + if ( ! ( method.getParameterTypes().length > 0 && args.length == method.getParameterTypes().length ) ) { + return false; + } + + // The first argument should be a String (the column name) + //noinspection RedundantIfStatement + if ( ! ( String.class.isInstance( args[0] ) && method.getParameterTypes()[0].equals( String.class ) ) ) { + return false; + } + + return true; + } + + /** + * For a given {@link ResultSet} method passed a column name, locate the corresponding method passed the same + * parameters but the column index. + * + * @param columnNameMethod The method passed the column name + * @return The corresponding method passed the column index. + * @throws NoSuchMethodException Should never happen, but... + */ + private Method locateCorrespondingColumnIndexMethod(Method columnNameMethod) throws NoSuchMethodException { + Class actualParameterTypes[] = new Class[columnNameMethod.getParameterTypes().length]; + actualParameterTypes[0] = int.class; + System.arraycopy( + columnNameMethod.getParameterTypes(), + 1, + actualParameterTypes, + 1, + columnNameMethod.getParameterTypes().length - 1 + ); + return columnNameMethod.getDeclaringClass().getMethod( columnNameMethod.getName(), actualParameterTypes ); + } + + private Object[] buildColumnIndexMethodArgs(Object[] incomingArgs, int columnIndex) { + Object actualArgs[] = new Object[incomingArgs.length]; + actualArgs[0] = new Integer( columnIndex ); + System.arraycopy( incomingArgs, 1, actualArgs, 1, incomingArgs.length - 1 ); + return actualArgs; + } + + private Object invokeMethod(Method method, Object args[]) throws Throwable { + try { + return method.invoke( rs, args ); + } + catch ( InvocationTargetException e ) { + throw e.getTargetException(); + } + } +} diff --git a/core/src/main/java/org/hibernate/engine/jdbc/SerializableBlobProxy.java b/core/src/main/java/org/hibernate/engine/jdbc/SerializableBlobProxy.java new file mode 100644 index 0000000000..1f857ee619 --- /dev/null +++ b/core/src/main/java/org/hibernate/engine/jdbc/SerializableBlobProxy.java @@ -0,0 +1,112 @@ +/* + * 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.Blob; +import java.lang.reflect.Method; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Proxy; +import java.lang.reflect.InvocationHandler; +import java.io.Serializable; + +import org.hibernate.HibernateException; + +/** + * Manages aspects of proxying {@link Blob Blobs} to add serializability. + * + * @author Gavin King + * @author Steve Ebersole + * @author Gail Badner + */ +public class SerializableBlobProxy implements InvocationHandler, Serializable { + private static final Class[] PROXY_INTERFACES = new Class[] { Blob.class, WrappedBlob.class, Serializable.class }; + + private transient final Blob blob; + + /** + * Builds a serializable {@link Blob} wrapper around the given {@link Blob}. + * + * @param blob The {@link Blob} to be wrapped. + * @see + */ + private SerializableBlobProxy(Blob blob) { + this.blob = blob; + } + + public Blob getWrappedBlob() { + if ( blob == null ) { + throw new IllegalStateException( "Blobs may not be accessed after serialization" ); + } + else { + return blob; + } + } + + /** + * {@inheritDoc} + */ + public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { + if ( "getWrappedBlob".equals( method.getName() ) ) { + return getWrappedBlob(); + } + try { + return method.invoke( getWrappedBlob(), args ); + } + catch ( AbstractMethodError e ) { + throw new HibernateException( "The JDBC driver does not implement the method: " + method, e ); + } + catch ( InvocationTargetException e ) { + throw e.getTargetException(); + } + } + + /** + * Generates a SerializableBlob proxy wrapping the provided Blob object. + * + * @param blob The Blob to wrap. + * + * @return The generated proxy. + */ + public static Blob generateProxy(Blob blob) { + return ( Blob ) Proxy.newProxyInstance( + getProxyClassLoader(), + PROXY_INTERFACES, + new SerializableBlobProxy( blob ) + ); + } + + /** + * Determines the appropriate class loader to which the generated proxy + * should be scoped. + * + * @return The class loader appropriate for proxy construction. + */ + public static ClassLoader getProxyClassLoader() { + ClassLoader cl = Thread.currentThread().getContextClassLoader(); + if ( cl == null ) { + cl = WrappedBlob.class.getClassLoader(); + } + return cl; + } +} diff --git a/core/src/main/java/org/hibernate/engine/jdbc/SerializableClobProxy.java b/core/src/main/java/org/hibernate/engine/jdbc/SerializableClobProxy.java new file mode 100644 index 0000000000..87ee1eec18 --- /dev/null +++ b/core/src/main/java/org/hibernate/engine/jdbc/SerializableClobProxy.java @@ -0,0 +1,111 @@ +/* + * 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.lang.reflect.InvocationHandler; +import java.lang.reflect.Method; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Proxy; +import java.sql.Clob; +import java.io.Serializable; + +import org.hibernate.HibernateException; + +/** + * Manages aspects of proxying {@link Clob Clobs} to add serializability. + * + * @author Gavin King + * @author Steve Ebersole + * @author Gail Badner + */ +public class SerializableClobProxy implements InvocationHandler, Serializable { + private static final Class[] PROXY_INTERFACES = new Class[] { Clob.class, WrappedClob.class, Serializable.class }; + + private transient final Clob clob; + + /** + * Builds a serializable {@link java.sql.Clob} wrapper around the given {@link java.sql.Clob}. + * + * @param clob The {@link java.sql.Clob} to be wrapped. + * @see #generateProxy(java.sql.Clob) + */ + protected SerializableClobProxy(Clob clob) { + this.clob = clob; + } + + public Clob getWrappedClob() { + if ( clob == null ) { + throw new IllegalStateException( "Clobs may not be accessed after serialization" ); + } + else { + return clob; + } + } + + /** + * {@inheritDoc} + */ + public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { + if ( "getWrappedClob".equals( method.getName() ) ) { + return getWrappedClob(); + } + try { + return method.invoke( getWrappedClob(), args ); + } + catch ( AbstractMethodError e ) { + throw new HibernateException( "The JDBC driver does not implement the method: " + method, e ); + } + catch ( InvocationTargetException e ) { + throw e.getTargetException(); + } + } + + /** + * Generates a SerializableClobProxy proxy wrapping the provided Clob object. + * + * @param clob The Clob to wrap. + * @return The generated proxy. + */ + public static Clob generateProxy(Clob clob) { + return ( Clob ) Proxy.newProxyInstance( + getProxyClassLoader(), + PROXY_INTERFACES, + new SerializableClobProxy( clob ) + ); + } + + /** + * Determines the appropriate class loader to which the generated proxy + * should be scoped. + * + * @return The class loader appropriate for proxy construction. + */ + public static ClassLoader getProxyClassLoader() { + ClassLoader cl = Thread.currentThread().getContextClassLoader(); + if ( cl == null ) { + cl = WrappedClob.class.getClassLoader(); + } + return cl; + } +} diff --git a/core/src/main/java/org/hibernate/engine/jdbc/SerializableNClobProxy.java b/core/src/main/java/org/hibernate/engine/jdbc/SerializableNClobProxy.java new file mode 100644 index 0000000000..3e205055f9 --- /dev/null +++ b/core/src/main/java/org/hibernate/engine/jdbc/SerializableNClobProxy.java @@ -0,0 +1,92 @@ +/* + * 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.Clob; +import java.lang.reflect.Proxy; + +/** + * Manages aspects of proxying java.sql.NClobs to add serializability. + * + * @author Steve Ebersole + */ +public class SerializableNClobProxy extends SerializableClobProxy { + private static final Class NCLOB_CLASS = loadNClobClassIfAvailable(); + + private static Class loadNClobClassIfAvailable() { + try { + return getProxyClassLoader().loadClass( "java.sql.NClob" ); + } + catch ( ClassNotFoundException e ) { + return null; + } + } + + private static final Class[] PROXY_INTERFACES = new Class[] { determineNClobInterface(), WrappedClob.class }; + + private static Class determineNClobInterface() { + // java.sql.NClob is a simple marker interface extending java.sql.Clob. So if java.sql.NClob is not available + // on the classloader, just use java.sql.Clob + return NCLOB_CLASS == null ? Clob.class : NCLOB_CLASS; + } + + public static boolean isNClob(Clob clob) { + return NCLOB_CLASS != null && NCLOB_CLASS.isInstance( clob ); + } + + /** + * Builds a serializable {@link java.sql.Clob} wrapper around the given {@link java.sql.Clob}. + * + * @param clob The {@link java.sql.Clob} to be wrapped. + * + * @see #generateProxy(java.sql.Clob) + */ + protected SerializableNClobProxy(Clob clob) { + super( clob ); + } + + /** + * Generates a SerializableClobProxy proxy wrapping the provided Clob object. + * + * @param clob The Clob to wrap. + * @return The generated proxy. + */ + public static Clob generateProxy(Clob clob) { + return ( Clob ) Proxy.newProxyInstance( + getProxyClassLoader(), + PROXY_INTERFACES, + new SerializableNClobProxy( clob ) + ); + } + + /** + * Determines the appropriate class loader to which the generated proxy + * should be scoped. + * + * @return The class loader appropriate for proxy construction. + */ + public static ClassLoader getProxyClassLoader() { + return SerializableClobProxy.getProxyClassLoader(); + } +} diff --git a/core/src/main/java/org/hibernate/engine/jdbc/StreamUtils.java b/core/src/main/java/org/hibernate/engine/jdbc/StreamUtils.java new file mode 100644 index 0000000000..5a6f5f3123 --- /dev/null +++ b/core/src/main/java/org/hibernate/engine/jdbc/StreamUtils.java @@ -0,0 +1,71 @@ +/* + * 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.io.InputStream; +import java.io.OutputStream; +import java.io.IOException; +import java.io.Reader; +import java.io.Writer; + +/** + * Stream copying utilities + * + * @author Steve Ebersole + */ +public class StreamUtils { + public static final int DEFAULT_CHUNK_SIZE = 1024; + + public static long copy(InputStream inputStream, OutputStream outputStream) throws IOException { + return copy( inputStream, outputStream, DEFAULT_CHUNK_SIZE ); + } + + public static long copy(InputStream inputStream, OutputStream outputStream, int bufferSize) throws IOException { + byte[] buffer = new byte[bufferSize]; + long count = 0; + int n; + while ( -1 != ( n = inputStream.read( buffer ) ) ) { + outputStream.write( buffer, 0, n ); + count += n; + } + return count; + + } + + public static long copy(Reader reader, Writer writer) throws IOException { + return copy( reader, writer, DEFAULT_CHUNK_SIZE ); + } + + public static long copy(Reader reader, Writer writer, int bufferSize) throws IOException { + char[] buffer = new char[bufferSize]; + long count = 0; + int n; + while ( -1 != ( n = reader.read( buffer ) ) ) { + writer.write( buffer, 0, n ); + count += n; + } + return count; + + } +} diff --git a/core/src/main/java/org/hibernate/engine/jdbc/WrappedBlob.java b/core/src/main/java/org/hibernate/engine/jdbc/WrappedBlob.java new file mode 100644 index 0000000000..0885e46697 --- /dev/null +++ b/core/src/main/java/org/hibernate/engine/jdbc/WrappedBlob.java @@ -0,0 +1,40 @@ +/* + * 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.Blob; + +/** + * Contract for {@link Blob} wrappers. + * + * @author Steve Ebersole + */ +public interface WrappedBlob { + /** + * Retrieve the wrapped {@link Blob} reference + * + * @return The wrapped {@link Blob} reference + */ + public Blob getWrappedBlob(); +} diff --git a/core/src/main/java/org/hibernate/engine/jdbc/WrappedClob.java b/core/src/main/java/org/hibernate/engine/jdbc/WrappedClob.java new file mode 100644 index 0000000000..97f621d78b --- /dev/null +++ b/core/src/main/java/org/hibernate/engine/jdbc/WrappedClob.java @@ -0,0 +1,40 @@ +/* + * 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.Clob; + +/** + * Contract for {@link Clob} wrappers. + * + * @author Steve Ebersole + */ +public interface WrappedClob { + /** + * Retrieve the wrapped {@link java.sql.Blob} reference + * + * @return The wrapped {@link java.sql.Blob} reference + */ + public Clob getWrappedClob(); +} diff --git a/core/src/main/java/org/hibernate/impl/SessionImpl.java b/core/src/main/java/org/hibernate/impl/SessionImpl.java index 720465cc63..73bd07b3ef 100644 --- a/core/src/main/java/org/hibernate/impl/SessionImpl.java +++ b/core/src/main/java/org/hibernate/impl/SessionImpl.java @@ -77,6 +77,7 @@ import org.hibernate.engine.QueryParameters; import org.hibernate.engine.StatefulPersistenceContext; import org.hibernate.engine.Status; import org.hibernate.engine.LoadQueryInfluencers; +import org.hibernate.engine.jdbc.LobCreationContext; import org.hibernate.engine.query.FilterQueryPlan; import org.hibernate.engine.query.HQLQueryPlan; import org.hibernate.engine.query.NativeSQLQueryPlan; @@ -140,7 +141,7 @@ import org.hibernate.util.StringHelper; * @author Gavin King */ public final class SessionImpl extends AbstractSessionImpl - implements EventSource, org.hibernate.classic.Session, JDBCContext.Context { + implements EventSource, org.hibernate.classic.Session, JDBCContext.Context, LobCreationContext { // todo : need to find a clean way to handle the "event source" role // a seperate classs responsible for generating/dispatching events just duplicates most of the Session methods... @@ -2001,6 +2002,23 @@ public final class SessionImpl extends AbstractSessionImpl oos.writeObject( childSessionsByEntityMode ); } + public Object execute(Callback callback) { + Connection connection = jdbcContext.getConnectionManager().getConnection(); + try { + return callback.executeOnConnection( connection ); + } + catch ( SQLException e ) { + throw JDBCExceptionHelper.convert( + getFactory().getSQLExceptionConverter(), + e, + "Error creating contextual LOB : " + e.getMessage() + ); + } + finally { + jdbcContext.getConnectionManager().afterStatement(); + } + } + private class CoordinatingEntityNameResolver implements EntityNameResolver { public String resolveEntityName(Object entity) { String entityName = interceptor.getEntityName( entity ); diff --git a/core/src/main/java/org/hibernate/jdbc/ResultSetWrapper.java b/core/src/main/java/org/hibernate/jdbc/ResultSetWrapper.java deleted file mode 100644 index 6b6cae3d9a..0000000000 --- a/core/src/main/java/org/hibernate/jdbc/ResultSetWrapper.java +++ /dev/null @@ -1,642 +0,0 @@ -/* - * Hibernate, Relational Persistence for Idiomatic Java - * - * Copyright (c) 2008, Red Hat Middleware LLC 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 Middleware LLC. - * - * 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.Reader; -import java.math.BigDecimal; -import java.net.URL; -import java.sql.Array; -import java.sql.Blob; -import java.sql.Clob; -import java.sql.Date; -import java.sql.Ref; -import java.sql.ResultSet; -import java.sql.ResultSetMetaData; -import java.sql.SQLException; -import java.sql.SQLWarning; -import java.sql.Statement; -import java.sql.Time; -import java.sql.Timestamp; -import java.util.Calendar; -import java.util.Map; - -/** - * A ResultSet delegate, responsible for locally caching the columnName-to-columnIndex - * resolution that has been found to be inefficient in a few vendor's drivers (i.e., Oracle - * and Postgres). - * - * @author Steve Ebersole - */ -public class ResultSetWrapper implements ResultSet { - - private ResultSet rs; - private ColumnNameCache columnNameCache; - - public ResultSetWrapper(ResultSet resultSet, ColumnNameCache columnNameCache) { - this.rs = resultSet; - this.columnNameCache = columnNameCache; - } - - /*package*/ ResultSet getTarget() { - return rs; - } - - - // ResultSet impl ("overridden") ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - - /** - * Overridden version to utilize local caching of the column indexes by name - * to improve performance for those drivers which are known to not support - * such caching by themselves. - * - * This implementation performs the caching based on the upper case version - * of the given column name. - * - * @param columnName The column name to resolve into an index. - * @return The column index corresponding to the given column name. - * @throws SQLException - if the ResultSet object does not contain - * columnName or a database access error occurs - */ - public int findColumn(String columnName) throws SQLException { - return columnNameCache.getIndexForColumnName( columnName, this ); - } - - public Array getArray(String colName) throws SQLException { - return rs.getArray( findColumn(colName) ); - } - - public void updateArray(String columnName, Array x) throws SQLException { - rs.updateArray( findColumn(columnName), x ); - } - - public InputStream getAsciiStream(String columnName) throws SQLException { - return rs.getAsciiStream( findColumn(columnName) ); - } - - public void updateAsciiStream(String columnName, InputStream x, int length) throws SQLException { - rs.updateAsciiStream( findColumn(columnName), x, length ); - } - - public BigDecimal getBigDecimal(String columnName) throws SQLException { - return rs.getBigDecimal( findColumn(columnName) ); - } - - public BigDecimal getBigDecimal(String columnName, int scale) throws SQLException { - return rs.getBigDecimal( findColumn(columnName), scale ); - } - - public void updateBigDecimal(String columnName, BigDecimal x) throws SQLException { - rs.updateBigDecimal( findColumn(columnName), x ); - } - - public InputStream getBinaryStream(String columnName) throws SQLException { - return rs.getBinaryStream( findColumn(columnName) ); - } - - public void updateBinaryStream(String columnName, InputStream x, int length) throws SQLException { - rs.updateBinaryStream( findColumn(columnName), x, length ); - } - - public Blob getBlob(String columnName) throws SQLException { - return rs.getBlob( findColumn(columnName) ); - } - - public void updateBlob(String columnName, Blob x) throws SQLException { - rs.updateBlob( findColumn(columnName), x ); - } - - public boolean getBoolean(String columnName) throws SQLException { - return rs.getBoolean( findColumn(columnName) ); - } - - public void updateBoolean(String columnName, boolean x) throws SQLException { - rs.updateBoolean( findColumn(columnName), x ); - } - - public byte getByte(String columnName) throws SQLException { - return rs.getByte( findColumn(columnName) ); - } - - public void updateByte(String columnName, byte x) throws SQLException { - rs.updateByte( findColumn(columnName), x ); - } - - public byte[] getBytes(String columnName) throws SQLException { - return rs.getBytes( findColumn(columnName) ); - } - - public void updateBytes(String columnName, byte[] x) throws SQLException { - rs.updateBytes( findColumn(columnName), x ); - } - - public Reader getCharacterStream(String columnName) throws SQLException { - return rs.getCharacterStream( findColumn(columnName) ); - } - - public void updateCharacterStream(String columnName, Reader x, int length) throws SQLException { - rs.updateCharacterStream( findColumn(columnName), x, length ); - } - - public Clob getClob(String columnName) throws SQLException { - return rs.getClob( findColumn(columnName) ); - } - - public void updateClob(String columnName, Clob x) throws SQLException { - rs.updateClob( findColumn(columnName), x ); - } - - public Date getDate(String columnName) throws SQLException { - return rs.getDate( findColumn(columnName) ); - } - - public Date getDate(String columnName, Calendar cal) throws SQLException { - return rs.getDate( findColumn(columnName), cal ); - } - - public void updateDate(String columnName, Date x) throws SQLException { - rs.updateDate( findColumn(columnName), x ); - } - - public double getDouble(String columnName) throws SQLException { - return rs.getDouble( findColumn(columnName) ); - } - - public void updateDouble(String columnName, double x) throws SQLException { - rs.updateDouble( findColumn(columnName), x ); - } - - public float getFloat(String columnName) throws SQLException { - return rs.getFloat( findColumn(columnName) ); - } - - public void updateFloat(String columnName, float x) throws SQLException { - rs.updateFloat( findColumn(columnName), x ); - } - - public int getInt(String columnName) throws SQLException { - return rs.getInt( findColumn(columnName) ); - } - - public void updateInt(String columnName, int x) throws SQLException { - rs.updateInt( findColumn(columnName), x ); - } - - public long getLong(String columnName) throws SQLException { - return rs.getLong( findColumn(columnName) ); - } - - public void updateLong(String columnName, long x) throws SQLException { - rs.updateLong( findColumn(columnName), x ); - } - - public Object getObject(String columnName) throws SQLException { - return rs.getObject( findColumn(columnName) ); - } - - public Object getObject(String columnName, Map map) throws SQLException { - return rs.getObject( findColumn(columnName), map ); - } - - public void updateObject(String columnName, Object x) throws SQLException { - rs.updateObject( findColumn(columnName), x ); - } - - public void updateObject(String columnName, Object x, int scale) throws SQLException { - rs.updateObject( findColumn(columnName), x, scale ); - } - - public Ref getRef(String columnName) throws SQLException { - return rs.getRef( findColumn(columnName) ); - } - - public void updateRef(String columnName, Ref x) throws SQLException { - rs.updateRef( findColumn(columnName), x ); - } - - public short getShort(String columnName) throws SQLException { - return rs.getShort( findColumn(columnName) ); - } - - public void updateShort(String columnName, short x) throws SQLException { - rs.updateShort( findColumn(columnName), x ); - } - - public String getString(String columnName) throws SQLException { - return rs.getString( findColumn(columnName) ); - } - - public void updateString(String columnName, String x) throws SQLException { - rs.updateString( findColumn(columnName), x ); - } - - public Time getTime(String columnName) throws SQLException { - return rs.getTime( findColumn(columnName) ); - } - - public Time getTime(String columnName, Calendar cal) throws SQLException { - return rs.getTime( findColumn(columnName), cal ); - } - - public void updateTime(String columnName, Time x) throws SQLException { - rs.updateTime( findColumn(columnName), x ); - } - - public Timestamp getTimestamp(String columnName) throws SQLException { - return rs.getTimestamp( findColumn(columnName) ); - } - - public void updateTimestamp(String columnName, Timestamp x) throws SQLException { - rs.updateTimestamp( findColumn(columnName), x ); - } - - public Timestamp getTimestamp(String columnName, Calendar cal) throws SQLException { - return rs.getTimestamp( findColumn(columnName), cal ); - } - - public InputStream getUnicodeStream(String columnName) throws SQLException { - return rs.getUnicodeStream( findColumn(columnName) ); - } - - public URL getURL(String columnName) throws SQLException { - return rs.getURL( findColumn(columnName) ); - } - - public void updateNull(String columnName) throws SQLException { - rs.updateNull( findColumn(columnName) ); - } - - - // ResultSet impl (delegated) ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - - public int getConcurrency() throws SQLException { - return rs.getConcurrency(); - } - - public int getFetchDirection() throws SQLException { - return rs.getFetchDirection(); - } - - public int getFetchSize() throws SQLException { - return rs.getFetchSize(); - } - - public int getRow() throws SQLException { - return rs.getRow(); - } - - public int getType() throws SQLException { - return rs.getType(); - } - - public void afterLast() throws SQLException { - rs.afterLast(); - } - - public void beforeFirst() throws SQLException { - rs.beforeFirst(); - } - - public void cancelRowUpdates() throws SQLException { - rs.cancelRowUpdates(); - } - - public void clearWarnings() throws SQLException { - rs.clearWarnings(); - } - - public void close() throws SQLException { - rs.close(); - } - - public void deleteRow() throws SQLException { - rs.deleteRow(); - } - - public void insertRow() throws SQLException { - rs.insertRow(); - } - - public void moveToCurrentRow() throws SQLException { - rs.moveToCurrentRow(); - } - - public void moveToInsertRow() throws SQLException { - rs.moveToInsertRow(); - } - - public void refreshRow() throws SQLException { - rs.refreshRow(); - } - - public void updateRow() throws SQLException { - rs.updateRow(); - } - - public boolean first() throws SQLException { - return rs.first(); - } - - public boolean isAfterLast() throws SQLException { - return rs.isAfterLast(); - } - - public boolean isBeforeFirst() throws SQLException { - return rs.isBeforeFirst(); - } - - public boolean isFirst() throws SQLException { - return rs.isFirst(); - } - - public boolean isLast() throws SQLException { - return rs.isLast(); - } - - public boolean last() throws SQLException { - return rs.last(); - } - - public boolean next() throws SQLException { - return rs.next(); - } - - public boolean previous() throws SQLException { - return rs.previous(); - } - - public boolean rowDeleted() throws SQLException { - return rs.rowDeleted(); - } - - public boolean rowInserted() throws SQLException { - return rs.rowInserted(); - } - - public boolean rowUpdated() throws SQLException { - return rs.rowUpdated(); - } - - public boolean wasNull() throws SQLException { - return rs.wasNull(); - } - - public byte getByte(int columnIndex) throws SQLException { - return rs.getByte(columnIndex); - } - - public double getDouble(int columnIndex) throws SQLException { - return rs.getDouble(columnIndex); - } - - public float getFloat(int columnIndex) throws SQLException { - return rs.getFloat(columnIndex); - } - - public int getInt(int columnIndex) throws SQLException { - return rs.getInt(columnIndex); - } - - public long getLong(int columnIndex) throws SQLException { - return rs.getLong(columnIndex); - } - - public short getShort(int columnIndex) throws SQLException { - return rs.getShort(columnIndex); - } - - public void setFetchDirection(int direction) throws SQLException { - rs.setFetchDirection(direction); - } - - public void setFetchSize(int rows) throws SQLException { - rs.setFetchSize(rows); - } - - public void updateNull(int columnIndex) throws SQLException { - rs.updateNull(columnIndex); - } - - public boolean absolute(int row) throws SQLException { - return rs.absolute(row); - } - - public boolean getBoolean(int columnIndex) throws SQLException { - return rs.getBoolean(columnIndex); - } - - public boolean relative(int rows) throws SQLException { - return rs.relative(rows); - } - - public byte[] getBytes(int columnIndex) throws SQLException { - return rs.getBytes(columnIndex); - } - - public void updateByte(int columnIndex, byte x) throws SQLException { - rs.updateByte(columnIndex, x); - } - - public void updateDouble(int columnIndex, double x) throws SQLException { - rs.updateDouble(columnIndex, x); - } - - public void updateFloat(int columnIndex, float x) throws SQLException { - rs.updateFloat(columnIndex, x); - } - - public void updateInt(int columnIndex, int x) throws SQLException { - rs.updateInt(columnIndex, x); - } - - public void updateLong(int columnIndex, long x) throws SQLException { - rs.updateLong(columnIndex, x); - } - - public void updateShort(int columnIndex, short x) throws SQLException { - rs.updateShort(columnIndex, x); - } - - public void updateBoolean(int columnIndex, boolean x) throws SQLException { - rs.updateBoolean(columnIndex, x); - } - - public void updateBytes(int columnIndex, byte[] x) throws SQLException { - rs.updateBytes(columnIndex, x); - } - - public InputStream getAsciiStream(int columnIndex) throws SQLException { - return rs.getAsciiStream(columnIndex); - } - - public InputStream getBinaryStream(int columnIndex) throws SQLException { - return rs.getBinaryStream(columnIndex); - } - - public InputStream getUnicodeStream(int columnIndex) throws SQLException { - return rs.getUnicodeStream(columnIndex); - } - - public void updateAsciiStream(int columnIndex, InputStream x, int length) throws SQLException { - rs.updateAsciiStream(columnIndex, x, length); - } - - public void updateBinaryStream(int columnIndex, InputStream x, int length) throws SQLException { - rs.updateBinaryStream(columnIndex, x, length); - } - - public Reader getCharacterStream(int columnIndex) throws SQLException { - return rs.getCharacterStream(columnIndex); - } - - public void updateCharacterStream(int columnIndex, Reader x, int length) throws SQLException { - rs.updateCharacterStream(columnIndex, x, length); - } - - public Object getObject(int columnIndex) throws SQLException { - return rs.getObject(columnIndex); - } - - public void updateObject(int columnIndex, Object x) throws SQLException { - rs.updateObject(columnIndex, x); - } - - public void updateObject(int columnIndex, Object x, int scale) throws SQLException { - rs.updateObject(columnIndex, x, scale); - } - - public String getCursorName() throws SQLException { - return rs.getCursorName(); - } - - public String getString(int columnIndex) throws SQLException { - return rs.getString(columnIndex); - } - - public void updateString(int columnIndex, String x) throws SQLException { - rs.updateString(columnIndex, x); - } - - public BigDecimal getBigDecimal(int columnIndex) throws SQLException { - return rs.getBigDecimal(columnIndex); - } - - public BigDecimal getBigDecimal(int columnIndex, int scale) throws SQLException { - return rs.getBigDecimal(columnIndex, scale); - } - - public void updateBigDecimal(int columnIndex, BigDecimal x) throws SQLException { - rs.updateBigDecimal(columnIndex, x); - } - - public URL getURL(int columnIndex) throws SQLException { - return rs.getURL(columnIndex); - } - - public Array getArray(int columnIndex) throws SQLException { - return rs.getArray(columnIndex); - } - - public void updateArray(int columnIndex, Array x) throws SQLException { - rs.updateArray(columnIndex, x); - } - - public Blob getBlob(int columnIndex) throws SQLException { - return rs.getBlob(columnIndex); - } - - public void updateBlob(int columnIndex, Blob x) throws SQLException { - rs.updateBlob(columnIndex, x); - } - - public Clob getClob(int columnIndex) throws SQLException { - return rs.getClob(columnIndex); - } - - public void updateClob(int columnIndex, Clob x) throws SQLException { - rs.updateClob(columnIndex, x); - } - - public Date getDate(int columnIndex) throws SQLException { - return rs.getDate(columnIndex); - } - - public void updateDate(int columnIndex, Date x) throws SQLException { - rs.updateDate(columnIndex, x); - } - - public Ref getRef(int columnIndex) throws SQLException { - return rs.getRef(columnIndex); - } - - public void updateRef(int columnIndex, Ref x) throws SQLException { - rs.updateRef(columnIndex, x); - } - - public ResultSetMetaData getMetaData() throws SQLException { - return rs.getMetaData(); - } - - public SQLWarning getWarnings() throws SQLException { - return rs.getWarnings(); - } - - public Statement getStatement() throws SQLException { - return rs.getStatement(); - } - - public Time getTime(int columnIndex) throws SQLException { - return rs.getTime(columnIndex); - } - - public void updateTime(int columnIndex, Time x) throws SQLException { - rs.updateTime(columnIndex, x); - } - - public Timestamp getTimestamp(int columnIndex) throws SQLException { - return rs.getTimestamp(columnIndex); - } - - public void updateTimestamp(int columnIndex, Timestamp x) throws SQLException { - rs.updateTimestamp(columnIndex, x); - } - - public Object getObject(int columnIndex, Map map) throws SQLException { - return rs.getObject( columnIndex, map ); - } - - public Date getDate(int columnIndex, Calendar cal) throws SQLException { - return rs.getDate(columnIndex, cal); - } - - public Time getTime(int columnIndex, Calendar cal) throws SQLException { - return rs.getTime(columnIndex, cal); - } - - public Timestamp getTimestamp(int columnIndex, Calendar cal) throws SQLException { - return rs.getTimestamp(columnIndex, cal); - } -} - diff --git a/core/src/main/java/org/hibernate/loader/Loader.java b/core/src/main/java/org/hibernate/loader/Loader.java index 6166b2a4ef..09a7f8a00d 100644 --- a/core/src/main/java/org/hibernate/loader/Loader.java +++ b/core/src/main/java/org/hibernate/loader/Loader.java @@ -1,10 +1,10 @@ /* * Hibernate, Relational Persistence for Idiomatic Java * - * Copyright (c) 2008, Red Hat Middleware LLC 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 Middleware LLC. + * 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 @@ -20,7 +20,6 @@ * Free Software Foundation, Inc. * 51 Franklin Street, Fifth Floor * Boston, MA 02110-1301 USA - * */ package org.hibernate.loader; @@ -63,6 +62,7 @@ import org.hibernate.engine.SessionImplementor; import org.hibernate.engine.SubselectFetch; import org.hibernate.engine.TwoPhaseLoad; import org.hibernate.engine.TypedValue; +import org.hibernate.engine.jdbc.ColumnNameCache; import org.hibernate.event.EventSource; import org.hibernate.event.PostLoadEvent; import org.hibernate.event.PreLoadEvent; @@ -70,8 +70,6 @@ import org.hibernate.exception.JDBCExceptionHelper; import org.hibernate.hql.HolderInstantiator; import org.hibernate.impl.FetchingScrollableResultsImpl; import org.hibernate.impl.ScrollableResultsImpl; -import org.hibernate.jdbc.ColumnNameCache; -import org.hibernate.jdbc.ResultSetWrapper; import org.hibernate.persister.collection.CollectionPersister; import org.hibernate.persister.entity.EntityPersister; import org.hibernate.persister.entity.Loadable; @@ -1839,7 +1837,9 @@ public abstract class Loader { if ( session.getFactory().getSettings().isWrapResultSetsEnabled() ) { try { log.debug("Wrapping result set [" + rs + "]"); - return new ResultSetWrapper( rs, retreiveColumnNameToIndexCache( rs ) ); + return session.getFactory() + .getSettings() + .getJdbcSupport().wrap( rs, retreiveColumnNameToIndexCache( rs ) ); } catch(SQLException e) { log.info("Error wrapping result set", e); diff --git a/core/src/main/java/org/hibernate/lob/BlobImpl.java b/core/src/main/java/org/hibernate/lob/BlobImpl.java deleted file mode 100644 index 7e6c6f96d0..0000000000 --- a/core/src/main/java/org/hibernate/lob/BlobImpl.java +++ /dev/null @@ -1,136 +0,0 @@ -/* - * Hibernate, Relational Persistence for Idiomatic Java - * - * Copyright (c) 2008, Red Hat Middleware LLC 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 Middleware LLC. - * - * 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.lob; - -import java.io.ByteArrayInputStream; -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.sql.Blob; -import java.sql.SQLException; - -/** - * A dummy implementation of java.sql.Blob that - * may be used to insert new data into a BLOB. - * @author Gavin King - */ -public class BlobImpl implements Blob { - - private InputStream stream; - private int length; - private boolean needsReset = false; - - public BlobImpl(byte[] bytes) { - this.stream = new ByteArrayInputStream(bytes); - this.length = bytes.length; - } - - public BlobImpl(InputStream stream, int length) { - this.stream = stream; - this.length = length; - } - - /** - * @see java.sql.Blob#length() - */ - public long length() throws SQLException { - return length; - } - - /** - * @see java.sql.Blob#truncate(long) - */ - public void truncate(long pos) throws SQLException { - excep(); - } - - /** - * @see java.sql.Blob#getBytes(long, int) - */ - public byte[] getBytes(long pos, int len) throws SQLException { - excep(); return null; - } - - /** - * @see java.sql.Blob#setBytes(long, byte[]) - */ - public int setBytes(long pos, byte[] bytes) throws SQLException { - excep(); return 0; - } - - /** - * @see java.sql.Blob#setBytes(long, byte[], int, int) - */ - public int setBytes(long pos, byte[] bytes, int i, int j) - throws SQLException { - excep(); return 0; - } - - /** - * @see java.sql.Blob#position(byte[], long) - */ - public long position(byte[] bytes, long pos) throws SQLException { - excep(); return 0; - } - - /** - * @see java.sql.Blob#getBinaryStream() - */ - public InputStream getBinaryStream() throws SQLException { - try { - if (needsReset) stream.reset(); - } - catch (IOException ioe) { - throw new SQLException("could not reset reader"); - } - needsReset = true; - return stream; - } - - /** - * @see java.sql.Blob#setBinaryStream(long) - */ - public OutputStream setBinaryStream(long pos) throws SQLException { - excep(); return null; - } - - /** - * @see java.sql.Blob#position(Blob, long) - */ - public long position(Blob blob, long pos) throws SQLException { - excep(); return 0; - } - - private static void excep() { - throw new UnsupportedOperationException("Blob may not be manipulated from creating session"); - } - -} - - - - - - diff --git a/core/src/main/java/org/hibernate/lob/ClobImpl.java b/core/src/main/java/org/hibernate/lob/ClobImpl.java deleted file mode 100644 index d49aee11b9..0000000000 --- a/core/src/main/java/org/hibernate/lob/ClobImpl.java +++ /dev/null @@ -1,160 +0,0 @@ -/* - * Hibernate, Relational Persistence for Idiomatic Java - * - * Copyright (c) 2008, Red Hat Middleware LLC 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 Middleware LLC. - * - * 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.lob; - -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.io.Reader; -import java.io.StringReader; -import java.io.Writer; -import java.sql.Clob; -import java.sql.SQLException; - -/** - * A dummy implementation of java.sql.Clob that - * may be used to insert new data into a CLOB. - * @author Gavin King - */ -public class ClobImpl implements Clob { - - private Reader reader; - private int length; - private boolean needsReset = false; - - public ClobImpl(String string) { - reader = new StringReader(string); - length = string.length(); - } - - public ClobImpl(Reader reader, int length) { - this.reader = reader; - this.length = length; - } - - /** - * @see java.sql.Clob#length() - */ - public long length() throws SQLException { - return length; - } - - /** - * @see java.sql.Clob#truncate(long) - */ - public void truncate(long pos) throws SQLException { - excep(); - } - - /** - * @see java.sql.Clob#getAsciiStream() - */ - public InputStream getAsciiStream() throws SQLException { - try { - if (needsReset) reader.reset(); - } - catch (IOException ioe) { - throw new SQLException("could not reset reader"); - } - needsReset = true; - return new ReaderInputStream(reader); - } - - /** - * @see java.sql.Clob#setAsciiStream(long) - */ - public OutputStream setAsciiStream(long pos) throws SQLException { - excep(); return null; - } - - /** - * @see java.sql.Clob#getCharacterStream() - */ - public Reader getCharacterStream() throws SQLException { - try { - if (needsReset) reader.reset(); - } - catch (IOException ioe) { - throw new SQLException("could not reset reader"); - } - needsReset = true; - return reader; - } - - /** - * @see java.sql.Clob#setCharacterStream(long) - */ - public Writer setCharacterStream(long pos) throws SQLException { - excep(); return null; - } - - /** - * @see java.sql.Clob#getSubString(long, int) - */ - public String getSubString(long pos, int len) throws SQLException { - excep(); return null; - } - - /** - * @see java.sql.Clob#setString(long, String) - */ - public int setString(long pos, String string) throws SQLException { - excep(); return 0; - } - - /** - * @see java.sql.Clob#setString(long, String, int, int) - */ - public int setString(long pos, String string, int i, int j) - throws SQLException { - excep(); return 0; - } - - /** - * @see java.sql.Clob#position(String, long) - */ - public long position(String string, long pos) throws SQLException { - excep(); return 0; - } - - /** - * @see java.sql.Clob#position(Clob, long) - */ - public long position(Clob colb, long pos) throws SQLException { - excep(); return 0; - } - - - private static void excep() { - throw new UnsupportedOperationException("Blob may not be manipulated from creating session"); - } - -} - - - - - - diff --git a/core/src/main/java/org/hibernate/lob/SerializableBlob.java b/core/src/main/java/org/hibernate/lob/SerializableBlob.java deleted file mode 100755 index b3cb3b502e..0000000000 --- a/core/src/main/java/org/hibernate/lob/SerializableBlob.java +++ /dev/null @@ -1,89 +0,0 @@ -/* - * Hibernate, Relational Persistence for Idiomatic Java - * - * Copyright (c) 2008, Red Hat Middleware LLC 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 Middleware LLC. - * - * 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.lob; - -import java.io.InputStream; -import java.io.OutputStream; -import java.io.Serializable; -import java.sql.Blob; -import java.sql.SQLException; - -/** - * @author Gavin King - */ -public class SerializableBlob implements Serializable, Blob { - - private transient final Blob blob; - - public SerializableBlob(Blob blob) { - this.blob = blob; - } - - public Blob getWrappedBlob() { - if ( blob==null ) { - throw new IllegalStateException("Blobs may not be accessed after serialization"); - } - else { - return blob; - } - } - - public long length() throws SQLException { - return getWrappedBlob().length(); - } - - public byte[] getBytes(long pos, int length) throws SQLException { - return getWrappedBlob().getBytes(pos, length); - } - - public InputStream getBinaryStream() throws SQLException { - return getWrappedBlob().getBinaryStream(); - } - - public long position(byte[] pattern, long start) throws SQLException { - return getWrappedBlob().position(pattern, start); - } - - public long position(Blob pattern, long start) throws SQLException { - return getWrappedBlob().position(pattern, start); - } - - public int setBytes(long pos, byte[] bytes) throws SQLException { - return getWrappedBlob().setBytes(pos, bytes); - } - - public int setBytes(long pos, byte[] bytes, int offset, int len) throws SQLException { - return getWrappedBlob().setBytes(pos, bytes, offset, len); - } - - public OutputStream setBinaryStream(long pos) throws SQLException { - return getWrappedBlob().setBinaryStream(pos); - } - - public void truncate(long len) throws SQLException { - getWrappedBlob().truncate(len); - } - -} diff --git a/core/src/main/java/org/hibernate/lob/SerializableClob.java b/core/src/main/java/org/hibernate/lob/SerializableClob.java deleted file mode 100755 index d3c973deae..0000000000 --- a/core/src/main/java/org/hibernate/lob/SerializableClob.java +++ /dev/null @@ -1,99 +0,0 @@ -/* - * Hibernate, Relational Persistence for Idiomatic Java - * - * Copyright (c) 2008, Red Hat Middleware LLC 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 Middleware LLC. - * - * 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.lob; - -import java.io.InputStream; -import java.io.OutputStream; -import java.io.Reader; -import java.io.Serializable; -import java.io.Writer; -import java.sql.Clob; -import java.sql.SQLException; - -/** - * @author Gavin King - */ -public class SerializableClob implements Serializable, Clob { - - private transient final Clob clob; - - public SerializableClob(Clob blob) { - this.clob = blob; - } - - public Clob getWrappedClob() { - if ( clob==null ) { - throw new IllegalStateException("Clobs may not be accessed after serialization"); - } - else { - return clob; - } - } - - public long length() throws SQLException { - return getWrappedClob().length(); - } - - public String getSubString(long pos, int length) throws SQLException { - return getWrappedClob().getSubString(pos, length); - } - - public Reader getCharacterStream() throws SQLException { - return getWrappedClob().getCharacterStream(); - } - - public InputStream getAsciiStream() throws SQLException { - return getWrappedClob().getAsciiStream(); - } - - public long position(String searchstr, long start) throws SQLException { - return getWrappedClob().position(searchstr, start); - } - - public long position(Clob searchstr, long start) throws SQLException { - return getWrappedClob().position(searchstr, start); - } - - public int setString(long pos, String str) throws SQLException { - return getWrappedClob().setString(pos, str); - } - - public int setString(long pos, String str, int offset, int len) throws SQLException { - return getWrappedClob().setString(pos, str, offset, len); - } - - public OutputStream setAsciiStream(long pos) throws SQLException { - return getWrappedClob().setAsciiStream(pos); - } - - public Writer setCharacterStream(long pos) throws SQLException { - return getWrappedClob().setCharacterStream(pos); - } - - public void truncate(long len) throws SQLException { - getWrappedClob().truncate(len); - } - -} diff --git a/core/src/main/java/org/hibernate/lob/package.html b/core/src/main/java/org/hibernate/lob/package.html deleted file mode 100755 index 2de22aeb3c..0000000000 --- a/core/src/main/java/org/hibernate/lob/package.html +++ /dev/null @@ -1,34 +0,0 @@ - - - - - -- This package defines dummy and wrapper implementations - of java.sql.Clob and java.sql.Blob. -
- - diff --git a/core/src/main/java/org/hibernate/type/BlobType.java b/core/src/main/java/org/hibernate/type/BlobType.java index 2442365ca6..761105e49c 100644 --- a/core/src/main/java/org/hibernate/type/BlobType.java +++ b/core/src/main/java/org/hibernate/type/BlobType.java @@ -36,11 +36,14 @@ import org.dom4j.Node; import org.hibernate.EntityMode; import org.hibernate.HibernateException; import org.hibernate.MappingException; +import org.hibernate.Hibernate; import org.hibernate.engine.Mapping; import org.hibernate.engine.SessionFactoryImplementor; import org.hibernate.engine.SessionImplementor; -import org.hibernate.lob.BlobImpl; -import org.hibernate.lob.SerializableBlob; +import org.hibernate.engine.jdbc.BlobImplementer; +import org.hibernate.engine.jdbc.WrappedBlob; +import org.hibernate.engine.jdbc.LobCreator; +import org.hibernate.engine.jdbc.NonContextualLobCreator; import org.hibernate.util.ArrayHelper; /** @@ -48,135 +51,223 @@ import org.hibernate.util.ArrayHelper; * @author Gavin King */ public class BlobType extends AbstractType { + /** + * {@inheritDoc} + */ + public void nullSafeSet( + PreparedStatement st, + Object value, + int index, + boolean[] settable, + SessionImplementor session) throws HibernateException, SQLException { + if ( settable[0] ) { + set( st, value, index, session ); + } + } - public void set(PreparedStatement st, Object value, int index, SessionImplementor session) - throws HibernateException, SQLException { - - if (value==null) { - st.setNull(index, Types.BLOB); + /** + * {@inheritDoc} + */ + public void nullSafeSet( + PreparedStatement st, + Object value, + int index, + SessionImplementor session) throws HibernateException, SQLException { + set( st, value, index, session ); + } + + public void set( + PreparedStatement st, + Object value, + int index, + SessionImplementor session) throws HibernateException, SQLException { + if ( value == null ) { + st.setNull( index, Types.BLOB ); + return; + } + + Blob blob = ( Blob ) value; + + if ( WrappedBlob.class.isInstance( blob ) ) { + blob = ( (WrappedBlob) value ).getWrappedBlob(); + } + + final boolean useInputStream = session.getFactory().getDialect().useInputStreamToInsertBlob() + && BlobImplementer.class.isInstance( blob ); + + if ( useInputStream ) { + st.setBinaryStream( index, blob.getBinaryStream(), (int) blob.length() ); } else { - - if (value instanceof SerializableBlob) { - value = ( (SerializableBlob) value ).getWrappedBlob(); - } - - final boolean useInputStream = session.getFactory().getDialect().useInputStreamToInsertBlob() && - (value instanceof BlobImpl); - - if ( useInputStream ) { - BlobImpl blob = (BlobImpl) value; - st.setBinaryStream( index, blob.getBinaryStream(), (int) blob.length() ); - } - else { - st.setBlob(index, (Blob) value); - } - + st.setBlob( index, blob ); } - } - public Object get(ResultSet rs, String name) throws HibernateException, SQLException { - Blob value = rs.getBlob(name); - return rs.wasNull() ? null : new SerializableBlob(value); + /** + * {@inheritDoc} + */ + public Object nullSafeGet( + ResultSet rs, + String name, + SessionImplementor session, + Object owner) throws HibernateException, SQLException { + return get( rs, name, Hibernate.getLobCreator( session ) ); } + /** + * {@inheritDoc} + */ + public Object nullSafeGet( + ResultSet rs, + String[] names, + SessionImplementor session, + Object owner) throws HibernateException, SQLException { + return get( rs, names[0], Hibernate.getLobCreator( session ) ); + } + + /** + * A method to extract the BLOB value from a result set. + * + * @param rs The result set + * @param name The name of the column containing the BLOB + * + * @return The BLOB + * + * @throws SQLException Indicates a problem accessing the result set + * + * @deprecated Use {@link #get(ResultSet,String,LobCreator)} instead + */ + public Object get(ResultSet rs, String name) throws SQLException { + return get( rs, name, NonContextualLobCreator.INSTANCE ); + } + + public Object get(ResultSet rs, String name, LobCreator lobCreator) throws SQLException { + Blob value = rs.getBlob( name ); + return rs.wasNull() ? null : lobCreator.wrap( value ); + } + + /** + * {@inheritDoc} + */ public Class getReturnedClass() { return Blob.class; } + /** + * {@inheritDoc} + */ public boolean isEqual(Object x, Object y, EntityMode entityMode) { return x == y; } + /** + * {@inheritDoc} + */ public int getHashCode(Object x, EntityMode entityMode) { return System.identityHashCode(x); } + /** + * {@inheritDoc} + */ public int compare(Object x, Object y, EntityMode entityMode) { return 0; //lobs cannot be compared } + /** + * {@inheritDoc} + */ public String getName() { return "blob"; } - + + /** + * {@inheritDoc} + */ public Serializable disassemble(Object value, SessionImplementor session, Object owner) - throws HibernateException { + throws UnsupportedOperationException { throw new UnsupportedOperationException("Blobs are not cacheable"); } + /** + * {@inheritDoc} + */ public Object deepCopy(Object value, EntityMode entityMode, SessionFactoryImplementor factory) { return value; } + /** + * {@inheritDoc} + */ public Object fromXMLNode(Node xml, Mapping factory) { throw new UnsupportedOperationException("todo"); } + /** + * {@inheritDoc} + */ public int getColumnSpan(Mapping mapping) { return 1; } + /** + * {@inheritDoc} + */ public boolean isMutable() { return false; } - public Object nullSafeGet(ResultSet rs, String name, - SessionImplementor session, Object owner) - throws HibernateException, SQLException { - return get(rs, name); - } - - public Object nullSafeGet(ResultSet rs, String[] names, - SessionImplementor session, Object owner) - throws HibernateException, SQLException { - return get( rs, names[0] ); - } - - public void nullSafeSet(PreparedStatement st, Object value, int index, - boolean[] settable, SessionImplementor session) - throws HibernateException, SQLException { - if ( settable[0] ) set(st, value, index, session); - } - - public void nullSafeSet(PreparedStatement st, Object value, int index, - SessionImplementor session) throws HibernateException, SQLException { - set(st, value, index, session); - } - - public Object replace(Object original, Object target, - SessionImplementor session, Object owner, Map copyCache) - throws HibernateException { + /** + * {@inheritDoc} + */ + public Object replace( + Object original, + Object target, + SessionImplementor session, + Object owner, + Map copyCache) throws HibernateException { //Blobs are ignored by merge() return target; } - + + /** + * {@inheritDoc} + */ public int[] sqlTypes(Mapping mapping) throws MappingException { return new int[] { Types.BLOB }; } + /** + * {@inheritDoc} + */ public void setToXMLNode(Node node, Object value, SessionFactoryImplementor factory) { throw new UnsupportedOperationException("todo"); } - public String toLoggableString(Object value, SessionFactoryImplementor factory) - throws HibernateException { + /** + * {@inheritDoc} + */ + public String toLoggableString(Object value, SessionFactoryImplementor factory) throws HibernateException { return value==null ? "null" : value.toString(); } - + + /** + * {@inheritDoc} + */ public boolean[] toColumnNullness(Object value, Mapping mapping) { return value==null ? ArrayHelper.FALSE : ArrayHelper.TRUE; } - public boolean isDirty(Object old, Object current, boolean[] checkable, SessionImplementor session) throws HibernateException { + /** + * {@inheritDoc} + */ + public boolean isDirty( + Object old, + Object current, + boolean[] checkable, + SessionImplementor session) throws HibernateException { return checkable[0] && isDirty(old, current, session); } } - - - - - diff --git a/core/src/main/java/org/hibernate/type/ClobType.java b/core/src/main/java/org/hibernate/type/ClobType.java index f4f07934d2..95abb38dce 100644 --- a/core/src/main/java/org/hibernate/type/ClobType.java +++ b/core/src/main/java/org/hibernate/type/ClobType.java @@ -40,8 +40,10 @@ import org.hibernate.MappingException; import org.hibernate.engine.Mapping; import org.hibernate.engine.SessionFactoryImplementor; import org.hibernate.engine.SessionImplementor; -import org.hibernate.lob.ClobImpl; -import org.hibernate.lob.SerializableClob; +import org.hibernate.engine.jdbc.LobCreator; +import org.hibernate.engine.jdbc.NonContextualLobCreator; +import org.hibernate.engine.jdbc.WrappedClob; +import org.hibernate.engine.jdbc.ClobImplementer; import org.hibernate.util.ArrayHelper; /** @@ -50,36 +52,87 @@ import org.hibernate.util.ArrayHelper; */ public class ClobType extends AbstractType { - public void set(PreparedStatement st, Object value, int index, SessionImplementor session) - throws HibernateException, SQLException { - - if (value==null) { - st.setNull(index, Types.CLOB); + public void nullSafeSet( + PreparedStatement st, + Object value, + int index, + boolean[] settable, + SessionImplementor session) throws SQLException { + if ( settable[0] ) { + set( st, value, index, session ); } - else { - - if (value instanceof SerializableClob) { - value = ( (SerializableClob) value ).getWrappedClob(); - } - - final boolean useReader = session.getFactory().getDialect().useInputStreamToInsertBlob() && - (value instanceof ClobImpl); - - if ( useReader ) { - ClobImpl clob = (ClobImpl) value; - st.setCharacterStream( index, clob.getCharacterStream(), (int) clob.length() ); - } - else { - st.setClob(index, (Clob) value); - } - - } - } - public Object get(ResultSet rs, String name) throws HibernateException, SQLException { - Clob value = rs.getClob(name); - return rs.wasNull() ? null : new SerializableClob(value); + public void nullSafeSet( + PreparedStatement st, + Object value, + int index, + SessionImplementor session) throws SQLException { + set( st, value, index, session ); + } + + public void set( + PreparedStatement st, + Object value, + int index, + SessionImplementor session) throws SQLException { + if ( value == null ) { + st.setNull( index, Types.CLOB ); + return; + } + + Clob clob = ( Clob ) value; + + if ( WrappedClob.class.isInstance( clob ) ) { + clob = ( (WrappedClob) value ).getWrappedClob(); + } + + final boolean useInputStream = session.getFactory().getDialect().useInputStreamToInsertBlob() + && ClobImplementer.class.isInstance( clob ); + + if ( useInputStream ) { + st.setCharacterStream( index, clob.getCharacterStream(), (int) clob.length() ); + } + else { + st.setClob( index, clob ); + } + } + + public Object nullSafeGet( + ResultSet rs, + String name, + SessionImplementor session, + Object owner) throws SQLException { + return get( rs, name, Hibernate.getLobCreator( session ) ); + } + + public Object nullSafeGet( + ResultSet rs, + String[] names, + SessionImplementor session, + Object owner) throws SQLException { + return get( rs, names[0], Hibernate.getLobCreator( session ) ); + } + + /** + * A method to extract the CLOB value from a result set. + * + * @param rs The result set + * @param name The name of the column containing the CLOB + * + * @return The CLOB + * + * @throws SQLException Indicates a problem accessing the result set + * + * @deprecated Use {@link #get(ResultSet,String,LobCreator)} instead + */ + public Object get(ResultSet rs, String name) throws SQLException { + return get( rs, name, NonContextualLobCreator.INSTANCE ); + } + + public Clob get(ResultSet rs, String name, LobCreator lobCreator) throws SQLException { + Clob value = rs.getClob( name ); + return rs.wasNull() ? null : lobCreator.wrap( value ); } public Class getReturnedClass() { @@ -123,29 +176,6 @@ public class ClobType extends AbstractType { return false; } - public Object nullSafeGet(ResultSet rs, String name, - SessionImplementor session, Object owner) - throws HibernateException, SQLException { - return get(rs, name); - } - - public Object nullSafeGet(ResultSet rs, String[] names, - SessionImplementor session, Object owner) - throws HibernateException, SQLException { - return get( rs, names[0] ); - } - - public void nullSafeSet(PreparedStatement st, Object value, int index, - boolean[] settable, SessionImplementor session) - throws HibernateException, SQLException { - if ( settable[0] ) set(st, value, index, session); - } - - public void nullSafeSet(PreparedStatement st, Object value, int index, - SessionImplementor session) throws HibernateException, SQLException { - set(st, value, index, session); - } - public Object replace(Object original, Object target, SessionImplementor session, Object owner, Map copyCache) throws HibernateException { diff --git a/jdbc3-testing/pom.xml b/jdbc3-testing/pom.xml new file mode 100644 index 0000000000..81e506aafc --- /dev/null +++ b/jdbc3-testing/pom.xml @@ -0,0 +1,61 @@ + +