HHH-7889 Cleanup inconsistencies in Blob, Clob, and NClob type

This commit is contained in:
Brett Meyer 2013-02-14 17:12:47 -05:00
parent cbacb9c8d9
commit 328986f392
5 changed files with 213 additions and 110 deletions

View File

@ -23,14 +23,17 @@
*/ */
package org.hibernate.type.descriptor.java; package org.hibernate.type.descriptor.java;
import java.io.Reader;
import java.io.Serializable; import java.io.Serializable;
import java.sql.Clob; import java.sql.Clob;
import java.sql.SQLException;
import java.util.Comparator; import java.util.Comparator;
import org.hibernate.HibernateException;
import org.hibernate.engine.jdbc.CharacterStream;
import org.hibernate.engine.jdbc.ClobImplementer; 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.engine.jdbc.CharacterStream;
import org.hibernate.engine.jdbc.internal.CharacterStreamImpl; import org.hibernate.engine.jdbc.internal.CharacterStreamImpl;
import org.hibernate.type.descriptor.WrapperOptions; import org.hibernate.type.descriptor.WrapperOptions;
@ -95,29 +98,33 @@ public class ClobTypeDescriptor extends AbstractTypeDescriptor<Clob> {
@SuppressWarnings({ "unchecked" }) @SuppressWarnings({ "unchecked" })
public <X> X unwrap(final Clob value, Class<X> type, WrapperOptions options) { public <X> X unwrap(final Clob value, Class<X> type, WrapperOptions options) {
if ( ! ( Clob.class.isAssignableFrom( type ) || CharacterStream.class.isAssignableFrom( type ) ) ) {
throw unknownUnwrap( type );
}
if ( value == null ) { if ( value == null ) {
return null; return null;
} }
if ( CharacterStream.class.isAssignableFrom( type ) ) { try {
if ( ClobImplementer.class.isInstance( value ) ) { if ( CharacterStream.class.isAssignableFrom( type ) ) {
// if the incoming Clob is a wrapper, just pass along its CharacterStream if ( ClobImplementer.class.isInstance( value ) ) {
return (X) ( (ClobImplementer) value ).getUnderlyingStream(); // if the incoming Clob is a wrapper, just pass along its BinaryStream
return (X) ( (ClobImplementer) value ).getUnderlyingStream();
}
else {
// otherwise we need to build a BinaryStream...
return (X) new CharacterStreamImpl( DataHelper.extractString( value.getCharacterStream() ) );
}
} }
else { else if (Clob.class.isAssignableFrom( type )) {
// otherwise we need to build one... final Clob clob = WrappedClob.class.isInstance( value )
return (X) new CharacterStreamImpl( DataHelper.extractString( value ) ); ? ( (WrappedClob) value ).getWrappedClob()
: value;
return (X) clob;
} }
} }
catch ( SQLException e ) {
throw new HibernateException( "Unable to access clob stream", e );
}
final Clob clob = WrappedClob.class.isInstance( value ) throw unknownUnwrap( type );
? ( (WrappedClob) value ).getWrappedClob()
: value;
return (X) clob;
} }
public <X> Clob wrap(X value, WrapperOptions options) { public <X> Clob wrap(X value, WrapperOptions options) {
@ -125,10 +132,16 @@ public class ClobTypeDescriptor extends AbstractTypeDescriptor<Clob> {
return null; return null;
} }
if ( ! Clob.class.isAssignableFrom( value.getClass() ) ) { // Support multiple return types from
throw unknownWrap( value.getClass() ); // org.hibernate.type.descriptor.sql.ClobTypeDescriptor
if ( Clob.class.isAssignableFrom( value.getClass() ) ) {
return options.getLobCreator().wrap( (Clob) value );
}
else if ( Reader.class.isAssignableFrom( value.getClass() ) ) {
Reader reader = (Reader) value;
return options.getLobCreator().createClob( DataHelper.extractString( reader ) );
} }
return options.getLobCreator().wrap( (Clob) value ); throw unknownWrap( value.getClass() );
} }
} }

View File

@ -23,10 +23,13 @@
*/ */
package org.hibernate.type.descriptor.java; package org.hibernate.type.descriptor.java;
import java.io.Reader;
import java.io.Serializable; import java.io.Serializable;
import java.sql.NClob; import java.sql.NClob;
import java.sql.SQLException;
import java.util.Comparator; import java.util.Comparator;
import org.hibernate.HibernateException;
import org.hibernate.engine.jdbc.CharacterStream; import org.hibernate.engine.jdbc.CharacterStream;
import org.hibernate.engine.jdbc.NClobImplementer; import org.hibernate.engine.jdbc.NClobImplementer;
import org.hibernate.engine.jdbc.NClobProxy; import org.hibernate.engine.jdbc.NClobProxy;
@ -95,29 +98,33 @@ public class NClobTypeDescriptor extends AbstractTypeDescriptor<NClob> {
@SuppressWarnings({ "unchecked" }) @SuppressWarnings({ "unchecked" })
public <X> X unwrap(final NClob value, Class<X> type, WrapperOptions options) { public <X> X unwrap(final NClob value, Class<X> type, WrapperOptions options) {
if ( ! ( NClob.class.isAssignableFrom( type ) || CharacterStream.class.isAssignableFrom( type ) ) ) {
throw unknownUnwrap( type );
}
if ( value == null ) { if ( value == null ) {
return null; return null;
} }
if ( CharacterStream.class.isAssignableFrom( type ) ) { try {
if ( NClobImplementer.class.isInstance( value ) ) { if ( CharacterStream.class.isAssignableFrom( type ) ) {
// if the incoming Clob is a wrapper, just pass along its CharacterStream if ( NClobImplementer.class.isInstance( value ) ) {
return (X) ( (NClobImplementer) value ).getUnderlyingStream(); // if the incoming NClob is a wrapper, just pass along its BinaryStream
return (X) ( (NClobImplementer) value ).getUnderlyingStream();
}
else {
// otherwise we need to build a BinaryStream...
return (X) new CharacterStreamImpl( DataHelper.extractString( value.getCharacterStream() ) );
}
} }
else { else if (NClob.class.isAssignableFrom( type )) {
// otherwise we need to build one... final NClob nclob = WrappedNClob.class.isInstance( value )
return (X) new CharacterStreamImpl( DataHelper.extractString( value ) ); ? ( (WrappedNClob) value ).getWrappedNClob()
: value;
return (X) nclob;
} }
} }
catch ( SQLException e ) {
throw new HibernateException( "Unable to access nclob stream", e );
}
final NClob clob = WrappedNClob.class.isInstance( value ) throw unknownUnwrap( type );
? ( (WrappedNClob) value ).getWrappedNClob()
: value;
return (X) clob;
} }
public <X> NClob wrap(X value, WrapperOptions options) { public <X> NClob wrap(X value, WrapperOptions options) {
@ -125,10 +132,16 @@ public class NClobTypeDescriptor extends AbstractTypeDescriptor<NClob> {
return null; return null;
} }
if ( ! NClob.class.isAssignableFrom( value.getClass() ) ) { // Support multiple return types from
throw unknownWrap( value.getClass() ); // org.hibernate.type.descriptor.sql.ClobTypeDescriptor
if ( NClob.class.isAssignableFrom( value.getClass() ) ) {
return options.getLobCreator().wrap( (NClob) value );
}
else if ( Reader.class.isAssignableFrom( value.getClass() ) ) {
Reader reader = (Reader) value;
return options.getLobCreator().createNClob( DataHelper.extractString( reader ) );
} }
return options.getLobCreator().wrap( (NClob) value ); throw unknownWrap( value.getClass() );
} }
} }

View File

@ -91,18 +91,18 @@ public abstract class BlobTypeDescriptor implements SqlTypeDescriptor {
@Override @Override
public <X> BasicExtractor<X> getBlobExtractor(final JavaTypeDescriptor<X> javaTypeDescriptor) { public <X> BasicExtractor<X> getBlobExtractor(final JavaTypeDescriptor<X> javaTypeDescriptor) {
return new BasicExtractor<X>( javaTypeDescriptor, this ) { return new BasicExtractor<X>( javaTypeDescriptor, this ) {
// For now, default to using getBlob. If extraction
// should also check useStreamForLobBinding, add
// checks here and use STREAM_BINDING.
@Override @Override
protected X doExtract(ResultSet rs, String name, WrapperOptions options) throws SQLException { protected X doExtract(ResultSet rs, String name, WrapperOptions options) throws SQLException {
return BLOB_BINDING.getExtractor( javaTypeDescriptor ).doExtract( rs, name, options ); return getBinding( options ).getExtractor( javaTypeDescriptor ).doExtract( rs, name, options );
} }
}; };
} }
}; };
private static final BlobTypeDescriptor getBinding( WrapperOptions options ) {
return options.useStreamForLobBinding() ? STREAM_BINDING : BLOB_BINDING;
}
public static final BlobTypeDescriptor PRIMITIVE_ARRAY_BINDING = public static final BlobTypeDescriptor PRIMITIVE_ARRAY_BINDING =
new BlobTypeDescriptor() { new BlobTypeDescriptor() {
@Override @Override
@ -175,4 +175,5 @@ public abstract class BlobTypeDescriptor implements SqlTypeDescriptor {
}; };
} }
}; };
} }

View File

