HHH-5659 HHH-4617 : PostgreSQL and Oracle LOBs

This commit is contained in:
Gail Badner 2011-02-02 00:35:01 -08:00
parent 600c29170a
commit 8e832d82a3
23 changed files with 620 additions and 86 deletions

View File

@ -42,9 +42,10 @@ import org.hibernate.engine.jdbc.LobCreator;
import org.hibernate.engine.jdbc.StreamUtils; import org.hibernate.engine.jdbc.StreamUtils;
import org.hibernate.intercept.FieldInterceptionHelper; import org.hibernate.intercept.FieldInterceptionHelper;
import org.hibernate.intercept.FieldInterceptor; import org.hibernate.intercept.FieldInterceptor;
import org.hibernate.mapping.ManyToOne;
import org.hibernate.proxy.HibernateProxy; import org.hibernate.proxy.HibernateProxy;
import org.hibernate.proxy.LazyInitializer; import org.hibernate.proxy.LazyInitializer;
import org.hibernate.type.CharacterArrayClobType;
import org.hibernate.type.MaterializedBlobType;
import org.hibernate.type.AnyType; import org.hibernate.type.AnyType;
import org.hibernate.type.BigDecimalType; import org.hibernate.type.BigDecimalType;
import org.hibernate.type.BigIntegerType; import org.hibernate.type.BigIntegerType;
@ -66,6 +67,7 @@ import org.hibernate.type.LocaleType;
import org.hibernate.type.LongType; import org.hibernate.type.LongType;
import org.hibernate.type.ManyToOneType; import org.hibernate.type.ManyToOneType;
import org.hibernate.type.ObjectType; import org.hibernate.type.ObjectType;
import org.hibernate.type.PrimitiveCharacterArrayClobType;
import org.hibernate.type.SerializableType; import org.hibernate.type.SerializableType;
import org.hibernate.type.ShortType; import org.hibernate.type.ShortType;
import org.hibernate.type.StringType; import org.hibernate.type.StringType;
@ -76,11 +78,11 @@ import org.hibernate.type.TimestampType;
import org.hibernate.type.TrueFalseType; import org.hibernate.type.TrueFalseType;
import org.hibernate.type.Type; import org.hibernate.type.Type;
import org.hibernate.type.TypeFactory; import org.hibernate.type.TypeFactory;
import org.hibernate.type.WrappedMaterializedBlobType;
import org.hibernate.type.YesNoType; import org.hibernate.type.YesNoType;
import org.hibernate.type.CharArrayType; import org.hibernate.type.CharArrayType;
import org.hibernate.type.WrapperBinaryType; import org.hibernate.type.WrapperBinaryType;
import org.hibernate.type.CharacterArrayType; import org.hibernate.type.CharacterArrayType;
import org.hibernate.type.MaterializedBlobType;
import org.hibernate.type.ImageType; import org.hibernate.type.ImageType;
import org.hibernate.type.MaterializedClobType; import org.hibernate.type.MaterializedClobType;
import org.hibernate.usertype.CompositeUserType; import org.hibernate.usertype.CompositeUserType;
@ -236,6 +238,25 @@ public final class Hibernate {
* @deprecated Use {@link org.hibernate.type.StandardBasicTypes#CLOB} instead. * @deprecated Use {@link org.hibernate.type.StandardBasicTypes#CLOB} instead.
*/ */
public static final ClobType CLOB = ClobType.INSTANCE; public static final ClobType CLOB = ClobType.INSTANCE;
/**
* Hibernate <tt>wrapper_materialized_blob</tt> type.
* @deprecated Use {@link org.hibernate.type.StandardBasicTypes#WRAPPER_MATERIALIZED_BLOB} instead.
*/
public static final WrappedMaterializedBlobType WRAPPER_MATERIALIZED_BLOB = WrappedMaterializedBlobType.INSTANCE;
/**
* Hibernate <tt>wrapper_characters_clob</tt> type.
* @deprecated Use {@link org.hibernate.type.StandardBasicTypes#WRAPPER_CHARACTERS_CLOB} instead.
*/
public static final CharacterArrayClobType WRAPPER_CHARACTERS_CLOB = CharacterArrayClobType.INSTANCE;
/**
* Hibernate <tt>characters_clob</tt> type.
* @deprecated Use {@link org.hibernate.type.StandardBasicTypes#CHARACTERS_CLOB} instead.
*/
public static final PrimitiveCharacterArrayClobType CHARACTERS_CLOB = PrimitiveCharacterArrayClobType.INSTANCE;
/** /**
* Hibernate <tt>calendar</tt> type. * Hibernate <tt>calendar</tt> type.
* @deprecated Use {@link org.hibernate.type.StandardBasicTypes#CALENDAR} instead. * @deprecated Use {@link org.hibernate.type.StandardBasicTypes#CALENDAR} instead.

View File

@ -182,13 +182,13 @@ public class SimpleValueBinder {
type = Hibernate.MATERIALIZED_CLOB.getName(); type = Hibernate.MATERIALIZED_CLOB.getName();
} }
else if ( mappings.getReflectionManager().equals( returnedClassOrElement, Character.class ) && isArray ) { else if ( mappings.getReflectionManager().equals( returnedClassOrElement, Character.class ) && isArray ) {
type = CharacterArrayClobType.class.getName(); type = Hibernate.WRAPPER_CHARACTERS_CLOB.getName();
} }
else if ( mappings.getReflectionManager().equals( returnedClassOrElement, char.class ) && isArray ) { else if ( mappings.getReflectionManager().equals( returnedClassOrElement, char.class ) && isArray ) {
type = PrimitiveCharacterArrayClobType.class.getName(); type = Hibernate.CHARACTERS_CLOB.getName();
} }
else if ( mappings.getReflectionManager().equals( returnedClassOrElement, Byte.class ) && isArray ) { else if ( mappings.getReflectionManager().equals( returnedClassOrElement, Byte.class ) && isArray ) {
type = WrappedMaterializedBlobType.class.getName(); type = Hibernate.WRAPPER_MATERIALIZED_BLOB.getName();
} }
else if ( mappings.getReflectionManager().equals( returnedClassOrElement, byte.class ) && isArray ) { else if ( mappings.getReflectionManager().equals( returnedClassOrElement, byte.class ) && isArray ) {
type = Hibernate.MATERIALIZED_BLOB.getName(); type = Hibernate.MATERIALIZED_BLOB.getName();

View File

@ -27,8 +27,11 @@ import java.sql.CallableStatement;
import java.sql.ResultSet; import java.sql.ResultSet;
import java.sql.SQLException; import java.sql.SQLException;
import java.sql.Types; import java.sql.Types;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap; import java.util.HashMap;
import java.util.HashSet; import java.util.HashSet;
import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Properties; import java.util.Properties;
import java.util.Set; import java.util.Set;
@ -66,7 +69,15 @@ import org.hibernate.sql.ANSIJoinFragment;
import org.hibernate.sql.CaseFragment; import org.hibernate.sql.CaseFragment;
import org.hibernate.sql.ForUpdateFragment; import org.hibernate.sql.ForUpdateFragment;
import org.hibernate.sql.JoinFragment; import org.hibernate.sql.JoinFragment;
import org.hibernate.type.BasicType;
import org.hibernate.type.BlobType;
import org.hibernate.type.CharacterArrayClobType;
import org.hibernate.type.ClobType;
import org.hibernate.type.MaterializedBlobType;
import org.hibernate.type.MaterializedClobType;
import org.hibernate.type.PrimitiveCharacterArrayClobType;
import org.hibernate.type.StandardBasicTypes; import org.hibernate.type.StandardBasicTypes;
import org.hibernate.type.WrappedMaterializedBlobType;
import org.hibernate.util.ReflectHelper; import org.hibernate.util.ReflectHelper;
import org.hibernate.util.StringHelper; import org.hibernate.util.StringHelper;
@ -94,14 +105,30 @@ public abstract class Dialect {
public static final String QUOTE = "`\"["; public static final String QUOTE = "`\"[";
public static final String CLOSED_QUOTE = "`\"]"; public static final String CLOSED_QUOTE = "`\"]";
private static final Set<BasicType> streamBindingLobTypes = new HashSet<BasicType>();
static {
// Blobs
streamBindingLobTypes.add( BlobType.INSTANCE.getAlternatives().getStreamBindingType() );
streamBindingLobTypes.add( MaterializedBlobType.INSTANCE.getAlternatives().getStreamBindingType() );
streamBindingLobTypes.add( WrappedMaterializedBlobType.INSTANCE.getAlternatives().getStreamBindingType() );
// Clobs
streamBindingLobTypes.add( ClobType.INSTANCE.getAlternatives().getStreamBindingType() );
streamBindingLobTypes.add( MaterializedClobType.INSTANCE.getAlternatives().getStreamBindingType() );
streamBindingLobTypes.add( CharacterArrayClobType.INSTANCE.getAlternatives().getStreamBindingType() );
streamBindingLobTypes.add( PrimitiveCharacterArrayClobType.INSTANCE.getAlternatives().getStreamBindingType() );
// TODO: shouldn't SerializableToBlobType be in this list???
}
private final TypeNames typeNames = new TypeNames(); private final TypeNames typeNames = new TypeNames();
private final TypeNames hibernateTypeNames = new TypeNames(); private final TypeNames hibernateTypeNames = new TypeNames();
private final List<BasicType> dialectSpecificOverrides = new ArrayList<BasicType>();
private final Properties properties = new Properties(); private final Properties properties = new Properties();
private final Map<String, SQLFunction> sqlFunctions = new HashMap<String, SQLFunction>(); private final Map<String, SQLFunction> sqlFunctions = new HashMap<String, SQLFunction>();
private final Set<String> sqlKeywords = new HashSet<String>(); private final Set<String> sqlKeywords = new HashSet<String>();
// constructors and factory methods ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // constructors and factory methods ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
protected Dialect() { protected Dialect() {
@ -210,6 +237,26 @@ public abstract class Dialect {
return properties; return properties;
} }
/**
* Retrieve dialect-specific types for overriding "basic" types.
* @return the dialect-specific types
*/
public final List<BasicType> getTypeOverrides() {
List<BasicType> allOverrides = dialectSpecificOverrides;
if ( useInputStreamToInsertBlob() ) {
allOverrides = new ArrayList<BasicType>( streamBindingLobTypes.size() + dialectSpecificOverrides.size() );
allOverrides.addAll( streamBindingLobTypes );
allOverrides.addAll( dialectSpecificOverrides );
}
return Collections.unmodifiableList( allOverrides );
}
protected final void addTypeOverride(BasicType typeOverride) {
if ( typeOverride != null ) {
dialectSpecificOverrides.add( typeOverride );
}
}
public String toString() { public String toString() {
return getClass().getName(); return getClass().getName();
} }

View File

@ -40,6 +40,7 @@ import org.hibernate.exception.JDBCExceptionHelper;
import org.hibernate.exception.TemplatedViolatedConstraintNameExtracter; import org.hibernate.exception.TemplatedViolatedConstraintNameExtracter;
import org.hibernate.exception.ViolatedConstraintNameExtracter; import org.hibernate.exception.ViolatedConstraintNameExtracter;
import org.hibernate.id.SequenceGenerator; import org.hibernate.id.SequenceGenerator;
import org.hibernate.type.MaterializedBlobType;
/** /**
* An SQL dialect for Postgres * An SQL dialect for Postgres
@ -143,6 +144,8 @@ public class PostgreSQLDialect extends Dialect {
registerFunction( "str", new SQLFunctionTemplate(Hibernate.STRING, "cast(?1 as varchar)") ); registerFunction( "str", new SQLFunctionTemplate(Hibernate.STRING, "cast(?1 as varchar)") );
addTypeOverride( MaterializedBlobType.INSTANCE.getAlternatives().getLobBindingType() );
getDefaultProperties().setProperty(Environment.STATEMENT_BATCH_SIZE, DEFAULT_BATCH_SIZE); getDefaultProperties().setProperty(Environment.STATEMENT_BATCH_SIZE, DEFAULT_BATCH_SIZE);
} }
@ -364,8 +367,18 @@ public class PostgreSQLDialect extends Dialect {
return false; return false;
} }
@Override
public boolean supportsExpectedLobUsagePattern() { public boolean supportsExpectedLobUsagePattern() {
// seems to have spotty LOB suppport return true;
}
@Override
public boolean supportsLobValueChangePropogation() {
return false;
}
@Override
public boolean supportsUnboundedLobLocatorMaterialization() {
return false; return false;
} }

View File

@ -0,0 +1,170 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2011, Red Hat Inc. or third-party contributors as
* indicated by the @author tags or express copyright attribution
* statements applied by the authors. All third-party contributions are
* distributed under license by Red Hat Inc.
*
* This copyrighted material is made available to anyone wishing to use, modify,
* copy, or redistribute it subject to the terms and conditions of the GNU
* Lesser General Public License, as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
* for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this distribution; if not, write to:
* Free Software Foundation, Inc.
* 51 Franklin Street, Fifth Floor
* Boston, MA 02110-1301 USA
*/
package org.hibernate.type;
import java.io.Serializable;
import java.lang.InstantiationException;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import org.hibernate.HibernateException;
import org.hibernate.type.descriptor.sql.BlobTypeDescriptor;
import org.hibernate.type.descriptor.sql.ClobTypeDescriptor;
import org.hibernate.type.descriptor.sql.SqlTypeDescriptor;
/**
* Provides alternative types for binding LOB values.
*
* @author Gail Badner
*/
public abstract class AlternativeLobTypes<S, T extends LobType<S>> implements Serializable {
private final T defaultType;
private final T streamBindingType;
private final T lobBindingType;
private AlternativeLobTypes(Class<? extends T> clazz,
SqlTypeDescriptor defaultDescriptor,
SqlTypeDescriptor streamBindingDescriptor,
SqlTypeDescriptor lobBindingDescriptor) {
Constructor constructor = getConstructor( clazz );
defaultType = createLobType( clazz, constructor, defaultDescriptor );
streamBindingType = createLobType( clazz, constructor, streamBindingDescriptor );
lobBindingType = createLobType( clazz, constructor, lobBindingDescriptor );
}
/**
* Returns the type that uses the default binding LOB values.
* @return type that uses the default binding
* @see BlobTypeDescriptor#DEFAULT
* @see ClobTypeDescriptor#DEFAULT
*/
public final T getDefaultType() {
return defaultType;
}
/**
* Returns the type that binds LOB values using streams.
*
* @return type that binds using a stream
*
* @see BlobTypeDescriptor#STREAM_BINDING
* @see ClobTypeDescriptor#STREAM_BINDING
*/
public final T getStreamBindingType() {
return streamBindingType;
}
/**
* Returns the type that explicitly binds the LOB value,
* @return type that binds the LOB
* @see BlobTypeDescriptor#BLOB_BINDING
* @see ClobTypeDescriptor#CLOB_BINDING
*/
public final T getLobBindingType() {
return lobBindingType;
}
protected Constructor getConstructor(Class<? extends T> clazz) {
try {
return clazz.getDeclaredConstructor( SqlTypeDescriptor.class, this.getClass() );
}
catch (NoSuchMethodException e) {
throw new HibernateException(
"Could not get constructor for " +
clazz.getClass().getName() +
" with argument types: [" +
SqlTypeDescriptor.class.getName() + ", " + this.getClass().getName() +
"]", e
);
}
}
protected T createLobType(Class<? extends T> lobTypeClass,
Constructor constructor,
SqlTypeDescriptor sqlTypeDescriptor) {
try {
return lobTypeClass.cast( constructor.newInstance( sqlTypeDescriptor, this ) );
}
catch ( InstantiationException e ) {
throw new HibernateException( "Cannot instantiate type: " + lobTypeClass.getName() );
}
catch ( IllegalAccessException e ) {
throw new HibernateException( "IllegalAccessException trying to instantiate: " + lobTypeClass.getName() );
}
catch (InvocationTargetException e) {
throw new HibernateException( "Could not create type: " + lobTypeClass.getName(), e.getCause() );
}
}
/**
* Provides alternative types for binding {@link java.sql.Types#CLOB CLOB} values.
*/
public static class ClobTypes<S, T extends LobType<S>> extends AlternativeLobTypes<S,T> {
/* package-protected */
ClobTypes(Class<? extends T> clobTypeClass) {
super(
clobTypeClass,
ClobTypeDescriptor.DEFAULT,
ClobTypeDescriptor.STREAM_BINDING,
ClobTypeDescriptor.CLOB_BINDING
);
}
}
/**
* Provides alternative types for binding {@link java.sql.Types#BLOB BLOB} values.
*/
public static class BlobTypes<S, T extends LobType<S>> extends AlternativeLobTypes<S,T> {
private final T primitiveArrayBindingType;
/* package-protected */
BlobTypes(Class<? extends T> blobTypeClass) {
super(
blobTypeClass,
BlobTypeDescriptor.DEFAULT,
BlobTypeDescriptor.STREAM_BINDING,
BlobTypeDescriptor.BLOB_BINDING
);
Constructor constructor = getConstructor( blobTypeClass );
primitiveArrayBindingType = createLobType(
blobTypeClass, constructor, BlobTypeDescriptor.PRIMITIVE_ARRAY_BINDING
);
}
/**
* Returns the type that explicitly binds the {@link java.sql.Types#BLOB BLOB} value,
* @return type that binds the {@link java.sql.Types#BLOB BLOB}
* @see BlobTypeDescriptor#PRIMITIVE_ARRAY_BINDING
*/
public final T getPrimitiveArrayBindingType() {
return primitiveArrayBindingType;
}
}
}

View File

@ -88,8 +88,11 @@ public class BasicTypeRegistry implements Serializable {
register( TextType.INSTANCE ); register( TextType.INSTANCE );
register( BlobType.INSTANCE ); register( BlobType.INSTANCE );
register( MaterializedBlobType.INSTANCE ); register( MaterializedBlobType.INSTANCE );
register( WrappedMaterializedBlobType.INSTANCE );
register( ClobType.INSTANCE ); register( ClobType.INSTANCE );
register( MaterializedClobType.INSTANCE ); register( MaterializedClobType.INSTANCE );
register( CharacterArrayClobType.INSTANCE );
register( PrimitiveCharacterArrayClobType.INSTANCE );
register( SerializableType.INSTANCE ); register( SerializableType.INSTANCE );
register( ObjectType.INSTANCE ); register( ObjectType.INSTANCE );

View File

@ -26,6 +26,7 @@ package org.hibernate.type;
import java.sql.Blob; import java.sql.Blob;
import org.hibernate.type.descriptor.java.BlobTypeDescriptor; import org.hibernate.type.descriptor.java.BlobTypeDescriptor;
import org.hibernate.type.descriptor.sql.SqlTypeDescriptor;
/** /**
* A type that maps between {@link java.sql.Types#BLOB BLOB} and {@link Blob} * A type that maps between {@link java.sql.Types#BLOB BLOB} and {@link Blob}
@ -33,11 +34,20 @@ import org.hibernate.type.descriptor.java.BlobTypeDescriptor;
* @author Gavin King * @author Gavin King
* @author Steve Ebersole * @author Steve Ebersole
*/ */
public class BlobType extends AbstractSingleColumnStandardBasicType<Blob> { public class BlobType extends LobType<Blob> {
public static final BlobType INSTANCE = new BlobType(); public static final BlobType INSTANCE = new BlobType();
public BlobType() { public BlobType() {
super( org.hibernate.type.descriptor.sql.BlobTypeDescriptor.INSTANCE, BlobTypeDescriptor.INSTANCE ); this(
org.hibernate.type.descriptor.sql.BlobTypeDescriptor.DEFAULT,
new AlternativeLobTypes.BlobTypes<Blob,BlobType>( BlobType.class )
);
}
protected BlobType(SqlTypeDescriptor sqlTypeDescriptor,
AlternativeLobTypes.BlobTypes<Blob,BlobType> blobTypes) {
super( sqlTypeDescriptor, BlobTypeDescriptor.INSTANCE, blobTypes );
} }
/** /**

View File

@ -25,6 +25,7 @@ package org.hibernate.type;
import org.hibernate.type.descriptor.java.CharacterArrayTypeDescriptor; import org.hibernate.type.descriptor.java.CharacterArrayTypeDescriptor;
import org.hibernate.type.descriptor.sql.ClobTypeDescriptor; import org.hibernate.type.descriptor.sql.ClobTypeDescriptor;
import org.hibernate.type.descriptor.sql.SqlTypeDescriptor;
/** /**
* A type that maps between {@link java.sql.Types#CLOB CLOB} and {@link Character Character[]} * A type that maps between {@link java.sql.Types#CLOB CLOB} and {@link Character Character[]}
@ -34,16 +35,22 @@ import org.hibernate.type.descriptor.sql.ClobTypeDescriptor;
* @author Emmanuel Bernard * @author Emmanuel Bernard
* @author Steve Ebersole * @author Steve Ebersole
*/ */
public class CharacterArrayClobType extends AbstractSingleColumnStandardBasicType<Character[]> { public class CharacterArrayClobType extends LobType<Character[]> {
public static final CharacterArrayClobType INSTANCE = new CharacterArrayClobType(); public static final CharacterArrayClobType INSTANCE = new CharacterArrayClobType();
public CharacterArrayClobType() { public CharacterArrayClobType() {
super( ClobTypeDescriptor.INSTANCE, CharacterArrayTypeDescriptor.INSTANCE ); this(
ClobTypeDescriptor.DEFAULT,
new AlternativeLobTypes.ClobTypes<Character[],CharacterArrayClobType>( CharacterArrayClobType.class )
);
}
protected CharacterArrayClobType(SqlTypeDescriptor sqlTypeDescriptor,
AlternativeLobTypes.ClobTypes<Character[],CharacterArrayClobType> clobTypes) {
super( sqlTypeDescriptor, CharacterArrayTypeDescriptor.INSTANCE, clobTypes );
} }
public String getName() { public String getName() {
// todo name these annotation types for addition to the registry return "wrapper_characters_clob";
return null;
} }
} }

View File

@ -26,6 +26,7 @@ package org.hibernate.type;
import java.sql.Clob; import java.sql.Clob;
import org.hibernate.type.descriptor.java.ClobTypeDescriptor; import org.hibernate.type.descriptor.java.ClobTypeDescriptor;
import org.hibernate.type.descriptor.sql.SqlTypeDescriptor;
/** /**
* A type that maps between {@link java.sql.Types#CLOB CLOB} and {@link Clob} * A type that maps between {@link java.sql.Types#CLOB CLOB} and {@link Clob}
@ -33,11 +34,20 @@ import org.hibernate.type.descriptor.java.ClobTypeDescriptor;
* @author Gavin King * @author Gavin King
* @author Steve Ebersole * @author Steve Ebersole
*/ */
public class ClobType extends AbstractSingleColumnStandardBasicType<Clob> { public class ClobType extends LobType<Clob> {
public static final ClobType INSTANCE = new ClobType(); public static final ClobType INSTANCE = new ClobType();
public ClobType() { public ClobType() {
super( org.hibernate.type.descriptor.sql.ClobTypeDescriptor.INSTANCE, ClobTypeDescriptor.INSTANCE ); this(
org.hibernate.type.descriptor.sql.ClobTypeDescriptor.DEFAULT,
new AlternativeLobTypes.ClobTypes<Clob,ClobType>( ClobType.class )
);
}
protected ClobType(SqlTypeDescriptor sqlTypeDescriptor,
AlternativeLobTypes.ClobTypes<Clob,ClobType> clobTypes) {
super( sqlTypeDescriptor, ClobTypeDescriptor.INSTANCE, clobTypes );
} }
public String getName() { public String getName() {
@ -53,5 +63,4 @@ public class ClobType extends AbstractSingleColumnStandardBasicType<Clob> {
protected Clob getReplacement(Clob original, Clob target) { protected Clob getReplacement(Clob original, Clob target) {
return target; return target;
} }
} }

View File

@ -0,0 +1,49 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2010, Red Hat Inc. or third-party contributors as
* indicated by the @author tags or express copyright attribution
* statements applied by the authors. All third-party contributions are
* distributed under license by Red Hat Inc.
*
* This copyrighted material is made available to anyone wishing to use, modify,
* copy, or redistribute it subject to the terms and conditions of the GNU
* Lesser General Public License, as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
* for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this distribution; if not, write to:
* Free Software Foundation, Inc.
* 51 Franklin Street, Fifth Floor
* Boston, MA 02110-1301 USA
*/
package org.hibernate.type;
import org.hibernate.type.descriptor.java.JavaTypeDescriptor;
import org.hibernate.type.descriptor.sql.SqlTypeDescriptor;
/**
* A base type used to define a LOB type; it also provides
* alternatives that can override this type via
* {@link org.hibernate.type.LobType#getAlternatives()} getAlternatives()}
*
* @author Gail Badner
*/
public abstract class LobType<T> extends AbstractSingleColumnStandardBasicType<T> {
private AlternativeLobTypes alternativeLobTypes;
public LobType(SqlTypeDescriptor sqlTypeDescriptor,
JavaTypeDescriptor<T> javaTypeDescriptor,
AlternativeLobTypes alternativeLobTypes) {
super( sqlTypeDescriptor, javaTypeDescriptor );
this.alternativeLobTypes = alternativeLobTypes;
}
public AlternativeLobTypes getAlternatives() {
return alternativeLobTypes;
}
}

View File

@ -25,6 +25,7 @@ package org.hibernate.type;
import org.hibernate.type.descriptor.java.PrimitiveByteArrayTypeDescriptor; import org.hibernate.type.descriptor.java.PrimitiveByteArrayTypeDescriptor;
import org.hibernate.type.descriptor.sql.BlobTypeDescriptor; import org.hibernate.type.descriptor.sql.BlobTypeDescriptor;
import org.hibernate.type.descriptor.sql.SqlTypeDescriptor;
/** /**
* A type that maps between {@link java.sql.Types#BLOB BLOB} and {@code byte[]} * A type that maps between {@link java.sql.Types#BLOB BLOB} and {@code byte[]}
@ -34,11 +35,20 @@ import org.hibernate.type.descriptor.sql.BlobTypeDescriptor;
* @author Gail Badner * @author Gail Badner
* @author Steve Ebersole * @author Steve Ebersole
*/ */
public class MaterializedBlobType extends AbstractSingleColumnStandardBasicType<byte[]> { public class MaterializedBlobType extends LobType<byte[]> {
public static final MaterializedBlobType INSTANCE = new MaterializedBlobType(); public static final MaterializedBlobType INSTANCE = new MaterializedBlobType();
public MaterializedBlobType() { public MaterializedBlobType() {
super( BlobTypeDescriptor.INSTANCE, PrimitiveByteArrayTypeDescriptor.INSTANCE ); this(
BlobTypeDescriptor.DEFAULT,
new AlternativeLobTypes.BlobTypes<byte[],MaterializedBlobType>( MaterializedBlobType.class )
);
}
protected MaterializedBlobType(SqlTypeDescriptor sqlTypeDescriptor,
AlternativeLobTypes.BlobTypes<byte[],MaterializedBlobType> blobTypes) {
super( sqlTypeDescriptor, PrimitiveByteArrayTypeDescriptor.INSTANCE, blobTypes );
} }
public String getName() { public String getName() {

View File

@ -25,6 +25,7 @@ package org.hibernate.type;
import org.hibernate.type.descriptor.java.StringTypeDescriptor; import org.hibernate.type.descriptor.java.StringTypeDescriptor;
import org.hibernate.type.descriptor.sql.ClobTypeDescriptor; import org.hibernate.type.descriptor.sql.ClobTypeDescriptor;
import org.hibernate.type.descriptor.sql.SqlTypeDescriptor;
/** /**
* A type that maps between {@link java.sql.Types#CLOB CLOB} and {@link String} * A type that maps between {@link java.sql.Types#CLOB CLOB} and {@link String}
@ -33,11 +34,19 @@ import org.hibernate.type.descriptor.sql.ClobTypeDescriptor;
* @author Gail Badner * @author Gail Badner
* @author Steve Ebersole * @author Steve Ebersole
*/ */
public class MaterializedClobType extends AbstractSingleColumnStandardBasicType<String> { public class MaterializedClobType extends LobType<String> {
public static final MaterializedClobType INSTANCE = new MaterializedClobType(); public static final MaterializedClobType INSTANCE = new MaterializedClobType();
public MaterializedClobType() { public MaterializedClobType() {
super( ClobTypeDescriptor.INSTANCE, StringTypeDescriptor.INSTANCE ); this(
ClobTypeDescriptor.DEFAULT,
new AlternativeLobTypes.ClobTypes<String,MaterializedClobType>( MaterializedClobType.class )
);
}
protected MaterializedClobType(SqlTypeDescriptor sqlTypeDescriptor,
AlternativeLobTypes.ClobTypes<String,MaterializedClobType> clobTypes) {
super( sqlTypeDescriptor, StringTypeDescriptor.INSTANCE, clobTypes );
} }
public String getName() { public String getName() {

View File

@ -25,21 +25,31 @@ package org.hibernate.type;
import org.hibernate.type.descriptor.java.PrimitiveCharacterArrayTypeDescriptor; import org.hibernate.type.descriptor.java.PrimitiveCharacterArrayTypeDescriptor;
import org.hibernate.type.descriptor.sql.ClobTypeDescriptor; import org.hibernate.type.descriptor.sql.ClobTypeDescriptor;
import org.hibernate.type.descriptor.sql.SqlTypeDescriptor;
/** /**
* Map a char[] to a Clob * Map a char[] to a Clob
* *
* @author Emmanuel Bernard * @author Emmanuel Bernard
*/ */
public class PrimitiveCharacterArrayClobType extends AbstractSingleColumnStandardBasicType<char[]> { public class PrimitiveCharacterArrayClobType extends LobType<char[]> {
public static final CharacterArrayClobType INSTANCE = new CharacterArrayClobType(); public static final PrimitiveCharacterArrayClobType INSTANCE = new PrimitiveCharacterArrayClobType();
public PrimitiveCharacterArrayClobType() { public PrimitiveCharacterArrayClobType() {
super( ClobTypeDescriptor.INSTANCE, PrimitiveCharacterArrayTypeDescriptor.INSTANCE ); this(
ClobTypeDescriptor.DEFAULT,
new AlternativeLobTypes.ClobTypes<char[],PrimitiveCharacterArrayClobType>(
PrimitiveCharacterArrayClobType.class
)
);
}
protected PrimitiveCharacterArrayClobType(SqlTypeDescriptor sqlTypeDescriptor, AlternativeLobTypes.
ClobTypes<char[],PrimitiveCharacterArrayClobType> clobTypes) {
super( sqlTypeDescriptor, PrimitiveCharacterArrayTypeDescriptor.INSTANCE, clobTypes );
} }
public String getName() { public String getName() {
// todo name these annotation types for addition to the registry return "characters_clob";
return null;
} }
} }

View File

@ -261,6 +261,12 @@ public class StandardBasicTypes {
*/ */
public static final MaterializedBlobType MATERIALIZED_BLOB = MaterializedBlobType.INSTANCE; public static final MaterializedBlobType MATERIALIZED_BLOB = MaterializedBlobType.INSTANCE;
/**
* The standard Hibernate type for mapping {@code Byte[]} to JDBC {@link java.sql.Types#BLOB BLOB}
* @see WrappedMaterializedBlobType
*/
public static final WrappedMaterializedBlobType WRAPPER_MATERIALIZED_BLOB = WrappedMaterializedBlobType.INSTANCE;
/** /**
* The standard Hibernate type for mapping {@code char[]} to JDBC {@link java.sql.Types#VARCHAR VARCHAR}. * The standard Hibernate type for mapping {@code char[]} to JDBC {@link java.sql.Types#VARCHAR VARCHAR}.
* *
@ -302,6 +308,18 @@ public class StandardBasicTypes {
*/ */
public static final MaterializedClobType MATERIALIZED_CLOB = MaterializedClobType.INSTANCE; public static final MaterializedClobType MATERIALIZED_CLOB = MaterializedClobType.INSTANCE;
/**
* The standard Hibernate type for mapping {@link Character[]} to JDBC {@link java.sql.Types#CLOB CLOB}.
* @see CharacterArrayClobType
*/
public static final CharacterArrayClobType WRAPPER_CHARACTERS_CLOB = CharacterArrayClobType.INSTANCE;
/**
* The standard Hibernate type for mapping {@link Character[]} to JDBC {@link java.sql.Types#CLOB CLOB}.
* @see PrimitiveCharacterArrayClobType
*/
public static final PrimitiveCharacterArrayClobType CHARACTERS_CLOB = PrimitiveCharacterArrayClobType.INSTANCE;
/** /**
* The standard Hibernate type for mapping {@link java.io.Serializable} to JDBC {@link java.sql.Types#VARBINARY VARBINARY}. * The standard Hibernate type for mapping {@link java.io.Serializable} to JDBC {@link java.sql.Types#VARBINARY VARBINARY}.
* <p/> * <p/>

View File

@ -24,6 +24,8 @@
package org.hibernate.type; package org.hibernate.type;
import java.io.Serializable; import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties; import java.util.Properties;
import org.hibernate.MappingException; import org.hibernate.MappingException;
@ -42,30 +44,65 @@ public class TypeResolver implements Serializable {
private final BasicTypeRegistry basicTypeRegistry; private final BasicTypeRegistry basicTypeRegistry;
private final TypeFactory typeFactory; private final TypeFactory typeFactory;
// Need to keep track of the "global" type overrides in case there are dialect-specific overrides.
// Dialect-specific types must be applied before "global" overrides are applied.
// Unfortunately, dialect-specific types are not known until this TypeResolver is scoped...
private final List<BasicType> typeOverrides;
// If this TypeResolver is scoped to a SessionFactoryImplementor with
// dialect-specific type overrides, scopedTypeRegistry will be initialized,
// dialect-specific type overrides will be applied, followed by "global"
// type overrides.
private BasicTypeRegistry scopedTypeRegistry;
public TypeResolver() { public TypeResolver() {
this( new BasicTypeRegistry(), new TypeFactory() ); this( new BasicTypeRegistry(), new TypeFactory() );
} }
public TypeResolver(BasicTypeRegistry basicTypeRegistry, TypeFactory typeFactory) { public TypeResolver(BasicTypeRegistry basicTypeRegistry, TypeFactory typeFactory) {
this.basicTypeRegistry = basicTypeRegistry; this.basicTypeRegistry = basicTypeRegistry;
this.typeOverrides = new ArrayList<BasicType>();
this.typeFactory = typeFactory; this.typeFactory = typeFactory;
} }
public TypeResolver scope(SessionFactoryImplementor factory) { public TypeResolver scope(SessionFactoryImplementor factory) {
typeFactory.injectSessionFactory( factory ); typeFactory.injectSessionFactory( factory );
return new TypeResolver( basicTypeRegistry.shallowCopy(), typeFactory ); // if there was a scopedTypeRegistry left from the last time this
// TypeResolver was scoped, then set it to null;
scopedTypeRegistry = null;
BasicTypeRegistry registry = basicTypeRegistry;
List<BasicType> dialectTypeOverrides = factory.getDialect().getTypeOverrides();
if ( factory != null && ! dialectTypeOverrides.isEmpty() ) {
// scoping to a factory with dialect-specific type overrides;
// create a new scopedTypeRegistry and override dialect-specific types
// before overriding the "global" type overrides;
scopedTypeRegistry = new BasicTypeRegistry();
registerTypeOverrides( scopedTypeRegistry, dialectTypeOverrides );
registerTypeOverrides( scopedTypeRegistry, typeOverrides );
registry = scopedTypeRegistry;
}
return new TypeResolver( registry.shallowCopy(), typeFactory );
} }
public void registerTypeOverride(BasicType type) { public void registerTypeOverride(BasicType type) {
basicTypeRegistry.register( type ); basicTypeRegistry.register( type );
typeOverrides.add( type );
} }
public void registerTypeOverride(UserType type, String[] keys) { public void registerTypeOverride(UserType type, String[] keys) {
basicTypeRegistry.register( type, keys ); basicTypeRegistry.register( type, keys );
typeOverrides.add( new CustomType( type, keys ) );
} }
public void registerTypeOverride(CompositeUserType type, String[] keys) { public void registerTypeOverride(CompositeUserType type, String[] keys) {
basicTypeRegistry.register( type, keys ); basicTypeRegistry.register( type, keys );
typeOverrides.add( new CompositeCustomType( type, keys ) );
}
private static void registerTypeOverrides(BasicTypeRegistry typeRegistry, List<BasicType> typeOverrides) {
for ( BasicType typeOverride : typeOverrides ) {
typeRegistry.register( typeOverride );
}
} }
public TypeFactory getTypeFactory() { public TypeFactory getTypeFactory() {
@ -73,14 +110,17 @@ public class TypeResolver implements Serializable {
} }
/** /**
* Locate a Hibernate {@linkplain BasicType basic type} given (one of) its registration names. * Locate a Hibernate {@linkplain BasicType basic type} given (one of) its registration names;
* if scoped to a {@link SessionFactoryImplementor}, the scoped type is returned.
* *
* @param name The registration name * @param name The registration name
* *
* @return The registered type * @return The registered type
*/ */
public BasicType basic(String name) { public BasicType basic(String name) {
return basicTypeRegistry.getRegisteredType( name ); return scopedTypeRegistry == null ?
basicTypeRegistry.getRegisteredType( name ) :
scopedTypeRegistry.getRegisteredType( name );
} }
/** /**

View File

@ -25,6 +25,7 @@ package org.hibernate.type;
import org.hibernate.type.descriptor.java.ByteArrayTypeDescriptor; import org.hibernate.type.descriptor.java.ByteArrayTypeDescriptor;
import org.hibernate.type.descriptor.sql.BlobTypeDescriptor; import org.hibernate.type.descriptor.sql.BlobTypeDescriptor;
import org.hibernate.type.descriptor.sql.SqlTypeDescriptor;
/** /**
* A type that maps JDBC {@link java.sql.Types#BLOB BLOB} and {@code Byte[]}. * A type that maps JDBC {@link java.sql.Types#BLOB BLOB} and {@code Byte[]}.
@ -32,15 +33,25 @@ import org.hibernate.type.descriptor.sql.BlobTypeDescriptor;
* *
* @author Strong Liu * @author Strong Liu
*/ */
public class WrappedMaterializedBlobType extends AbstractSingleColumnStandardBasicType<Byte[]> { public class WrappedMaterializedBlobType extends LobType<Byte[]> {
public static final WrappedMaterializedBlobType INSTANCE = new WrappedMaterializedBlobType(); public static final WrappedMaterializedBlobType INSTANCE = new WrappedMaterializedBlobType();
public WrappedMaterializedBlobType() { public WrappedMaterializedBlobType() {
super( BlobTypeDescriptor.INSTANCE, ByteArrayTypeDescriptor.INSTANCE ); this(
BlobTypeDescriptor.DEFAULT,
new AlternativeLobTypes.BlobTypes<Byte[],WrappedMaterializedBlobType>(
WrappedMaterializedBlobType.class
)
);
}
protected WrappedMaterializedBlobType(SqlTypeDescriptor sqlTypeDescriptor,
AlternativeLobTypes.BlobTypes<Byte[],WrappedMaterializedBlobType> clobTypes) {
super( sqlTypeDescriptor, ByteArrayTypeDescriptor.INSTANCE, clobTypes );
} }
public String getName() { public String getName() {
// todo name these annotation types for addition to the registry return "wrapper_materialized_blob";
return null;
} }
} }

View File

@ -25,12 +25,15 @@ package org.hibernate.type.descriptor.java;
import java.io.Serializable; import java.io.Serializable;
import java.sql.Blob; import java.sql.Blob;
import java.sql.Clob;
import java.sql.SQLException; 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.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.type.descriptor.CharacterStream;
import org.hibernate.type.descriptor.WrapperOptions; import org.hibernate.type.descriptor.WrapperOptions;
/** /**
@ -107,7 +110,7 @@ public class BlobTypeDescriptor extends AbstractTypeDescriptor<Blob> {
@SuppressWarnings({ "unchecked" }) @SuppressWarnings({ "unchecked" })
public <X> X unwrap(Blob value, Class<X> type, WrapperOptions options) { public <X> X unwrap(Blob value, Class<X> type, WrapperOptions options) {
if ( !Blob.class.isAssignableFrom( type ) ) { if ( ! ( Blob.class.isAssignableFrom( type ) || BinaryStream.class.isAssignableFrom( type ) ) ) {
throw unknownUnwrap( type ); throw unknownUnwrap( type );
} }
@ -115,6 +118,15 @@ public class BlobTypeDescriptor extends AbstractTypeDescriptor<Blob> {
return null; return null;
} }
if ( BinaryStream.class.isAssignableFrom( type ) ) {
try {
return (X) new BinaryStreamImpl( DataHelper.extractBytes( value.getBinaryStream() ) );
}
catch ( SQLException e ) {
throw new HibernateException( "Unable to access blob stream", e );
}
}
final Blob blob = WrappedBlob.class.isInstance( value ) final Blob blob = WrappedBlob.class.isInstance( value )
? ( (WrappedBlob) value ).getWrappedBlob() ? ( (WrappedBlob) value ).getWrappedBlob()
: value; : value;

View File

@ -23,6 +23,7 @@
*/ */
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.sql.SQLException;
@ -31,6 +32,7 @@ import java.util.Comparator;
import org.hibernate.HibernateException; import org.hibernate.HibernateException;
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.type.descriptor.WrapperOptions; import org.hibernate.type.descriptor.WrapperOptions;
/** /**
@ -98,8 +100,8 @@ public class ClobTypeDescriptor extends AbstractTypeDescriptor<Clob> {
} }
@SuppressWarnings({ "unchecked" }) @SuppressWarnings({ "unchecked" })
public <X> X unwrap(Clob value, Class<X> type, WrapperOptions options) { public <X> X unwrap(final Clob value, Class<X> type, WrapperOptions options) {
if ( !Clob.class.isAssignableFrom( type ) ) { if ( ! ( Clob.class.isAssignableFrom( type ) || CharacterStream.class.isAssignableFrom( type ) ) ) {
throw unknownUnwrap( type ); throw unknownUnwrap( type );
} }
@ -107,6 +109,15 @@ public class ClobTypeDescriptor extends AbstractTypeDescriptor<Clob> {
return null; return null;
} }
if ( CharacterStream.class.isAssignableFrom( type ) ) {
try {
return (X) new CharacterStreamImpl( DataHelper.extractString( value.getCharacterStream() ) );
}
catch ( SQLException e ) {
throw new HibernateException( "Unable to access lob stream", e );
}
}
final Clob clob = WrappedClob.class.isInstance( value ) final Clob clob = WrappedClob.class.isInstance( value )
? ( (WrappedClob) value ).getWrappedClob() ? ( (WrappedClob) value ).getWrappedClob()
: value; : value;

View File

@ -23,7 +23,6 @@
*/ */
package org.hibernate.type.descriptor.sql; package org.hibernate.type.descriptor.sql;
import java.io.InputStream;
import java.sql.Blob; import java.sql.Blob;
import java.sql.PreparedStatement; import java.sql.PreparedStatement;
import java.sql.ResultSet; import java.sql.ResultSet;
@ -41,32 +40,73 @@ import org.hibernate.type.descriptor.WrapperOptions;
* *
* @author Steve Ebersole * @author Steve Ebersole
*/ */
public class BlobTypeDescriptor implements SqlTypeDescriptor { public abstract class BlobTypeDescriptor implements SqlTypeDescriptor {
public static final BlobTypeDescriptor INSTANCE = new BlobTypeDescriptor();
public int getSqlType() { private BlobTypeDescriptor() {}
return Types.BLOB;
}
public <X> ValueBinder<X> getBinder(final JavaTypeDescriptor<X> javaTypeDescriptor) { public static final BlobTypeDescriptor DEFAULT =
new BlobTypeDescriptor() {
public <X> BasicBinder<X> getBlobBinder(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() ) { if ( options.useStreamForLobBinding() ) {
final BinaryStream binaryStream = javaTypeDescriptor.unwrap( value, BinaryStream.class, options ); STREAM_BINDING.getBlobBinder( javaTypeDescriptor ).doBind( st, value, index, options );
st.setBinaryStream( index, binaryStream.getInputStream(), binaryStream.getLength() );
} }
else if ( byte[].class.isInstance( value ) ) { else if ( byte[].class.isInstance( value ) ) {
// performance shortcut for binding BLOB data in byte[] format // performance shortcut for binding BLOB data in byte[] format
final byte[] bytes = (byte[]) value; PRIMITIVE_ARRAY_BINDING.getBlobBinder( javaTypeDescriptor ).doBind( st, value, index, options );
st.setBytes( index, bytes );
} }
else { else {
st.setBlob( index, javaTypeDescriptor.unwrap( value, Blob.class, options ) ); BLOB_BINDING.getBlobBinder( javaTypeDescriptor ).doBind( st, value, index, options );
} }
} }
}; };
} }
};
public static final BlobTypeDescriptor PRIMITIVE_ARRAY_BINDING =
new BlobTypeDescriptor() {
public <X> BasicBinder<X> getBlobBinder(final JavaTypeDescriptor<X> javaTypeDescriptor) {
return new BasicBinder<X>( javaTypeDescriptor, this ) {
@Override
public void doBind(PreparedStatement st, X value, int index, WrapperOptions options)
throws SQLException {
st.setBytes( index, ( byte[] ) value );
}
};
}
};
public static final BlobTypeDescriptor BLOB_BINDING =
new BlobTypeDescriptor() {
public <X> BasicBinder<X> getBlobBinder(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.setBlob( index, javaTypeDescriptor.unwrap( value, Blob.class, options ) );
}
};
}
};
public static final BlobTypeDescriptor STREAM_BINDING =
new BlobTypeDescriptor() {
public <X> BasicBinder<X> getBlobBinder(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 BinaryStream binaryStream = javaTypeDescriptor.unwrap( value, BinaryStream.class, options );
st.setBinaryStream( index, binaryStream.getInputStream(), binaryStream.getLength() );
}
};
}
};
protected abstract <X> BasicBinder<X> getBlobBinder(final JavaTypeDescriptor<X> javaTypeDescriptor);
public <X> ValueExtractor<X> getExtractor(final JavaTypeDescriptor<X> javaTypeDescriptor) { public <X> ValueExtractor<X> getExtractor(final JavaTypeDescriptor<X> javaTypeDescriptor) {
return new BasicExtractor<X>( javaTypeDescriptor, this ) { return new BasicExtractor<X>( javaTypeDescriptor, this ) {
@ -76,4 +116,12 @@ public class BlobTypeDescriptor implements SqlTypeDescriptor {
} }
}; };
} }
public int getSqlType() {
return Types.BLOB;
}
public <X> ValueBinder<X> getBinder(final JavaTypeDescriptor<X> javaTypeDescriptor) {
return getBlobBinder( javaTypeDescriptor );
}
} }

View File

@ -41,27 +41,61 @@ import org.hibernate.type.descriptor.java.JavaTypeDescriptor;
* *
* @author Steve Ebersole * @author Steve Ebersole
*/ */
public class ClobTypeDescriptor implements SqlTypeDescriptor { public abstract class ClobTypeDescriptor implements SqlTypeDescriptor {
public static final ClobTypeDescriptor INSTANCE = new ClobTypeDescriptor();
public int getSqlType() { public static final ClobTypeDescriptor DEFAULT =
return Types.CLOB; new ClobTypeDescriptor() {
} public <X> BasicBinder<X> getClobBinder(final JavaTypeDescriptor<X> javaTypeDescriptor) {
public <X> ValueBinder<X> getBinder(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() ) { if ( options.useStreamForLobBinding() ) {
final CharacterStream characterStream = javaTypeDescriptor.unwrap( value, CharacterStream.class, options ); STREAM_BINDING.getClobBinder( javaTypeDescriptor ).doBind( st, value, index, options );
st.setCharacterStream( index, characterStream.getReader(), characterStream.getLength() );
} }
else { else {
st.setClob( index, javaTypeDescriptor.unwrap( value, Clob.class, options ) ); 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.getReader(), 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() {
return Types.CLOB;
}
public <X> ValueExtractor<X> getExtractor(final JavaTypeDescriptor<X> javaTypeDescriptor) { public <X> ValueExtractor<X> getExtractor(final JavaTypeDescriptor<X> javaTypeDescriptor) {
return new BasicExtractor<X>( javaTypeDescriptor, this ) { return new BasicExtractor<X>( javaTypeDescriptor, this ) {

View File

@ -48,7 +48,7 @@ public class StringValueMappingTest extends TestCase {
private final StringTypeDescriptor stringJavaDescriptor = new StringTypeDescriptor(); private final StringTypeDescriptor stringJavaDescriptor = new StringTypeDescriptor();
private final VarcharTypeDescriptor varcharSqlDescriptor = new VarcharTypeDescriptor(); private final VarcharTypeDescriptor varcharSqlDescriptor = new VarcharTypeDescriptor();
private final ClobTypeDescriptor clobSqlDescriptor = new ClobTypeDescriptor(); private final ClobTypeDescriptor clobSqlDescriptor = ClobTypeDescriptor.DEFAULT;
private final WrapperOptions wrapperOptions = new WrapperOptions() { private final WrapperOptions wrapperOptions = new WrapperOptions() {
public boolean useStreamForLobBinding() { public boolean useStreamForLobBinding() {

View File

@ -28,7 +28,7 @@ import java.util.Arrays;
import org.hibernate.Session; import org.hibernate.Session;
import org.hibernate.cfg.AnnotationConfiguration; import org.hibernate.cfg.AnnotationConfiguration;
import org.hibernate.cfg.Configuration; import org.hibernate.cfg.Configuration;
import org.hibernate.cfg.Environment; import org.hibernate.dialect.PostgreSQLDialect;
import org.hibernate.test.annotations.TestCase; import org.hibernate.test.annotations.TestCase;
import org.hibernate.testing.junit.DialectChecks; import org.hibernate.testing.junit.DialectChecks;
import org.hibernate.testing.junit.RequiresDialectFeature; import org.hibernate.testing.junit.RequiresDialectFeature;
@ -56,8 +56,16 @@ public class MaterializedBlobTest extends TestCase {
public void testTypeSelection() { public void testTypeSelection() {
int index = sfi().getEntityPersister( MaterializedBlobEntity.class.getName() ).getEntityMetamodel().getPropertyIndex( "theBytes" ); int index = sfi().getEntityPersister( MaterializedBlobEntity.class.getName() ).getEntityMetamodel().getPropertyIndex( "theBytes" );
Type type = sfi().getEntityPersister( MaterializedBlobEntity.class.getName() ).getEntityMetamodel().getProperties()[index].getType(); Type type = sfi().getEntityPersister( MaterializedBlobEntity.class.getName() ).getEntityMetamodel().getProperties()[index].getType();
if ( PostgreSQLDialect.class.isInstance( getDialect() )) {
assertEquals( MaterializedBlobType.INSTANCE.getAlternatives().getLobBindingType(), type );
}
else if ( getDialect().useInputStreamToInsertBlob() ) {
assertEquals( MaterializedBlobType.INSTANCE.getAlternatives().getStreamBindingType(), type );
}
else {
assertEquals( MaterializedBlobType.INSTANCE, type ); assertEquals( MaterializedBlobType.INSTANCE, type );
} }
}
public void testSaving() { public void testSaving() {
byte[] testData = "test data".getBytes(); byte[] testData = "test data".getBytes();

View File

@ -33,6 +33,7 @@ import org.hibernate.dialect.Dialect;
import org.hibernate.dialect.H2Dialect; import org.hibernate.dialect.H2Dialect;
import org.hibernate.testing.junit.functional.DatabaseSpecificFunctionalTestCase; import org.hibernate.testing.junit.functional.DatabaseSpecificFunctionalTestCase;
import org.hibernate.testing.junit.functional.FunctionalTestClassTestSuite; import org.hibernate.testing.junit.functional.FunctionalTestClassTestSuite;
import org.hibernate.type.descriptor.java.DataHelper;
/** /**
* Tests lazy materialization of data mapped by * Tests lazy materialization of data mapped by
@ -166,14 +167,7 @@ public class ClobLocatorTest extends DatabaseSpecificFunctionalTestCase {
} }
private String extractData(Clob clob) throws Throwable { private String extractData(Clob clob) throws Throwable {
if ( getDialect() instanceof H2Dialect ) { return DataHelper.extractString( clob.getCharacterStream() );
return clob.getSubString( 1, ( int ) clob.length() );
}
else {
char[] data = new char[ (int) clob.length() ];
clob.getCharacterStream().read( data );
return new String( data );
}
} }