HHH-7698 - In efficient LOB creations backed by streams

This commit is contained in:
Steve Ebersole 2012-10-19 17:28:46 -04:00 committed by brmeyer
parent e5d3b2b34c
commit d118c24776
31 changed files with 259 additions and 172 deletions

View File

@ -31,16 +31,12 @@ import java.sql.Clob;
* @author Steve Ebersole * @author Steve Ebersole
*/ */
public abstract class AbstractLobCreator implements LobCreator { public abstract class AbstractLobCreator implements LobCreator {
/** @Override
* {@inheritDoc}
*/
public Blob wrap(Blob blob) { public Blob wrap(Blob blob) {
return SerializableBlobProxy.generateProxy( blob ); return SerializableBlobProxy.generateProxy( blob );
} }
/** @Override
* {@inheritDoc}
*/
public Clob wrap(Clob clob) { public Clob wrap(Clob clob) {
if ( SerializableNClobProxy.isNClob( clob ) ) { if ( SerializableNClobProxy.isNClob( clob ) ) {
return SerializableNClobProxy.generateProxy( clob ); return SerializableNClobProxy.generateProxy( clob );

View File

@ -21,7 +21,8 @@
* 51 Franklin Street, Fifth Floor * 51 Franklin Street, Fifth Floor
* Boston, MA 02110-1301 USA * Boston, MA 02110-1301 USA
*/ */
package org.hibernate.type.descriptor; package org.hibernate.engine.jdbc;
import java.io.InputStream; import java.io.InputStream;
/** /**
@ -49,5 +50,10 @@ public interface BinaryStream {
* *
* @return The input stream length * @return The input stream length
*/ */
public int getLength(); public long getLength();
/**
* Release any underlying resources.
*/
public void release();
} }

View File

@ -23,11 +23,16 @@
*/ */
package org.hibernate.engine.jdbc; package org.hibernate.engine.jdbc;
/** /**
* Marker interface for non-contextually created {@link java.sql.Blob} instances.. * Marker interface for non-contextually created {@link java.sql.Blob} instances..
* *
* @author Steve Ebersole * @author Steve Ebersole
*/ */
public interface BlobImplementer { public interface BlobImplementer {
/**
* Gets access to the data underlying this BLOB.
*
* @return Access to the underlying data.
*/
public BinaryStream getUnderlyingStream();
} }

View File

@ -22,6 +22,7 @@
* Boston, MA 02110-1301 USA * Boston, MA 02110-1301 USA
*/ */
package org.hibernate.engine.jdbc; package org.hibernate.engine.jdbc;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.lang.reflect.InvocationHandler; import java.lang.reflect.InvocationHandler;
@ -30,12 +31,12 @@ import java.lang.reflect.Proxy;
import java.sql.Blob; import java.sql.Blob;
import java.sql.SQLException; import java.sql.SQLException;
import org.hibernate.type.descriptor.java.BinaryStreamImpl; import org.hibernate.engine.jdbc.internal.BinaryStreamImpl;
import org.hibernate.type.descriptor.java.DataHelper; import org.hibernate.type.descriptor.java.DataHelper;
/** /**
* Manages aspects of proxying {@link Blob Blobs} for non-contextual creation, including proxy creation and * Manages aspects of proxying {@link Blob} references for non-contextual creation, including proxy creation and
* handling proxy invocations. * handling proxy invocations. We use proxies here solely to avoid JDBC version incompatibilities.
* *
* @author Gavin King * @author Gavin King
* @author Steve Ebersole * @author Steve Ebersole
@ -44,8 +45,7 @@ import org.hibernate.type.descriptor.java.DataHelper;
public class BlobProxy implements InvocationHandler { public class BlobProxy implements InvocationHandler {
private static final Class[] PROXY_INTERFACES = new Class[] { Blob.class, BlobImplementer.class }; private static final Class[] PROXY_INTERFACES = new Class[] { Blob.class, BlobImplementer.class };
private InputStream stream; private BinaryStream binaryStream;
private long length;
private boolean needsReset = false; private boolean needsReset = false;
/** /**
@ -55,8 +55,7 @@ public class BlobProxy implements InvocationHandler {
* @see #generateProxy(byte[]) * @see #generateProxy(byte[])
*/ */
private BlobProxy(byte[] bytes) { private BlobProxy(byte[] bytes) {
this.stream = new BinaryStreamImpl( bytes ); binaryStream = new BinaryStreamImpl( bytes );
this.length = bytes.length;
} }
/** /**
@ -67,17 +66,17 @@ public class BlobProxy implements InvocationHandler {
* @see #generateProxy(java.io.InputStream, long) * @see #generateProxy(java.io.InputStream, long)
*/ */
private BlobProxy(InputStream stream, long length) { private BlobProxy(InputStream stream, long length) {
this.stream = stream; this.binaryStream = new StreamBackedBinaryStream( stream, length );
this.length = length;
} }
private long getLength() { private long getLength() {
return length; return binaryStream.getLength();
} }
private InputStream getStream() throws SQLException { private InputStream getStream() throws SQLException {
InputStream stream = binaryStream.getInputStream();
try { try {
if (needsReset) { if ( needsReset ) {
stream.reset(); stream.reset();
} }
} }
@ -94,6 +93,7 @@ public class BlobProxy implements InvocationHandler {
* @throws UnsupportedOperationException if any methods other than {@link Blob#length()} * @throws UnsupportedOperationException if any methods other than {@link Blob#length()}
* or {@link Blob#getBinaryStream} are invoked. * or {@link Blob#getBinaryStream} are invoked.
*/ */
@Override
@SuppressWarnings({ "UnnecessaryBoxing" }) @SuppressWarnings({ "UnnecessaryBoxing" })
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
final String methodName = method.getName(); final String methodName = method.getName();
@ -102,6 +102,9 @@ public class BlobProxy implements InvocationHandler {
if ( "length".equals( methodName ) && argCount == 0 ) { if ( "length".equals( methodName ) && argCount == 0 ) {
return Long.valueOf( getLength() ); return Long.valueOf( getLength() );
} }
if ( "getUnderlyingStream".equals( methodName ) ) {
return binaryStream;
}
if ( "getBinaryStream".equals( methodName ) ) { if ( "getBinaryStream".equals( methodName ) ) {
if ( argCount == 0 ) { if ( argCount == 0 ) {
return getStream(); return getStream();
@ -137,7 +140,7 @@ public class BlobProxy implements InvocationHandler {
} }
} }
if ( "free".equals( methodName ) && argCount == 0 ) { if ( "free".equals( methodName ) && argCount == 0 ) {
stream.close(); binaryStream.release();
return null; return null;
} }
if ( "toString".equals( methodName ) && argCount == 0 ) { if ( "toString".equals( methodName ) && argCount == 0 ) {
@ -197,4 +200,43 @@ public class BlobProxy implements InvocationHandler {
} }
return cl; return cl;
} }
private static class StreamBackedBinaryStream implements BinaryStream {
private final InputStream stream;
private final long length;
private byte[] bytes;
private StreamBackedBinaryStream(InputStream stream, long length) {
this.stream = stream;
this.length = length;
}
@Override
public InputStream getInputStream() {
return stream;
}
@Override
public byte[] getBytes() {
if ( bytes == null ) {
bytes = DataHelper.extractBytes( stream );
}
return bytes;
}
@Override
public long getLength() {
return (int) length;
}
@Override
public void release() {
try {
stream.close();
}
catch (IOException ignore) {
}
}
}
} }

View File

@ -21,7 +21,9 @@
* 51 Franklin Street, Fifth Floor * 51 Franklin Street, Fifth Floor
* Boston, MA 02110-1301 USA * Boston, MA 02110-1301 USA
*/ */
package org.hibernate.type.descriptor; package org.hibernate.engine.jdbc;
import java.io.InputStream;
import java.io.Reader; import java.io.Reader;
/** /**
@ -32,17 +34,28 @@ import java.io.Reader;
*/ */
public interface CharacterStream { public interface CharacterStream {
/** /**
* Retrieve the reader. * Provides access to the underlying data as a Reader.
* *
* @return The reader. * @return The reader.
*/ */
public Reader getReader(); public Reader asReader();
/** /**
* Retrieve the number of characters. JDBC 3 and earlier defined the length in terms of int type rather than * Provides access to the underlying data as a String.
* long type :( *
* @return The underlying String data
*/
public String asString();
/**
* Retrieve the number of characters.
* *
* @return The number of characters. * @return The number of characters.
*/ */
public int getLength(); public long getLength();
/**
* Release any underlying resources.
*/
public void release();
} }

View File

@ -23,11 +23,16 @@
*/ */
package org.hibernate.engine.jdbc; package org.hibernate.engine.jdbc;
/** /**
* Marker interface for non-contextually created {@link java.sql.Clob} instances.. * Marker interface for non-contextually created {@link java.sql.Clob} instances..
* *
* @author Steve Ebersole * @author Steve Ebersole
*/ */
public interface ClobImplementer { public interface ClobImplementer {
/**
* Gets access to the data underlying this CLOB.
*
* @return Access to the underlying data.
*/
public CharacterStream getUnderlyingStream();
} }

View File

@ -33,11 +33,12 @@ import java.lang.reflect.Proxy;
import java.sql.Clob; import java.sql.Clob;
import java.sql.SQLException; import java.sql.SQLException;
import org.hibernate.engine.jdbc.internal.CharacterStreamImpl;
import org.hibernate.type.descriptor.java.DataHelper; import org.hibernate.type.descriptor.java.DataHelper;
/** /**
* Manages aspects of proxying {@link Clob Clobs} for non-contextual creation, including proxy creation and * Manages aspects of proxying {@link Clob Clobs} for non-contextual creation, including proxy creation and
* handling proxy invocations. * handling proxy invocations. We use proxies here solely to avoid JDBC version incompatibilities.
* *
* @author Gavin King * @author Gavin King
* @author Steve Ebersole * @author Steve Ebersole
@ -46,12 +47,9 @@ import org.hibernate.type.descriptor.java.DataHelper;
public class ClobProxy implements InvocationHandler { public class ClobProxy implements InvocationHandler {
private static final Class[] PROXY_INTERFACES = new Class[] { Clob.class, ClobImplementer.class }; private static final Class[] PROXY_INTERFACES = new Class[] { Clob.class, ClobImplementer.class };
private String string; private final CharacterStream characterStream;
private Reader reader;
private long length;
private boolean needsReset = false; private boolean needsReset = false;
/** /**
* Constructor used to build {@link Clob} from string data. * Constructor used to build {@link Clob} from string data.
* *
@ -59,9 +57,7 @@ public class ClobProxy implements InvocationHandler {
* @see #generateProxy(String) * @see #generateProxy(String)
*/ */
protected ClobProxy(String string) { protected ClobProxy(String string) {
this.string = string; this.characterStream = new CharacterStreamImpl( string );
reader = new StringReader(string);
length = string.length();
} }
/** /**
@ -72,28 +68,25 @@ public class ClobProxy implements InvocationHandler {
* @see #generateProxy(java.io.Reader, long) * @see #generateProxy(java.io.Reader, long)
*/ */
protected ClobProxy(Reader reader, long length) { protected ClobProxy(Reader reader, long length) {
this.reader = reader; this.characterStream = new CharacterStreamImpl( reader, length );
this.length = length;
} }
protected long getLength() { protected long getLength() {
return length; return characterStream.getLength();
} }
protected InputStream getAsciiStream() throws SQLException { protected InputStream getAsciiStream() throws SQLException {
resetIfNeeded(); resetIfNeeded();
return new ReaderInputStream( reader ); return new ReaderInputStream( characterStream.asReader() );
} }
protected Reader getCharacterStream() throws SQLException { protected Reader getCharacterStream() throws SQLException {
resetIfNeeded(); resetIfNeeded();
return reader; return characterStream.asReader();
} }
protected String getSubString(long start, int length) { protected String getSubString(long start, int length) {
if ( string == null ) { final String string = characterStream.asString();
throw new UnsupportedOperationException( "Clob was not created from string; cannot substring" );
}
// semi-naive implementation // semi-naive implementation
int endIndex = Math.min( ((int)start)+length, string.length() ); int endIndex = Math.min( ((int)start)+length, string.length() );
return string.substring( (int)start, endIndex ); return string.substring( (int)start, endIndex );
@ -105,6 +98,7 @@ public class ClobProxy implements InvocationHandler {
* @throws UnsupportedOperationException if any methods other than {@link Clob#length()}, * @throws UnsupportedOperationException if any methods other than {@link Clob#length()},
* {@link Clob#getAsciiStream()}, or {@link Clob#getCharacterStream()} are invoked. * {@link Clob#getAsciiStream()}, or {@link Clob#getCharacterStream()} are invoked.
*/ */
@Override
@SuppressWarnings({ "UnnecessaryBoxing" }) @SuppressWarnings({ "UnnecessaryBoxing" })
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
final String methodName = method.getName(); final String methodName = method.getName();
@ -113,6 +107,9 @@ public class ClobProxy implements InvocationHandler {
if ( "length".equals( methodName ) && argCount == 0 ) { if ( "length".equals( methodName ) && argCount == 0 ) {
return Long.valueOf( getLength() ); return Long.valueOf( getLength() );
} }
if ( "getUnderlyingStream".equals( methodName ) ) {
return characterStream;
}
if ( "getAsciiStream".equals( methodName ) && argCount == 0 ) { if ( "getAsciiStream".equals( methodName ) && argCount == 0 ) {
return getAsciiStream(); return getAsciiStream();
} }
@ -152,7 +149,7 @@ public class ClobProxy implements InvocationHandler {
return getSubString( start-1, length ); return getSubString( start-1, length );
} }
if ( "free".equals( methodName ) && argCount == 0 ) { if ( "free".equals( methodName ) && argCount == 0 ) {
reader.close(); characterStream.release();
return null; return null;
} }
if ( "toString".equals( methodName ) && argCount == 0 ) { if ( "toString".equals( methodName ) && argCount == 0 ) {
@ -171,7 +168,7 @@ public class ClobProxy implements InvocationHandler {
protected void resetIfNeeded() throws SQLException { protected void resetIfNeeded() throws SQLException {
try { try {
if ( needsReset ) { if ( needsReset ) {
reader.reset(); characterStream.asReader().reset();
} }
} }
catch ( IOException ioe ) { catch ( IOException ioe ) {

View File

@ -59,9 +59,7 @@ public class ContextualLobCreator extends AbstractLobCreator implements LobCreat
return lobCreationContext.execute( CREATE_BLOB_CALLBACK ); return lobCreationContext.execute( CREATE_BLOB_CALLBACK );
} }
/** @Override
* {@inheritDoc}
*/
public Blob createBlob(byte[] bytes) { public Blob createBlob(byte[] bytes) {
try { try {
Blob blob = createBlob(); Blob blob = createBlob();
@ -73,25 +71,11 @@ public class ContextualLobCreator extends AbstractLobCreator implements LobCreat
} }
} }
/** @Override
* {@inheritDoc}
*/
public Blob createBlob(InputStream inputStream, long length) { public Blob createBlob(InputStream inputStream, long length) {
try { // IMPL NOTE : it is inefficient to use JDBC LOB locator creation to create a LOB
Blob blob = createBlob(); // backed by a given stream. So just wrap the stream (which is what the NonContextualLobCreator does).
OutputStream byteStream = blob.setBinaryStream( 1 ); return NonContextualLobCreator.INSTANCE.createBlob( inputStream, length );
StreamUtils.copy( inputStream, byteStream );
byteStream.flush();
byteStream.close();
// todo : validate length written versus length given?
return blob;
}
catch ( SQLException e ) {
throw new JDBCException( "Unable to prepare BLOB binary stream for writing",e );
}
catch ( IOException e ) {
throw new HibernateException( "Unable to write stream contents to BLOB", e );
}
} }
/** /**
@ -103,9 +87,7 @@ public class ContextualLobCreator extends AbstractLobCreator implements LobCreat
return lobCreationContext.execute( CREATE_CLOB_CALLBACK ); return lobCreationContext.execute( CREATE_CLOB_CALLBACK );
} }
/** @Override
* {@inheritDoc}
*/
public Clob createClob(String string) { public Clob createClob(String string) {
try { try {
Clob clob = createClob(); Clob clob = createClob();
@ -117,24 +99,11 @@ public class ContextualLobCreator extends AbstractLobCreator implements LobCreat
} }
} }
/** @Override
* {@inheritDoc}
*/
public Clob createClob(Reader reader, long length) { public Clob createClob(Reader reader, long length) {
try { // IMPL NOTE : it is inefficient to use JDBC LOB locator creation to create a LOB
Clob clob = createClob(); // backed by a given stream. So just wrap the stream (which is what the NonContextualLobCreator does).
Writer writer = clob.setCharacterStream( 1 ); return NonContextualLobCreator.INSTANCE.createClob( reader, length );
StreamUtils.copy( reader, writer );
writer.flush();
writer.close();
return clob;
}
catch ( SQLException e ) {
throw new JDBCException( "Unable to prepare CLOB stream for writing", e );
}
catch ( IOException e ) {
throw new HibernateException( "Unable to write CLOB stream content", e );
}
} }
/** /**
@ -146,9 +115,7 @@ public class ContextualLobCreator extends AbstractLobCreator implements LobCreat
return lobCreationContext.execute( CREATE_NCLOB_CALLBACK ); return lobCreationContext.execute( CREATE_NCLOB_CALLBACK );
} }
/** @Override
* {@inheritDoc}
*/
public NClob createNClob(String string) { public NClob createNClob(String string) {
try { try {
NClob nclob = createNClob(); NClob nclob = createNClob();
@ -160,24 +127,11 @@ public class ContextualLobCreator extends AbstractLobCreator implements LobCreat
} }
} }
/** @Override
* {@inheritDoc}
*/
public NClob createNClob(Reader reader, long length) { public NClob createNClob(Reader reader, long length) {
try { // IMPL NOTE : it is inefficient to use JDBC LOB locator creation to create a LOB
NClob nclob = createNClob(); // backed by a given stream. So just wrap the stream (which is what the NonContextualLobCreator does).
Writer writer = nclob.setCharacterStream( 1 ); return NonContextualLobCreator.INSTANCE.createNClob( reader, length );
StreamUtils.copy( reader, writer );
writer.flush();
writer.close();
return nclob;
}
catch ( SQLException e ) {
throw new JDBCException( "Unable to prepare NCLOB stream for writing", e );
}
catch ( IOException e ) {
throw new HibernateException( "Unable to write NCLOB stream content", e );
}
} }
public static final LobCreationContext.Callback<Blob> CREATE_BLOB_CALLBACK = new LobCreationContext.Callback<Blob>() { public static final LobCreationContext.Callback<Blob> CREATE_BLOB_CALLBACK = new LobCreationContext.Callback<Blob>() {

View File

@ -31,8 +31,6 @@ import java.sql.NClob;
/** /**
* Contract for creating various LOB references. * Contract for creating various LOB references.
* *
* @todo LobCreator really needs to be an api since we expose it to users.
*
* @author Steve Ebersole * @author Steve Ebersole
* @author Gail Badner * @author Gail Badner
*/ */

View File

@ -22,6 +22,7 @@
* Boston, MA 02110-1301 USA * Boston, MA 02110-1301 USA
*/ */
package org.hibernate.engine.jdbc; package org.hibernate.engine.jdbc;
import java.io.Reader; import java.io.Reader;
import java.lang.reflect.Proxy; import java.lang.reflect.Proxy;
import java.sql.Clob; import java.sql.Clob;
@ -29,10 +30,10 @@ import java.sql.NClob;
/** /**
* Manages aspects of proxying java.sql.NClobs for non-contextual creation, including proxy creation and * Manages aspects of proxying java.sql.NClobs for non-contextual creation, including proxy creation and
* handling proxy invocations. * handling proxy invocations. We use proxies here solely to avoid JDBC version incompatibilities.
* <p/> * <p/>
* Generated proxies are typed as {@link java.sql.Clob} (java.sql.NClob extends {@link java.sql.Clob}) and in JDK 1.6 environments, they * Generated proxies are typed as {@link java.sql.Clob} (java.sql.NClob extends {@link java.sql.Clob})
* are also typed to java.sql.NClob * and in JDK 1.6+ environments, they are also typed to java.sql.NClob
* *
* @author Steve Ebersole * @author Steve Ebersole
*/ */

View File

@ -22,6 +22,7 @@
* Boston, MA 02110-1301 USA * Boston, MA 02110-1301 USA
*/ */
package org.hibernate.engine.jdbc; package org.hibernate.engine.jdbc;
import java.io.InputStream; import java.io.InputStream;
import java.io.Reader; import java.io.Reader;
import java.sql.Blob; import java.sql.Blob;
@ -41,44 +42,32 @@ public class NonContextualLobCreator extends AbstractLobCreator implements LobCr
private NonContextualLobCreator() { private NonContextualLobCreator() {
} }
/** @Override
* {@inheritDoc}
*/
public Blob createBlob(byte[] bytes) { public Blob createBlob(byte[] bytes) {
return BlobProxy.generateProxy( bytes ); return BlobProxy.generateProxy( bytes );
} }
/** @Override
* {@inheritDoc}
*/
public Blob createBlob(InputStream stream, long length) { public Blob createBlob(InputStream stream, long length) {
return BlobProxy.generateProxy( stream, length ); return BlobProxy.generateProxy( stream, length );
} }
/** @Override
* {@inheritDoc}
*/
public Clob createClob(String string) { public Clob createClob(String string) {
return ClobProxy.generateProxy( string ); return ClobProxy.generateProxy( string );
} }
/** @Override
* {@inheritDoc}
*/
public Clob createClob(Reader reader, long length) { public Clob createClob(Reader reader, long length) {
return ClobProxy.generateProxy( reader, length ); return ClobProxy.generateProxy( reader, length );
} }
/** @Override
* {@inheritDoc}
*/
public NClob createNClob(String string) { public NClob createNClob(String string) {
return NClobProxy.generateProxy( string ); return NClobProxy.generateProxy( string );
} }
/** @Override
* {@inheritDoc}
*/
public NClob createNClob(Reader reader, long length) { public NClob createNClob(Reader reader, long length) {
return NClobProxy.generateProxy( reader, length ); return NClobProxy.generateProxy( reader, length );
} }

View File

@ -1,10 +1,10 @@
/* /*
* Hibernate, Relational Persistence for Idiomatic Java * Hibernate, Relational Persistence for Idiomatic Java
* *
* Copyright (c) 2008, Red Hat Middleware LLC or third-party contributors as * Copyright (c) 2008, Red Hat Inc. or third-party contributors as
* indicated by the @author tags or express copyright attribution * indicated by the @author tags or express copyright attribution
* statements applied by the authors. All third-party contributions are * statements applied by the authors. All third-party contributions are
* distributed under license by Red Hat Middleware LLC. * distributed under license by Red Hat Inc.
* *
* This copyrighted material is made available to anyone wishing to use, modify, * 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 * copy, or redistribute it subject to the terms and conditions of the GNU
@ -20,7 +20,6 @@
* Free Software Foundation, Inc. * Free Software Foundation, Inc.
* 51 Franklin Street, Fifth Floor * 51 Franklin Street, Fifth Floor
* Boston, MA 02110-1301 USA * Boston, MA 02110-1301 USA
*
*/ */
package org.hibernate.engine.jdbc; package org.hibernate.engine.jdbc;
import java.io.IOException; import java.io.IOException;
@ -42,5 +41,4 @@ public class ReaderInputStream extends InputStream {
public int read() throws IOException { public int read() throws IOException {
return reader.read(); return reader.read();
} }
} }

View File

@ -22,6 +22,7 @@
* Boston, MA 02110-1301 USA * Boston, MA 02110-1301 USA
*/ */
package org.hibernate.engine.jdbc; package org.hibernate.engine.jdbc;
import java.io.Serializable; import java.io.Serializable;
import java.lang.reflect.InvocationHandler; import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException; import java.lang.reflect.InvocationTargetException;
@ -62,9 +63,7 @@ public class SerializableBlobProxy implements InvocationHandler, Serializable {
} }
} }
/** @Override
* {@inheritDoc}
*/
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if ( "getWrappedBlob".equals( method.getName() ) ) { if ( "getWrappedBlob".equals( method.getName() ) ) {
return getWrappedBlob(); return getWrappedBlob();

View File

@ -22,6 +22,7 @@
* Boston, MA 02110-1301 USA * Boston, MA 02110-1301 USA
*/ */
package org.hibernate.engine.jdbc; package org.hibernate.engine.jdbc;
import java.io.Serializable; import java.io.Serializable;
import java.lang.reflect.InvocationHandler; import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException; import java.lang.reflect.InvocationTargetException;
@ -62,9 +63,7 @@ public class SerializableClobProxy implements InvocationHandler, Serializable {
} }
} }
/** @Override
* {@inheritDoc}
*/
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if ( "getWrappedClob".equals( method.getName() ) ) { if ( "getWrappedClob".equals( method.getName() ) ) {
return getWrappedClob(); return getWrappedClob();

View File

@ -22,6 +22,7 @@
* Boston, MA 02110-1301 USA * Boston, MA 02110-1301 USA
*/ */
package org.hibernate.engine.jdbc; package org.hibernate.engine.jdbc;
import java.lang.reflect.Proxy; import java.lang.reflect.Proxy;
import java.sql.Clob; import java.sql.Clob;

View File

@ -22,6 +22,7 @@
* Boston, MA 02110-1301 USA * Boston, MA 02110-1301 USA
*/ */
package org.hibernate.engine.jdbc; package org.hibernate.engine.jdbc;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.io.OutputStream; import java.io.OutputStream;

View File

@ -22,6 +22,7 @@
* Boston, MA 02110-1301 USA * Boston, MA 02110-1301 USA
*/ */
package org.hibernate.engine.jdbc; package org.hibernate.engine.jdbc;
import java.sql.Blob; import java.sql.Blob;
/** /**

View File

@ -22,6 +22,7 @@
* Boston, MA 02110-1301 USA * Boston, MA 02110-1301 USA
*/ */
package org.hibernate.engine.jdbc; package org.hibernate.engine.jdbc;
import java.sql.Clob; import java.sql.Clob;
/** /**

View File

@ -21,12 +21,13 @@
* 51 Franklin Street, Fifth Floor * 51 Franklin Street, Fifth Floor
* Boston, MA 02110-1301 USA * Boston, MA 02110-1301 USA
*/ */
package org.hibernate.type.descriptor.java; package org.hibernate.engine.jdbc.internal;
import java.io.ByteArrayInputStream; import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import org.hibernate.type.descriptor.BinaryStream; import org.hibernate.engine.jdbc.BinaryStream;
/** /**
* Implementation of {@link BinaryStream} * Implementation of {@link BinaryStream}
@ -50,7 +51,16 @@ public class BinaryStreamImpl extends ByteArrayInputStream implements BinaryStre
return buf; return buf;
} }
public int getLength() { public long getLength() {
return length; return length;
} }
@Override
public void release() {
try {
super.close();
}
catch (IOException ignore) {
}
}
} }

View File

@ -21,12 +21,15 @@
* 51 Franklin Street, Fifth Floor * 51 Franklin Street, Fifth Floor
* Boston, MA 02110-1301 USA * Boston, MA 02110-1301 USA
*/ */
package org.hibernate.type.descriptor.java; package org.hibernate.engine.jdbc.internal;
import java.io.IOException;
import java.io.InputStream;
import java.io.Reader; import java.io.Reader;
import java.io.StringReader; import java.io.StringReader;
import org.hibernate.type.descriptor.CharacterStream; import org.hibernate.engine.jdbc.CharacterStream;
import org.hibernate.type.descriptor.java.DataHelper;
/** /**
* Implementation of {@link CharacterStream} * Implementation of {@link CharacterStream}
@ -34,19 +37,51 @@ import org.hibernate.type.descriptor.CharacterStream;
* @author Steve Ebersole * @author Steve Ebersole
*/ */
public class CharacterStreamImpl implements CharacterStream { public class CharacterStreamImpl implements CharacterStream {
private final StringReader reader; private final long length;
private final int length;
private Reader reader;
private String string;
public CharacterStreamImpl(String chars) { public CharacterStreamImpl(String chars) {
reader = new StringReader( chars ); this.string = chars;
length = chars.length(); this.length = chars.length();
} }
public Reader getReader() { public CharacterStreamImpl(Reader reader, long length) {
this.reader = reader;
this.length = length;
}
@Override
public Reader asReader() {
if ( reader == null ) {
reader = new StringReader( string );
}
return reader; return reader;
} }
public int getLength() { @Override
public String asString() {
if ( string == null ) {
string = DataHelper.extractString( reader );
}
return string;
}
@Override
public long getLength() {
return length; return length;
} }
@Override
public void release() {
if ( reader == null ) {
return;
}
try {
reader.close();
}
catch (IOException ignore) {
}
}
} }

View File

@ -31,9 +31,11 @@ import java.sql.SQLException;
import java.util.Comparator; import java.util.Comparator;
import org.hibernate.HibernateException; import org.hibernate.HibernateException;
import org.hibernate.engine.jdbc.BlobImplementer;
import org.hibernate.engine.jdbc.BlobProxy; import org.hibernate.engine.jdbc.BlobProxy;
import org.hibernate.engine.jdbc.WrappedBlob; import org.hibernate.engine.jdbc.WrappedBlob;
import org.hibernate.type.descriptor.BinaryStream; import org.hibernate.engine.jdbc.BinaryStream;
import org.hibernate.engine.jdbc.internal.BinaryStreamImpl;
import org.hibernate.type.descriptor.WrapperOptions; import org.hibernate.type.descriptor.WrapperOptions;
/** /**
@ -117,16 +119,33 @@ public class BlobTypeDescriptor extends AbstractTypeDescriptor<Blob> {
try { try {
if ( BinaryStream.class.isAssignableFrom( type ) ) { if ( BinaryStream.class.isAssignableFrom( type ) ) {
if ( BlobImplementer.class.isInstance( value ) ) {
// if the incoming Blob is a wrapper, just pass along its BinaryStream
return (X) ( (BlobImplementer) value ).getUnderlyingStream();
}
else {
// otherwise we need to build a BinaryStream...
return (X) new BinaryStreamImpl( DataHelper.extractBytes( value.getBinaryStream() ) ); return (X) new BinaryStreamImpl( DataHelper.extractBytes( value.getBinaryStream() ) );
} else if ( byte[].class.isAssignableFrom( type )) { }
}
else if ( byte[].class.isAssignableFrom( type )) {
if ( BlobImplementer.class.isInstance( value ) ) {
// if the incoming Blob is a wrapper, just grab the bytes from its BinaryStream
return (X) ( (BlobImplementer) value ).getUnderlyingStream().getBytes();
}
else {
// otherwise extract the bytes from the stream manually
return (X) DataHelper.extractBytes( value.getBinaryStream() ); return (X) DataHelper.extractBytes( value.getBinaryStream() );
} else if (Blob.class.isAssignableFrom( type )) { }
}
else if (Blob.class.isAssignableFrom( type )) {
final Blob blob = WrappedBlob.class.isInstance( value ) final Blob blob = WrappedBlob.class.isInstance( value )
? ( (WrappedBlob) value ).getWrappedBlob() ? ( (WrappedBlob) value ).getWrappedBlob()
: value; : value;
return (X) blob; return (X) blob;
} }
} catch ( SQLException e ) { }
catch ( SQLException e ) {
throw new HibernateException( "Unable to access blob stream", e ); throw new HibernateException( "Unable to access blob stream", e );
} }
@ -142,9 +161,11 @@ public class BlobTypeDescriptor extends AbstractTypeDescriptor<Blob> {
// org.hibernate.type.descriptor.sql.BlobTypeDescriptor // org.hibernate.type.descriptor.sql.BlobTypeDescriptor
if ( Blob.class.isAssignableFrom( value.getClass() ) ) { if ( Blob.class.isAssignableFrom( value.getClass() ) ) {
return options.getLobCreator().wrap( (Blob) value ); return options.getLobCreator().wrap( (Blob) value );
} else if ( byte[].class.isAssignableFrom( value.getClass() ) ) { }
else if ( byte[].class.isAssignableFrom( value.getClass() ) ) {
return options.getLobCreator().createBlob( ( byte[] ) value); return options.getLobCreator().createBlob( ( byte[] ) value);
} else if ( InputStream.class.isAssignableFrom( value.getClass() ) ) { }
else if ( InputStream.class.isAssignableFrom( value.getClass() ) ) {
InputStream inputStream = ( InputStream ) value; InputStream inputStream = ( InputStream ) value;
try { try {
return options.getLobCreator().createBlob( inputStream, inputStream.available() ); return options.getLobCreator().createBlob( inputStream, inputStream.available() );
@ -154,7 +175,6 @@ public class BlobTypeDescriptor extends AbstractTypeDescriptor<Blob> {
} }
} }
throw unknownWrap( value.getClass() ); throw unknownWrap( value.getClass() );
} }
} }

View File

@ -29,7 +29,8 @@ import java.sql.Blob;
import java.sql.SQLException; import java.sql.SQLException;
import org.hibernate.HibernateException; import org.hibernate.HibernateException;
import org.hibernate.type.descriptor.BinaryStream; import org.hibernate.engine.jdbc.BinaryStream;
import org.hibernate.engine.jdbc.internal.BinaryStreamImpl;
import org.hibernate.type.descriptor.WrapperOptions; import org.hibernate.type.descriptor.WrapperOptions;
/** /**

View File

@ -28,7 +28,8 @@ import java.io.StringReader;
import java.sql.Clob; import java.sql.Clob;
import java.util.Arrays; import java.util.Arrays;
import org.hibernate.type.descriptor.CharacterStream; import org.hibernate.engine.jdbc.CharacterStream;
import org.hibernate.engine.jdbc.internal.CharacterStreamImpl;
import org.hibernate.type.descriptor.WrapperOptions; import org.hibernate.type.descriptor.WrapperOptions;
/** /**

View File

@ -27,9 +27,11 @@ import java.io.Serializable;
import java.sql.Clob; import java.sql.Clob;
import java.util.Comparator; import java.util.Comparator;
import org.hibernate.engine.jdbc.ClobImplementer;
import org.hibernate.engine.jdbc.ClobProxy; import org.hibernate.engine.jdbc.ClobProxy;
import org.hibernate.engine.jdbc.WrappedClob; import org.hibernate.engine.jdbc.WrappedClob;
import org.hibernate.type.descriptor.CharacterStream; import org.hibernate.engine.jdbc.CharacterStream;
import org.hibernate.engine.jdbc.internal.CharacterStreamImpl;
import org.hibernate.type.descriptor.WrapperOptions; import org.hibernate.type.descriptor.WrapperOptions;
/** /**
@ -102,8 +104,15 @@ public class ClobTypeDescriptor extends AbstractTypeDescriptor<Clob> {
} }
if ( CharacterStream.class.isAssignableFrom( type ) ) { if ( CharacterStream.class.isAssignableFrom( type ) ) {
if ( ClobImplementer.class.isInstance( value ) ) {
// if the incoming Clob is a wrapper, just pass along its CharacterStream
return (X) ( (ClobImplementer) value ).getUnderlyingStream();
}
else {
// otherwise we need to build one...
return (X) new CharacterStreamImpl( DataHelper.extractString( value ) ); return (X) new CharacterStreamImpl( DataHelper.extractString( value ) );
} }
}
final Clob clob = WrappedClob.class.isInstance( value ) final Clob clob = WrappedClob.class.isInstance( value )
? ( (WrappedClob) value ).getWrappedClob() ? ( (WrappedClob) value ).getWrappedClob()

View File

@ -34,8 +34,9 @@ import java.sql.SQLException;
import org.jboss.logging.Logger; import org.jboss.logging.Logger;
import org.hibernate.HibernateException; import org.hibernate.HibernateException;
import org.hibernate.engine.jdbc.internal.BinaryStreamImpl;
import org.hibernate.internal.CoreMessageLogger; import org.hibernate.internal.CoreMessageLogger;
import org.hibernate.type.descriptor.BinaryStream; import org.hibernate.engine.jdbc.BinaryStream;
/** /**
* A help for dealing with BLOB and CLOB data * A help for dealing with BLOB and CLOB data

View File

@ -30,7 +30,8 @@ import java.sql.SQLException;
import java.util.Arrays; import java.util.Arrays;
import org.hibernate.HibernateException; import org.hibernate.HibernateException;
import org.hibernate.type.descriptor.BinaryStream; import org.hibernate.engine.jdbc.BinaryStream;
import org.hibernate.engine.jdbc.internal.BinaryStreamImpl;
import org.hibernate.type.descriptor.WrapperOptions; import org.hibernate.type.descriptor.WrapperOptions;
/** /**

View File

@ -28,7 +28,8 @@ import java.io.StringReader;
import java.sql.Clob; import java.sql.Clob;
import java.util.Arrays; import java.util.Arrays;
import org.hibernate.type.descriptor.CharacterStream; import org.hibernate.engine.jdbc.CharacterStream;
import org.hibernate.engine.jdbc.internal.CharacterStreamImpl;
import org.hibernate.type.descriptor.WrapperOptions; import org.hibernate.type.descriptor.WrapperOptions;
/** /**

View File

@ -30,8 +30,9 @@ import java.sql.Blob;
import java.sql.SQLException; import java.sql.SQLException;
import org.hibernate.HibernateException; import org.hibernate.HibernateException;
import org.hibernate.engine.jdbc.internal.BinaryStreamImpl;
import org.hibernate.internal.util.SerializationHelper; import org.hibernate.internal.util.SerializationHelper;
import org.hibernate.type.descriptor.BinaryStream; import org.hibernate.engine.jdbc.BinaryStream;
import org.hibernate.type.descriptor.WrapperOptions; import org.hibernate.type.descriptor.WrapperOptions;
/** /**

View File

@ -27,7 +27,8 @@ import java.io.Reader;
import java.io.StringReader; import java.io.StringReader;
import java.sql.Clob; import java.sql.Clob;
import org.hibernate.type.descriptor.CharacterStream; import org.hibernate.engine.jdbc.CharacterStream;
import org.hibernate.engine.jdbc.internal.CharacterStreamImpl;
import org.hibernate.type.descriptor.WrapperOptions; import org.hibernate.type.descriptor.WrapperOptions;
/** /**

View File

@ -29,7 +29,7 @@ import java.sql.ResultSet;
import java.sql.SQLException; import java.sql.SQLException;
import java.sql.Types; import java.sql.Types;
import org.hibernate.type.descriptor.BinaryStream; import org.hibernate.engine.jdbc.BinaryStream;
import org.hibernate.type.descriptor.WrapperOptions; import org.hibernate.type.descriptor.WrapperOptions;
import org.hibernate.type.descriptor.java.JavaTypeDescriptor; import org.hibernate.type.descriptor.java.JavaTypeDescriptor;

View File

@ -29,7 +29,7 @@ import java.sql.ResultSet;
import java.sql.SQLException; import java.sql.SQLException;
import java.sql.Types; import java.sql.Types;
import org.hibernate.type.descriptor.CharacterStream; import org.hibernate.engine.jdbc.CharacterStream;
import org.hibernate.type.descriptor.ValueBinder; import org.hibernate.type.descriptor.ValueBinder;
import org.hibernate.type.descriptor.ValueExtractor; import org.hibernate.type.descriptor.ValueExtractor;
import org.hibernate.type.descriptor.WrapperOptions; import org.hibernate.type.descriptor.WrapperOptions;
@ -80,7 +80,7 @@ public abstract class ClobTypeDescriptor implements SqlTypeDescriptor {
protected void doBind(PreparedStatement st, X value, int index, WrapperOptions options) protected void doBind(PreparedStatement st, X value, int index, WrapperOptions options)
throws SQLException { throws SQLException {
final CharacterStream characterStream = javaTypeDescriptor.unwrap( value, CharacterStream.class, options ); final CharacterStream characterStream = javaTypeDescriptor.unwrap( value, CharacterStream.class, options );
st.setCharacterStream( index, characterStream.getReader(), characterStream.getLength() ); st.setCharacterStream( index, characterStream.asReader(), characterStream.getLength() );
} }
}; };
} }