@ -23,6 +23,7 @@
*/ */
package org.hibernate.type.descriptor.sql; package org.hibernate.type.descriptor.sql;
import java.sql.CallableStatement;
import java.sql.Clob; import java.sql.Clob;
import java.sql.PreparedStatement; import java.sql.PreparedStatement;
import java.sql.ResultSet; import java.sql.ResultSet;
@ -39,59 +40,10 @@ import org.hibernate.type.descriptor.java.JavaTypeDescriptor;
* Descriptor for {@link Types#CLOB CLOB} handling. * Descriptor for {@link Types#CLOB CLOB} handling.
* *
* @author Steve Ebersole * @author Steve Ebersole
* @author Gail Badner
*/ */
public abstract class ClobTypeDescriptor implements SqlTypeDescriptor { public abstract class ClobTypeDescriptor implements SqlTypeDescriptor {
@Override
public static final ClobTypeDescriptor DEFAULT =
new ClobTypeDescriptor() {
public <X> BasicBinder<X> getClobBinder(final JavaTypeDescriptor<X> javaTypeDescriptor) {
return new BasicBinder<X>( javaTypeDescriptor, this ) {
@Override
protected void doBind(PreparedStatement st, X value, int index, WrapperOptions options) throws SQLException {
if ( options.useStreamForLobBinding() ) {
STREAM_BINDING.getClobBinder( javaTypeDescriptor ).doBind( st, value, index, options );
}
else {
CLOB_BINDING.getClobBinder( javaTypeDescriptor ).doBind( st, value, index, options );
}
}
};
}
};
public static final ClobTypeDescriptor CLOB_BINDING =
new ClobTypeDescriptor() {
public <X> BasicBinder<X> getClobBinder(final JavaTypeDescriptor<X> javaTypeDescriptor) {
return new BasicBinder<X>( javaTypeDescriptor, this ) {
@Override
protected void doBind(PreparedStatement st, X value, int index, WrapperOptions options)
throws SQLException {
st.setClob( index, javaTypeDescriptor.unwrap( value, Clob.class, options ) );
}
};
}
};
public static final ClobTypeDescriptor STREAM_BINDING =
new ClobTypeDescriptor() {
public <X> BasicBinder<X> getClobBinder(final JavaTypeDescriptor<X> javaTypeDescriptor) {
return new BasicBinder<X>( javaTypeDescriptor, this ) {
@Override
protected void doBind(PreparedStatement st, X value, int index, WrapperOptions options)
throws SQLException {
final CharacterStream characterStream = javaTypeDescriptor.unwrap( value, CharacterStream.class, options );
st.setCharacterStream( index, characterStream.asReader(), characterStream.getLength() );
}
};
}
};
protected abstract <X> BasicBinder<X> getClobBinder(final JavaTypeDescriptor<X> javaTypeDescriptor);
public <X> ValueBinder<X> getBinder(final JavaTypeDescriptor<X> javaTypeDescriptor) {
return getClobBinder( javaTypeDescriptor );
}
public int getSqlType() { public int getSqlType() {
return Types.CLOB; return Types.CLOB;
} }
@ -101,12 +53,99 @@ public abstract class ClobTypeDescriptor implements SqlTypeDescriptor {
return true; return true;
} }
public <X> ValueExtractor<X> getExtractor(final JavaTypeDescriptor<X> javaTypeDescriptor) { protected abstract <X> BasicExtractor<X> getClobExtractor(JavaTypeDescriptor<X> javaTypeDescriptor);
return new BasicExtractor<X>( javaTypeDescriptor, this ) {
@Override @Override
protected X doExtract(ResultSet rs, String name, WrapperOptions options) throws SQLException { public <X> ValueExtractor<X> getExtractor(JavaTypeDescriptor<X> javaTypeDescriptor) {
return javaTypeDescriptor.wrap( rs.getClob( name ), options ); return getClobExtractor( javaTypeDescriptor );
} }
};
protected abstract <X> BasicBinder<X> getClobBinder(JavaTypeDescriptor<X> javaTypeDescriptor);
@Override
public <X> ValueBinder<X> getBinder(JavaTypeDescriptor<X> javaTypeDescriptor) {
return getClobBinder( javaTypeDescriptor );
} }
public static final ClobTypeDescriptor DEFAULT =
new ClobTypeDescriptor() {
@Override
public <X> BasicBinder<X> getClobBinder(final JavaTypeDescriptor<X> javaTypeDescriptor) {
return new BasicBinder<X>( javaTypeDescriptor, this ) {
@Override
protected void doBind(PreparedStatement st, X value, int index, WrapperOptions options) throws SQLException {
getBinding( options ).getClobBinder( javaTypeDescriptor ).doBind( st, value, index, options );
}
};
}
@Override
public <X> BasicExtractor<X> getClobExtractor(final JavaTypeDescriptor<X> javaTypeDescriptor) {
return new BasicExtractor<X>( javaTypeDescriptor, this ) {
@Override
protected X doExtract(ResultSet rs, String name, WrapperOptions options) throws SQLException {
return getBinding( options ).getClobExtractor( javaTypeDescriptor ).doExtract( rs, name, options );
}
};
}
};
private static final ClobTypeDescriptor getBinding( WrapperOptions options ) {
return options.useStreamForLobBinding() ? STREAM_BINDING : CLOB_BINDING;
}
public static final ClobTypeDescriptor CLOB_BINDING =
new ClobTypeDescriptor() {
@Override
public <X> BasicBinder<X> getClobBinder(final JavaTypeDescriptor<X> javaTypeDescriptor) {
return new BasicBinder<X>( javaTypeDescriptor, this ) {
@Override
protected void doBind(PreparedStatement st, X value, int index, WrapperOptions options)
throws SQLException {
st.setClob( index, javaTypeDescriptor.unwrap( value, Clob.class, options ) );
}
};
}
@Override
public <X> BasicExtractor<X> getClobExtractor(final JavaTypeDescriptor<X> javaTypeDescriptor) {
return new BasicExtractor<X>(javaTypeDescriptor, this) {
@Override
protected X doExtract(ResultSet rs, String name, WrapperOptions options) throws SQLException {
return javaTypeDescriptor.wrap( rs.getClob( name ), options );
}
};
};
};
public static final ClobTypeDescriptor STREAM_BINDING =
new ClobTypeDescriptor() {
@Override
public <X> BasicBinder<X> getClobBinder(final JavaTypeDescriptor<X> javaTypeDescriptor) {
return new BasicBinder<X>( javaTypeDescriptor, this ) {
@Override
protected void doBind(PreparedStatement st, X value, int index, WrapperOptions options)
throws SQLException {
final CharacterStream characterStream = javaTypeDescriptor.unwrap( value, CharacterStream.class, options );
st.setCharacterStream( index, characterStream.asReader(), characterStream.getLength() );
}
};
}
@Override
public <X> BasicExtractor<X> getClobExtractor(final JavaTypeDescriptor<X> javaTypeDescriptor) {
return new BasicExtractor<X>(javaTypeDescriptor, this) {
@Override
protected X doExtract(ResultSet rs, String name, WrapperOptions options) throws SQLException {
return javaTypeDescriptor.wrap( rs.getCharacterStream( name ), options );
}
};
};
};
} }

