HHH-5400 - Blob persistence fails with Hibernate 3.6.0-SNAPSHOT, works with 3.5.3-Final
git-svn-id: https://svn.jboss.org/repos/hibernate/core/trunk@20070 1b8cb986-b30d-0410-93ca-fae66ebed9b2
This commit is contained in:
parent
9dff718744
commit
84c52c457d
|
@ -29,9 +29,11 @@ import java.lang.reflect.Proxy;
|
||||||
import java.sql.Blob;
|
import java.sql.Blob;
|
||||||
import java.sql.SQLException;
|
import java.sql.SQLException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.io.ByteArrayInputStream;
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
|
||||||
|
import org.hibernate.type.descriptor.java.BinaryStreamImpl;
|
||||||
|
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 Blobs} for non-contextual creation, including proxy creation and
|
||||||
* handling proxy invocations.
|
* handling proxy invocations.
|
||||||
|
@ -48,18 +50,18 @@ public class BlobProxy implements InvocationHandler {
|
||||||
private boolean needsReset = false;
|
private boolean needsReset = false;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Ctor used to build {@link Blob} from byte array.
|
* Constructor used to build {@link Blob} from byte array.
|
||||||
*
|
*
|
||||||
* @param bytes The byte array
|
* @param bytes The byte array
|
||||||
* @see #generateProxy(byte[])
|
* @see #generateProxy(byte[])
|
||||||
*/
|
*/
|
||||||
private BlobProxy(byte[] bytes) {
|
private BlobProxy(byte[] bytes) {
|
||||||
this.stream = new ByteArrayInputStream( bytes );
|
this.stream = new BinaryStreamImpl( bytes );
|
||||||
this.length = bytes.length;
|
this.length = bytes.length;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Ctor used to build {@link Blob} from a stream.
|
* Constructor used to build {@link Blob} from a stream.
|
||||||
*
|
*
|
||||||
* @param stream The binary stream
|
* @param stream The binary stream
|
||||||
* @param length The length of the stream
|
* @param length The length of the stream
|
||||||
|
@ -93,26 +95,62 @@ 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.
|
||||||
*/
|
*/
|
||||||
|
@SuppressWarnings({ "UnnecessaryBoxing" })
|
||||||
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
|
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
|
||||||
if ( "length".equals( method.getName() ) ) {
|
final String methodName = method.getName();
|
||||||
return new Long( getLength() );
|
final int argCount = method.getParameterTypes().length;
|
||||||
|
|
||||||
|
if ( "length".equals( methodName ) && argCount == 0 ) {
|
||||||
|
return Long.valueOf( getLength() );
|
||||||
}
|
}
|
||||||
if ( "getBinaryStream".equals( method.getName() ) && method.getParameterTypes().length == 0 ) {
|
if ( "getBinaryStream".equals( methodName ) ) {
|
||||||
return getStream();
|
if ( argCount == 0 ) {
|
||||||
|
return getStream();
|
||||||
|
}
|
||||||
|
else if ( argCount == 2 ) {
|
||||||
|
long start = (Long) args[0];
|
||||||
|
if ( start < 1 ) {
|
||||||
|
throw new SQLException( "Start position 1-based; must be 1 or more." );
|
||||||
|
}
|
||||||
|
if ( start > getLength() ) {
|
||||||
|
throw new SQLException( "Start position [" + start + "] cannot exceed overall CLOB length [" + getLength() + "]" );
|
||||||
|
}
|
||||||
|
int length = (Integer) args[1];
|
||||||
|
if ( length < 0 ) {
|
||||||
|
// java docs specifically say for getBinaryStream(long,int) that the start+length must not exceed the
|
||||||
|
// total length, however that is at odds with the getBytes(long,int) behavior.
|
||||||
|
throw new SQLException( "Length must be great-than-or-equal to zero." );
|
||||||
|
}
|
||||||
|
return DataHelper.subStream( getStream(), start-1, length );
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if ( "free".equals( method.getName() ) ) {
|
if ( "getBytes".equals( methodName ) ) {
|
||||||
|
if ( argCount == 2 ) {
|
||||||
|
long start = (Long) args[0];
|
||||||
|
if ( start < 1 ) {
|
||||||
|
throw new SQLException( "Start position 1-based; must be 1 or more." );
|
||||||
|
}
|
||||||
|
int length = (Integer) args[1];
|
||||||
|
if ( length < 0 ) {
|
||||||
|
throw new SQLException( "Length must be great-than-or-equal to zero." );
|
||||||
|
}
|
||||||
|
return DataHelper.extractBytes( getStream(), start-1, length );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ( "free".equals( methodName ) && argCount == 0 ) {
|
||||||
stream.close();
|
stream.close();
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
if ( "toString".equals( method.getName() ) ) {
|
if ( "toString".equals( methodName ) && argCount == 0 ) {
|
||||||
return this.toString();
|
return this.toString();
|
||||||
}
|
}
|
||||||
if ( "equals".equals( method.getName() ) ) {
|
if ( "equals".equals( methodName ) && argCount == 1 ) {
|
||||||
return Boolean.valueOf( proxy == args[0] );
|
return Boolean.valueOf( proxy == args[0] );
|
||||||
}
|
}
|
||||||
if ( "hashCode".equals( method.getName() ) ) {
|
if ( "hashCode".equals( methodName ) && argCount == 0 ) {
|
||||||
return new Integer( this.hashCode() );
|
return new Integer( this.hashCode() );
|
||||||
}
|
}
|
||||||
|
|
||||||
throw new UnsupportedOperationException( "Blob may not be manipulated from creating session" );
|
throw new UnsupportedOperationException( "Blob may not be manipulated from creating session" );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -33,6 +33,8 @@ import java.io.StringReader;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
|
||||||
|
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.
|
||||||
|
@ -51,7 +53,7 @@ public class ClobProxy implements InvocationHandler {
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Ctor used to build {@link Clob} from string data.
|
* Constructor used to build {@link Clob} from string data.
|
||||||
*
|
*
|
||||||
* @param string The byte array
|
* @param string The byte array
|
||||||
* @see #generateProxy(String)
|
* @see #generateProxy(String)
|
||||||
|
@ -63,7 +65,7 @@ public class ClobProxy implements InvocationHandler {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Ctor used to build {@link Clob} from a reader.
|
* Constructor used to build {@link Clob} from a reader.
|
||||||
*
|
*
|
||||||
* @param reader The character reader.
|
* @param reader The character reader.
|
||||||
* @param length The length of the reader stream.
|
* @param length The length of the reader stream.
|
||||||
|
@ -88,12 +90,13 @@ public class ClobProxy implements InvocationHandler {
|
||||||
return reader;
|
return reader;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected String getSubString(long pos, int length) {
|
protected String getSubString(long start, int length) {
|
||||||
if ( string == null ) {
|
if ( string == null ) {
|
||||||
throw new UnsupportedOperationException( "Clob was not created from string; cannot substring" );
|
throw new UnsupportedOperationException( "Clob was not created from string; cannot substring" );
|
||||||
}
|
}
|
||||||
// naive impl...
|
// semi-naive implementation
|
||||||
return string.substring( (int)pos-1, (int)(pos+length-1) );
|
int endIndex = Math.min( ((int)start)+length, string.length() );
|
||||||
|
return string.substring( (int)start, endIndex );
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -104,31 +107,64 @@ public class ClobProxy implements InvocationHandler {
|
||||||
*/
|
*/
|
||||||
@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 {
|
||||||
if ( "length".equals( method.getName() ) ) {
|
final String methodName = method.getName();
|
||||||
|
final int argCount = method.getParameterTypes().length;
|
||||||
|
|
||||||
|
if ( "length".equals( methodName ) && argCount == 0 ) {
|
||||||
return Long.valueOf( getLength() );
|
return Long.valueOf( getLength() );
|
||||||
}
|
}
|
||||||
if ( "getAsciiStream".equals( method.getName() ) ) {
|
if ( "getAsciiStream".equals( methodName ) && argCount == 0 ) {
|
||||||
return getAsciiStream();
|
return getAsciiStream();
|
||||||
}
|
}
|
||||||
if ( "getCharacterStream".equals( method.getName() ) ) {
|
if ( "getCharacterStream".equals( methodName ) ) {
|
||||||
return getCharacterStream();
|
if ( argCount == 0 ) {
|
||||||
|
return getCharacterStream();
|
||||||
|
}
|
||||||
|
else if ( argCount == 2 ) {
|
||||||
|
long start = (Long) args[0];
|
||||||
|
if ( start < 1 ) {
|
||||||
|
throw new SQLException( "Start position 1-based; must be 1 or more." );
|
||||||
|
}
|
||||||
|
if ( start > getLength() ) {
|
||||||
|
throw new SQLException( "Start position [" + start + "] cannot exceed overall CLOB length [" + getLength() + "]" );
|
||||||
|
}
|
||||||
|
int length = (Integer) args[1];
|
||||||
|
if ( length < 0 ) {
|
||||||
|
// java docs specifically say for getCharacterStream(long,int) that the start+length must not exceed the
|
||||||
|
// total length, however that is at odds with the getSubString(long,int) behavior.
|
||||||
|
throw new SQLException( "Length must be great-than-or-equal to zero." );
|
||||||
|
}
|
||||||
|
return DataHelper.subStream( getCharacterStream(), start-1, length );
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if ( "getSubString".equals( method.getName() ) ) {
|
if ( "getSubString".equals( methodName ) && argCount == 2 ) {
|
||||||
return getSubString( (Long)args[0], (Integer)args[1] );
|
long start = (Long) args[0];
|
||||||
|
if ( start < 1 ) {
|
||||||
|
throw new SQLException( "Start position 1-based; must be 1 or more." );
|
||||||
|
}
|
||||||
|
if ( start > getLength() ) {
|
||||||
|
throw new SQLException( "Start position [" + start + "] cannot exceed overall CLOB length [" + getLength() + "]" );
|
||||||
|
}
|
||||||
|
int length = (Integer) args[1];
|
||||||
|
if ( length < 0 ) {
|
||||||
|
throw new SQLException( "Length must be great-than-or-equal to zero." );
|
||||||
|
}
|
||||||
|
return getSubString( start-1, length );
|
||||||
}
|
}
|
||||||
if ( "free".equals( method.getName() ) ) {
|
if ( "free".equals( methodName ) && argCount == 0 ) {
|
||||||
reader.close();
|
reader.close();
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
if ( "toString".equals( method.getName() ) ) {
|
if ( "toString".equals( methodName ) && argCount == 0 ) {
|
||||||
return this.toString();
|
return this.toString();
|
||||||
}
|
}
|
||||||
if ( "equals".equals( method.getName() ) ) {
|
if ( "equals".equals( methodName ) && argCount == 1 ) {
|
||||||
return Boolean.valueOf( proxy == args[0] );
|
return Boolean.valueOf( proxy == args[0] );
|
||||||
}
|
}
|
||||||
if ( "hashCode".equals( method.getName() ) ) {
|
if ( "hashCode".equals( methodName ) && argCount == 0 ) {
|
||||||
return new Integer( this.hashCode() );
|
return new Integer( this.hashCode() );
|
||||||
}
|
}
|
||||||
|
|
||||||
throw new UnsupportedOperationException( "Clob may not be manipulated from creating session" );
|
throw new UnsupportedOperationException( "Clob may not be manipulated from creating session" );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -38,6 +38,13 @@ public interface BinaryStream {
|
||||||
*/
|
*/
|
||||||
public InputStream getInputStream();
|
public InputStream getInputStream();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Access to the bytes.
|
||||||
|
*
|
||||||
|
* @return The bytes.
|
||||||
|
*/
|
||||||
|
public byte[] getBytes();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieve the length of the input stream
|
* Retrieve the length of the input stream
|
||||||
*
|
*
|
||||||
|
|
|
@ -33,17 +33,21 @@ import org.hibernate.type.descriptor.BinaryStream;
|
||||||
*
|
*
|
||||||
* @author Steve Ebersole
|
* @author Steve Ebersole
|
||||||
*/
|
*/
|
||||||
public class BinaryStreamImpl implements BinaryStream {
|
public class BinaryStreamImpl extends ByteArrayInputStream implements BinaryStream {
|
||||||
private final ByteArrayInputStream stream;
|
|
||||||
private final int length;
|
private final int length;
|
||||||
|
|
||||||
public BinaryStreamImpl(byte[] bytes) {
|
public BinaryStreamImpl(byte[] bytes) {
|
||||||
this.stream = new ByteArrayInputStream( bytes );
|
super( bytes );
|
||||||
this.length = bytes.length;
|
this.length = bytes.length;
|
||||||
}
|
}
|
||||||
|
|
||||||
public InputStream getInputStream() {
|
public InputStream getInputStream() {
|
||||||
return stream;
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public byte[] getBytes() {
|
||||||
|
// from ByteArrayInputStream
|
||||||
|
return buf;
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getLength() {
|
public int getLength() {
|
||||||
|
|
|
@ -27,11 +27,13 @@ import java.io.ByteArrayOutputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.io.Reader;
|
import java.io.Reader;
|
||||||
|
import java.io.StringReader;
|
||||||
|
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
import org.hibernate.HibernateException;
|
import org.hibernate.HibernateException;
|
||||||
|
import org.hibernate.type.descriptor.BinaryStream;
|
||||||
import org.hibernate.util.ReflectHelper;
|
import org.hibernate.util.ReflectHelper;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -84,7 +86,46 @@ public class DataHelper {
|
||||||
return stringBuilder.toString();
|
return stringBuilder.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static String extractString(Reader characterStream, long start, int length) {
|
||||||
|
StringBuilder stringBuilder = new StringBuilder( length );
|
||||||
|
try {
|
||||||
|
long skipped = characterStream.skip( start - 1 );
|
||||||
|
if ( skipped != start - 1 ) {
|
||||||
|
throw new HibernateException( "Unable to skip needed bytes" );
|
||||||
|
}
|
||||||
|
char[] buffer = new char[2048];
|
||||||
|
int charsRead = 0;
|
||||||
|
while ( true ) {
|
||||||
|
int amountRead = characterStream.read( buffer, 0, buffer.length );
|
||||||
|
if ( amountRead == -1 ) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
stringBuilder.append( buffer, 0, amountRead );
|
||||||
|
if ( amountRead < buffer.length ) {
|
||||||
|
// we have read up to the end of stream
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
charsRead += amountRead;
|
||||||
|
if ( charsRead >= length ) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch ( IOException ioe ) {
|
||||||
|
throw new HibernateException( "IOException occurred reading a binary value", ioe );
|
||||||
|
}
|
||||||
|
return stringBuilder.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Object subStream(Reader characterStream, long start, int length) {
|
||||||
|
return new StringReader( extractString( characterStream, start, length ) );
|
||||||
|
}
|
||||||
|
|
||||||
public static byte[] extractBytes(InputStream inputStream) {
|
public static byte[] extractBytes(InputStream inputStream) {
|
||||||
|
if ( BinaryStream.class.isInstance( inputStream ) ) {
|
||||||
|
return ( (BinaryStream ) inputStream ).getBytes();
|
||||||
|
}
|
||||||
|
|
||||||
// read the stream contents into a buffer and return the complete byte[]
|
// read the stream contents into a buffer and return the complete byte[]
|
||||||
ByteArrayOutputStream outputStream = new ByteArrayOutputStream(2048);
|
ByteArrayOutputStream outputStream = new ByteArrayOutputStream(2048);
|
||||||
try {
|
try {
|
||||||
|
@ -116,4 +157,47 @@ public class DataHelper {
|
||||||
}
|
}
|
||||||
return outputStream.toByteArray();
|
return outputStream.toByteArray();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static byte[] extractBytes(InputStream inputStream, long position, int length) {
|
||||||
|
if ( BinaryStream.class.isInstance( inputStream ) && Integer.MAX_VALUE > position ) {
|
||||||
|
byte[] data = ( (BinaryStream ) inputStream ).getBytes();
|
||||||
|
int size = Math.min( length, data.length );
|
||||||
|
byte[] result = new byte[size];
|
||||||
|
System.arraycopy( data, (int) position, result, 0, size );
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
ByteArrayOutputStream outputStream = new ByteArrayOutputStream( length );
|
||||||
|
try {
|
||||||
|
long skipped = inputStream.skip( position - 1 );
|
||||||
|
if ( skipped != position - 1 ) {
|
||||||
|
throw new HibernateException( "Unable to skip needed bytes" );
|
||||||
|
}
|
||||||
|
byte[] buffer = new byte[2048];
|
||||||
|
int bytesRead = 0;
|
||||||
|
while ( true ) {
|
||||||
|
int amountRead = inputStream.read( buffer );
|
||||||
|
if ( amountRead == -1 ) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
outputStream.write( buffer, 0, amountRead );
|
||||||
|
if ( amountRead < buffer.length ) {
|
||||||
|
// we have read up to the end of stream
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
bytesRead += amountRead;
|
||||||
|
if ( bytesRead >= length ) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch ( IOException ioe ) {
|
||||||
|
throw new HibernateException( "IOException occurred reading a binary value", ioe );
|
||||||
|
}
|
||||||
|
return outputStream.toByteArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static InputStream subStream(InputStream inputStream, long start, int length) {
|
||||||
|
return new BinaryStreamImpl( extractBytes( inputStream, start, length ) );
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -56,6 +56,11 @@ public class BlobTypeDescriptor implements SqlTypeDescriptor {
|
||||||
final BinaryStream binaryStream = javaTypeDescriptor.unwrap( value, BinaryStream.class, options );
|
final BinaryStream binaryStream = javaTypeDescriptor.unwrap( value, BinaryStream.class, options );
|
||||||
st.setBinaryStream( index, binaryStream.getInputStream(), binaryStream.getLength() );
|
st.setBinaryStream( index, binaryStream.getInputStream(), binaryStream.getLength() );
|
||||||
}
|
}
|
||||||
|
else if ( byte[].class.isInstance( value ) ) {
|
||||||
|
// performance shortcut for binding BLOB data in byte[] format
|
||||||
|
final byte[] bytes = (byte[]) value;
|
||||||
|
st.setBytes( index, bytes );
|
||||||
|
}
|
||||||
else {
|
else {
|
||||||
st.setBlob( index, javaTypeDescriptor.unwrap( value, Blob.class, options ) );
|
st.setBlob( index, javaTypeDescriptor.unwrap( value, Blob.class, options ) );
|
||||||
}
|
}
|
||||||
|
|
|
@ -45,14 +45,16 @@ import org.hibernate.engine.jdbc.NClobImplementer;
|
||||||
* @author Steve Ebersole
|
* @author Steve Ebersole
|
||||||
*/
|
*/
|
||||||
public class JdbcSupportTest extends TestCase {
|
public class JdbcSupportTest extends TestCase {
|
||||||
public void testLobCreator() throws ClassNotFoundException, SQLException {
|
private static class LobCreationContextImpl implements LobCreationContext {
|
||||||
final LobCreationContext lobCreationContext = new LobCreationContext() {
|
public Object execute(Callback callback) {
|
||||||
public Object execute(Callback callback) {
|
fail( "Unexpected call to getConnection" );
|
||||||
fail( "Unexpeted call to getConnection" );
|
return null;
|
||||||
return null;
|
}
|
||||||
}
|
}
|
||||||
};
|
|
||||||
|
|
||||||
|
private LobCreationContextImpl lobCreationContext = new LobCreationContextImpl();
|
||||||
|
|
||||||
|
public void testLobCreator() throws ClassNotFoundException, SQLException {
|
||||||
LobCreator lobCreator = JdbcSupportLoader.loadJdbcSupport( null ).getLobCreator( lobCreationContext );
|
LobCreator lobCreator = JdbcSupportLoader.loadJdbcSupport( null ).getLobCreator( lobCreationContext );
|
||||||
|
|
||||||
Blob blob = lobCreator.createBlob( new byte[] {} );
|
Blob blob = lobCreator.createBlob( new byte[] {} );
|
||||||
|
@ -69,6 +71,20 @@ public class JdbcSupportTest extends TestCase {
|
||||||
assertTrue( nclob instanceof NClobImplementer );
|
assertTrue( nclob instanceof NClobImplementer );
|
||||||
nclob = lobCreator.wrap( nclob );
|
nclob = lobCreator.wrap( nclob );
|
||||||
assertTrue( nclob instanceof WrappedClob );
|
assertTrue( nclob instanceof WrappedClob );
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testLobAccess() throws SQLException {
|
||||||
|
LobCreator lobCreator = JdbcSupportLoader.loadJdbcSupport( null ).getLobCreator( lobCreationContext );
|
||||||
|
|
||||||
|
Blob blob = lobCreator.createBlob( "Hi".getBytes() );
|
||||||
|
assertEquals( 2, blob.length() );
|
||||||
|
assertEquals( 2, blob.getBytes( 1, 5 ).length );
|
||||||
|
blob.getBinaryStream();
|
||||||
|
|
||||||
|
Clob clob = lobCreator.createClob( "Hi" );
|
||||||
|
assertEquals( 2, clob.length() );
|
||||||
|
assertEquals( 2, clob.getSubString( 1, 5 ).length() );
|
||||||
|
clob.getCharacterStream();
|
||||||
|
clob.getAsciiStream();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue