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.intercept.FieldInterceptionHelper;
import org.hibernate.intercept.FieldInterceptor;
import org.hibernate.mapping.ManyToOne;
import org.hibernate.proxy.HibernateProxy;
import org.hibernate.proxy.LazyInitializer;
import org.hibernate.type.CharacterArrayClobType;
import org.hibernate.type.MaterializedBlobType;
import org.hibernate.type.AnyType;
import org.hibernate.type.BigDecimalType;
import org.hibernate.type.BigIntegerType;
@ -66,6 +67,7 @@ import org.hibernate.type.LocaleType;
import org.hibernate.type.LongType;
import org.hibernate.type.ManyToOneType;
import org.hibernate.type.ObjectType;
import org.hibernate.type.PrimitiveCharacterArrayClobType;
import org.hibernate.type.SerializableType;
import org.hibernate.type.ShortType;
import org.hibernate.type.StringType;
@ -76,11 +78,11 @@ import org.hibernate.type.TimestampType;
import org.hibernate.type.TrueFalseType;
import org.hibernate.type.Type;
import org.hibernate.type.TypeFactory;
import org.hibernate.type.WrappedMaterializedBlobType;
import org.hibernate.type.YesNoType;
import org.hibernate.type.CharArrayType;
import org.hibernate.type.WrapperBinaryType;
import org.hibernate.type.CharacterArrayType;
import org.hibernate.type.MaterializedBlobType;
import org.hibernate.type.ImageType;
import org.hibernate.type.MaterializedClobType;
import org.hibernate.usertype.CompositeUserType;
@ -236,6 +238,25 @@ public final class Hibernate {
* @deprecated Use {@link org.hibernate.type.StandardBasicTypes#CLOB} instead.
*/
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.
* @deprecated Use {@link org.hibernate.type.StandardBasicTypes#CALENDAR} instead.

View File

@ -182,13 +182,13 @@ public class SimpleValueBinder {
type = Hibernate.MATERIALIZED_CLOB.getName();
}
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 ) {
type = PrimitiveCharacterArrayClobType.class.getName();
type = Hibernate.CHARACTERS_CLOB.getName();
}
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 ) {
type = Hibernate.MATERIALIZED_BLOB.getName();

View File

@ -27,8 +27,11 @@ import java.sql.CallableStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Types;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
@ -66,7 +69,15 @@ import org.hibernate.sql.ANSIJoinFragment;
import org.hibernate.sql.CaseFragment;
import org.hibernate.sql.ForUpdateFragment;
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.WrappedMaterializedBlobType;
import org.hibernate.util.ReflectHelper;
import org.hibernate.util.StringHelper;
@ -94,14 +105,30 @@ public abstract class Dialect {
public static final String 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 hibernateTypeNames = new TypeNames();
private final List<BasicType> dialectSpecificOverrides = new ArrayList<BasicType>();
private final Properties properties = new Properties();
private final Map<String, SQLFunction> sqlFunctions = new HashMap<String, SQLFunction>();
private final Set<String> sqlKeywords = new HashSet<String>();
// constructors and factory methods ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
protected Dialect() {
@ -210,6 +237,26 @@ public abstract class Dialect {
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() {
return getClass().getName();
}

View File

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

View File

@ -26,6 +26,7 @@ package org.hibernate.type;
import java.sql.Blob;
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}
@ -33,11 +34,20 @@ import org.hibernate.type.descriptor.java.BlobTypeDescriptor;
* @author Gavin King
* @author Steve Ebersole
*/
public class BlobType extends AbstractSingleColumnStandardBasicType<Blob> {
public class BlobType extends LobType<Blob> {
public static final BlobType INSTANCE = new 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.sql.ClobTypeDescriptor;
import org.hibernate.type.descriptor.sql.SqlTypeDescriptor;
/**
* 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 Steve Ebersole
*/
public class CharacterArrayClobType extends AbstractSingleColumnStandardBasicType<Character[]> {
public class CharacterArrayClobType extends LobType<Character[]> {
public static final CharacterArrayClobType INSTANCE = new 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() {
// todo name these annotation types for addition to the registry
return null;
return "wrapper_characters_clob";
}
}

View File

@ -26,6 +26,7 @@ package org.hibernate.type;
import java.sql.Clob;
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}
@ -33,11 +34,20 @@ import org.hibernate.type.descriptor.java.ClobTypeDescriptor;
* @author Gavin King
* @author Steve Ebersole
*/
public class ClobType extends AbstractSingleColumnStandardBasicType<Clob> {
public class ClobType extends LobType<Clob> {
public static final ClobType INSTANCE = new 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() {
@ -53,5 +63,4 @@ public class ClobType extends AbstractSingleColumnStandardBasicType<Clob> {
protected Clob getReplacement(Clob original, Clob 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.sql.BlobTypeDescriptor;
import org.hibernate.type.descriptor.sql.SqlTypeDescriptor;
/**
* 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 Steve Ebersole
*/
public class MaterializedBlobType extends AbstractSingleColumnStandardBasicType<byte[]> {
public class MaterializedBlobType extends LobType<byte[]> {
public static final MaterializedBlobType INSTANCE = new 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() {

View File

@ -25,6 +25,7 @@ package org.hibernate.type;
import org.hibernate.type.descriptor.java.StringTypeDescriptor;
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}
@ -33,11 +34,19 @@ import org.hibernate.type.descriptor.sql.ClobTypeDescriptor;
* @author Gail Badner
* @author Steve Ebersole
*/
public class MaterializedClobType extends AbstractSingleColumnStandardBasicType<String> {
public class MaterializedClobType extends LobType<String> {
public static final MaterializedClobType INSTANCE = new 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() {

View File

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

View File

@ -261,6 +261,12 @@ public class StandardBasicTypes {
*/
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}.
*
@ -302,6 +308,18 @@ public class StandardBasicTypes {
*/
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}.
* <p/>

View File

@ -24,6 +24,8 @@
package org.hibernate.type;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;
import org.hibernate.MappingException;
@ -42,30 +44,65 @@ public class TypeResolver implements Serializable {
private final BasicTypeRegistry basicTypeRegistry;
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() {
this( new BasicTypeRegistry(), new TypeFactory() );
}
public TypeResolver(BasicTypeRegistry basicTypeRegistry, TypeFactory typeFactory) {
this.basicTypeRegistry = basicTypeRegistry;
this.typeOverrides = new ArrayList<BasicType>();
this.typeFactory = typeFactory;
}
public TypeResolver scope(SessionFactoryImplementor 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) {
basicTypeRegistry.register( type );
typeOverrides.add( type );
}
public void registerTypeOverride(UserType type, String[] keys) {
basicTypeRegistry.register( type, keys );
typeOverrides.add( new CustomType( type, keys ) );
}
public void registerTypeOverride(CompositeUserType type, String[] 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() {
@ -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
*
* @return The registered type
*/
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.sql.BlobTypeDescriptor;
import org.hibernate.type.descriptor.sql.SqlTypeDescriptor;
/**
* 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
*/
public class WrappedMaterializedBlobType extends AbstractSingleColumnStandardBasicType<Byte[]> {
public class WrappedMaterializedBlobType extends LobType<Byte[]> {
public static final WrappedMaterializedBlobType INSTANCE = new 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() {
// todo name these annotation types for addition to the registry
return null;
return "wrapper_materialized_blob";
}
}

View File

@ -25,12 +25,15 @@ package org.hibernate.type.descriptor.java;
import java.io.Serializable;
import java.sql.Blob;
import java.sql.Clob;
import java.sql.SQLException;
import java.util.Comparator;
import org.hibernate.HibernateException;
import org.hibernate.engine.jdbc.BlobProxy;
import org.hibernate.engine.jdbc.WrappedBlob;
import org.hibernate.type.descriptor.BinaryStream;
import org.hibernate.type.descriptor.CharacterStream;
import org.hibernate.type.descriptor.WrapperOptions;
/**
@ -107,7 +110,7 @@ public class BlobTypeDescriptor extends AbstractTypeDescriptor<Blob> {
@SuppressWarnings({ "unchecked" })
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 );
}
@ -115,6 +118,15 @@ public class BlobTypeDescriptor extends AbstractTypeDescriptor<Blob> {
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 )
? ( (WrappedBlob) value ).getWrappedBlob()
: value;

View File

@ -23,6 +23,7 @@
*/
package org.hibernate.type.descriptor.java;
import java.io.Reader;
import java.io.Serializable;
import java.sql.Clob;
import java.sql.SQLException;
@ -31,6 +32,7 @@ import java.util.Comparator;
import org.hibernate.HibernateException;
import org.hibernate.engine.jdbc.ClobProxy;
import org.hibernate.engine.jdbc.WrappedClob;
import org.hibernate.type.descriptor.CharacterStream;
import org.hibernate.type.descriptor.WrapperOptions;
/**
@ -98,8 +100,8 @@ public class ClobTypeDescriptor extends AbstractTypeDescriptor<Clob> {
}
@SuppressWarnings({ "unchecked" })
public <X> X unwrap(Clob value, Class<X> type, WrapperOptions options) {
if ( !Clob.class.isAssignableFrom( type ) ) {
public <X> X unwrap(final Clob value, Class<X> type, WrapperOptions options) {
if ( ! ( Clob.class.isAssignableFrom( type ) || CharacterStream.class.isAssignableFrom( type ) ) ) {
throw unknownUnwrap( type );
}
@ -107,6 +109,15 @@ public class ClobTypeDescriptor extends AbstractTypeDescriptor<Clob> {
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 )
? ( (WrappedClob) value ).getWrappedClob()
: value;

View File

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

View File

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

View File

@ -28,7 +28,7 @@ import java.util.Arrays;
import org.hibernate.Session;
import org.hibernate.cfg.AnnotationConfiguration;
import org.hibernate.cfg.Configuration;
import org.hibernate.cfg.Environment;
import org.hibernate.dialect.PostgreSQLDialect;
import org.hibernate.test.annotations.TestCase;
import org.hibernate.testing.junit.DialectChecks;
import org.hibernate.testing.junit.RequiresDialectFeature;
@ -56,7 +56,15 @@ public class MaterializedBlobTest extends TestCase {
public void testTypeSelection() {
int index = sfi().getEntityPersister( MaterializedBlobEntity.class.getName() ).getEntityMetamodel().getPropertyIndex( "theBytes" );
Type type = sfi().getEntityPersister( MaterializedBlobEntity.class.getName() ).getEntityMetamodel().getProperties()[index].getType();
assertEquals( MaterializedBlobType.INSTANCE, type );
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 );
}
}
public void testSaving() {

View File

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