View File

@ -23,6 +23,7 @@
*/ */
package org.hibernate.type.descriptor.sql; package org.hibernate.type.descriptor.sql;
import java.sql.CallableStatement;
import java.sql.NClob; import java.sql.NClob;
import java.sql.PreparedStatement; import java.sql.PreparedStatement;
import java.sql.ResultSet; import java.sql.ResultSet;
@ -42,27 +43,62 @@ import org.hibernate.type.descriptor.java.JavaTypeDescriptor;
* @author Gail Badner * @author Gail Badner
*/ */
public abstract class NClobTypeDescriptor implements SqlTypeDescriptor { public abstract class NClobTypeDescriptor implements SqlTypeDescriptor {
@Override
public int getSqlType() {
return Types.NCLOB;
}
@Override
public boolean canBeRemapped() {
return true;
}
protected abstract <X> BasicExtractor<X> getNClobExtractor(JavaTypeDescriptor<X> javaTypeDescriptor);
@Override
public <X> ValueExtractor<X> getExtractor(JavaTypeDescriptor<X> javaTypeDescriptor) {
return getNClobExtractor( javaTypeDescriptor );
}
protected abstract <X> BasicBinder<X> getNClobBinder(JavaTypeDescriptor<X> javaTypeDescriptor);
@Override
public <X> ValueBinder<X> getBinder(JavaTypeDescriptor<X> javaTypeDescriptor) {
return getNClobBinder( javaTypeDescriptor );
}
public static final NClobTypeDescriptor DEFAULT = public static final NClobTypeDescriptor DEFAULT =
new NClobTypeDescriptor() { new NClobTypeDescriptor() {
public <X> BasicBinder<X> getNClobBinder(final JavaTypeDescriptor<X> javaTypeDescriptor) { @Override
public <X> BasicBinder<X> getNClobBinder(final JavaTypeDescriptor<X> javaTypeDescriptor) {
return new BasicBinder<X>( javaTypeDescriptor, this ) { return new BasicBinder<X>( javaTypeDescriptor, this ) {
@Override @Override
protected void doBind(PreparedStatement st, X value, int index, WrapperOptions options) throws SQLException { protected void doBind(PreparedStatement st, X value, int index, WrapperOptions options) throws SQLException {
if ( options.useStreamForLobBinding() ) { getBinding( options ).getNClobBinder( javaTypeDescriptor ).doBind( st, value, index, options );
STREAM_BINDING.getNClobBinder( javaTypeDescriptor ).doBind( st, value, index, options );
}
else {
CLOB_BINDING.getNClobBinder( javaTypeDescriptor ).doBind( st, value, index, options );
}
} }
}; };
} }
@Override
public <X> BasicExtractor<X> getNClobExtractor(final JavaTypeDescriptor<X> javaTypeDescriptor) {
return new BasicExtractor<X>( javaTypeDescriptor, this ) {
@Override
protected X doExtract(ResultSet rs, String name, WrapperOptions options) throws SQLException {
return getBinding( options ).getNClobExtractor( javaTypeDescriptor ).doExtract( rs, name, options );
}
};
}
}; };
public static final NClobTypeDescriptor CLOB_BINDING = private static final NClobTypeDescriptor getBinding( WrapperOptions options ) {
return options.useStreamForLobBinding() ? STREAM_BINDING : NCLOB_BINDING;
}
public static final NClobTypeDescriptor NCLOB_BINDING =
new NClobTypeDescriptor() { new NClobTypeDescriptor() {
public <X> BasicBinder<X> getNClobBinder(final JavaTypeDescriptor<X> javaTypeDescriptor) { @Override
public <X> BasicBinder<X> getNClobBinder(final JavaTypeDescriptor<X> javaTypeDescriptor) {
return new BasicBinder<X>( javaTypeDescriptor, this ) { return new BasicBinder<X>( javaTypeDescriptor, this ) {
@Override @Override
protected void doBind(PreparedStatement st, X value, int index, WrapperOptions options) protected void doBind(PreparedStatement st, X value, int index, WrapperOptions options)
@ -71,11 +107,24 @@ public abstract class NClobTypeDescriptor implements SqlTypeDescriptor {
} }
}; };
} }
@Override
public <X> BasicExtractor<X> getNClobExtractor(final JavaTypeDescriptor<X> javaTypeDescriptor) {
return new BasicExtractor<X>(javaTypeDescriptor, this) {
@Override
protected X doExtract(ResultSet rs, String name, WrapperOptions options) throws SQLException {
return javaTypeDescriptor.wrap( rs.getNClob( name ), options );
}
};
};
}; };
public static final NClobTypeDescriptor STREAM_BINDING = public static final NClobTypeDescriptor STREAM_BINDING =
new NClobTypeDescriptor() { new NClobTypeDescriptor() {
public <X> BasicBinder<X> getNClobBinder(final JavaTypeDescriptor<X> javaTypeDescriptor) { @Override
public <X> BasicBinder<X> getNClobBinder(final JavaTypeDescriptor<X> javaTypeDescriptor) {
return new BasicBinder<X>( javaTypeDescriptor, this ) { return new BasicBinder<X>( javaTypeDescriptor, this ) {
@Override @Override
protected void doBind(PreparedStatement st, X value, int index, WrapperOptions options) protected void doBind(PreparedStatement st, X value, int index, WrapperOptions options)
@ -85,29 +134,17 @@ public abstract class NClobTypeDescriptor implements SqlTypeDescriptor {
} }
}; };
} }
@Override
public <X> BasicExtractor<X> getNClobExtractor(final JavaTypeDescriptor<X> javaTypeDescriptor) {
return new BasicExtractor<X>(javaTypeDescriptor, this) {
@Override
protected X doExtract(ResultSet rs, String name, WrapperOptions options) throws SQLException {
return javaTypeDescriptor.wrap( rs.getCharacterStream( name ), options );
}
};
};
}; };
protected abstract <X> BasicBinder<X> getNClobBinder(final JavaTypeDescriptor<X> javaTypeDescriptor);
public <X> ValueBinder<X> getBinder(final JavaTypeDescriptor<X> javaTypeDescriptor) {
return getNClobBinder( javaTypeDescriptor );
}
public int getSqlType() {
return Types.CLOB;
}
@Override
public boolean canBeRemapped() {
return true;
}
public <X> ValueExtractor<X> getExtractor(final JavaTypeDescriptor<X> javaTypeDescriptor) {
return new BasicExtractor<X>( javaTypeDescriptor, this ) {
@Override
protected X doExtract(ResultSet rs, String name, WrapperOptions options) throws SQLException {
return javaTypeDescriptor.wrap( rs.getNClob( name ), options );
}
};
}
} }