HHH-5957 : Provide a way for dialects to override a SqlTypeDescriptor

This commit is contained in:
Gail Badner 2011-02-23 15:37:37 -08:00
parent 39d2d53968
commit 89eabb920d
27 changed files with 883 additions and 96 deletions

View File

@ -67,6 +67,9 @@ import org.hibernate.sql.CaseFragment;
import org.hibernate.sql.ForUpdateFragment;
import org.hibernate.sql.JoinFragment;
import org.hibernate.type.StandardBasicTypes;
import org.hibernate.type.descriptor.sql.BlobTypeDescriptor;
import org.hibernate.type.descriptor.sql.ClobTypeDescriptor;
import org.hibernate.type.descriptor.sql.SqlTypeDescriptor;
import org.hibernate.util.ReflectHelper;
import org.hibernate.util.StringHelper;
@ -293,6 +296,67 @@ public abstract class Dialect {
typeNames.put( code, name );
}
/**
* Allows the dialect to override a {@link SqlTypeDescriptor}.
* <p/>
* If <code>sqlTypeDescriptor</code> is a "standard basic" SQL type
* descriptor, then this method uses {@link #getSqlTypeDescriptorOverride}
* to get an optional override based on the SQL code returned by
* {@link SqlTypeDescriptor#getSqlType()}.
* <p/>
* If this dialect does not provide an override, then this method
* simply returns <code>sqlTypeDescriptor</code>
*
* @param sqlTypeDescriptor The {@link SqlTypeDescriptor} to override
* @return The {@link SqlTypeDescriptor} that should be used for this dialect;
* if there is no override, then <code>sqlTypeDescriptor</code> is returned.
* @throws IllegalArgumentException if <code>sqlTypeDescriptor</code> is null.
*
* @see {@link SqlTypeDescriptor}
* @see {@link #getSqlTypeDescriptorOverride}
* @see {@link StandardBasicTypes#isStandardBasicSqlTypeDescriptor(org.hibernate.type.descriptor.sql.SqlTypeDescriptor)}
*/
public SqlTypeDescriptor resolveSqlTypeDescriptor(SqlTypeDescriptor sqlTypeDescriptor) {
if ( sqlTypeDescriptor == null ) {
throw new IllegalArgumentException( "sqlTypeDescriptor is null" );
}
SqlTypeDescriptor overrideBySqlCode = null;
if ( StandardBasicTypes.isStandardBasicSqlTypeDescriptor( sqlTypeDescriptor ) ) {
overrideBySqlCode = getSqlTypeDescriptorOverride( sqlTypeDescriptor.getSqlType() );
}
return overrideBySqlCode == null ? sqlTypeDescriptor : overrideBySqlCode;
}
/**
* Returns the {@link SqlTypeDescriptor} that should override the
* "standard basic" SQL type descriptor for values of the specified
* column type, or null, if there is no override.
*
* @param sqlCode A {@link Types} constant indicating the SQL column type
* @return The {@link SqlTypeDescriptor} that should override the
* "standard basic" SQL type descriptor, or null, if there is no override.
*
* @see {@link SqlTypeDescriptor}
* @see {@link StandardBasicTypes#isStandardBasicSqlTypeDescriptor(org.hibernate.type.descriptor.sql.SqlTypeDescriptor)}
*/
protected SqlTypeDescriptor getSqlTypeDescriptorOverride(int sqlCode) {
SqlTypeDescriptor descriptor;
switch ( sqlCode ) {
case Types.BLOB: {
descriptor = useInputStreamToInsertBlob() ? BlobTypeDescriptor.STREAM_BINDING : null;
break;
}
case Types.CLOB: {
descriptor = useInputStreamToInsertBlob() ? ClobTypeDescriptor.STREAM_BINDING : null;
break;
}
default: {
descriptor = null;
break;
}
}
return descriptor;
}
// hibernate type mapping support ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

View File

@ -40,6 +40,9 @@ import org.hibernate.exception.JDBCExceptionHelper;
import org.hibernate.exception.TemplatedViolatedConstraintNameExtracter;
import org.hibernate.exception.ViolatedConstraintNameExtracter;
import org.hibernate.id.SequenceGenerator;
import org.hibernate.type.descriptor.sql.BlobTypeDescriptor;
import org.hibernate.type.descriptor.sql.ClobTypeDescriptor;
import org.hibernate.type.descriptor.sql.SqlTypeDescriptor;
/**
* An SQL dialect for Postgres
@ -144,6 +147,30 @@ public class PostgreSQLDialect extends Dialect {
registerFunction( "str", new SQLFunctionTemplate(Hibernate.STRING, "cast(?1 as varchar)") );
getDefaultProperties().setProperty(Environment.STATEMENT_BATCH_SIZE, DEFAULT_BATCH_SIZE);
getDefaultProperties().setProperty( Environment.NON_CONTEXTUAL_LOB_CREATION, "true" );
}
/**
* {@inheritDoc}
*/
@Override
public SqlTypeDescriptor getSqlTypeDescriptorOverride(int sqlCode) {
SqlTypeDescriptor descriptor;
switch ( sqlCode ) {
case Types.BLOB: {
descriptor = BlobTypeDescriptor.BLOB_BINDING;
break;
}
case Types.CLOB: {
descriptor = ClobTypeDescriptor.CLOB_BINDING;
break;
}
default: {
descriptor = super.getSqlTypeDescriptorOverride( sqlCode );
break;
}
}
return descriptor;
}
public String getAddColumnString() {
@ -364,8 +391,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

@ -56,6 +56,10 @@ public abstract class AbstractSingleColumnStandardBasicType<T>
public LobCreator getLobCreator() {
return NonContextualLobCreator.INSTANCE;
}
public SqlTypeDescriptor resolveSqlTypeDescriptor(SqlTypeDescriptor sqlTypeDescriptor) {
return sqlTypeDescriptor;
}
};
public final int sqlType() {

View File

@ -244,13 +244,17 @@ public abstract class AbstractStandardBasicType<T>
public LobCreator getLobCreator() {
return Hibernate.getLobCreator( session );
}
public SqlTypeDescriptor resolveSqlTypeDescriptor(SqlTypeDescriptor sqlTypeDescriptor) {
return session.getFactory().getTypeResolver().resolveSqlTypeDescriptor( sqlTypeDescriptor );
}
};
return nullSafeGet( rs, name, options );
}
protected final T nullSafeGet(ResultSet rs, String name, WrapperOptions options) throws SQLException {
return sqlTypeDescriptor.getExtractor( javaTypeDescriptor ).extract( rs, name, options );
return resolveSqlTypeDescriptor( options ).getExtractor( javaTypeDescriptor ).extract( rs, name, options );
}
public Object get(ResultSet rs, String name, SessionImplementor session) throws HibernateException, SQLException {
@ -272,6 +276,10 @@ public abstract class AbstractStandardBasicType<T>
public LobCreator getLobCreator() {
return Hibernate.getLobCreator( session );
}
public SqlTypeDescriptor resolveSqlTypeDescriptor(SqlTypeDescriptor sqlTypeDescriptor) {
return session.getFactory().getTypeResolver().resolveSqlTypeDescriptor( sqlTypeDescriptor );
}
};
nullSafeSet( st, value, index, options );
@ -279,7 +287,11 @@ public abstract class AbstractStandardBasicType<T>
@SuppressWarnings({ "unchecked" })
protected final void nullSafeSet(PreparedStatement st, Object value, int index, WrapperOptions options) throws SQLException {
sqlTypeDescriptor.getBinder( javaTypeDescriptor ).bind( st, (T) value, index, options );
resolveSqlTypeDescriptor( options ).getBinder( javaTypeDescriptor ).bind( st, ( T ) value, index, options );
}
private SqlTypeDescriptor resolveSqlTypeDescriptor(WrapperOptions options) {
return options.resolveSqlTypeDescriptor( sqlTypeDescriptor );
}
public void set(PreparedStatement st, T value, int index, SessionImplementor session) throws HibernateException, SQLException {

View File

@ -37,7 +37,7 @@ public class BlobType extends AbstractSingleColumnStandardBasicType<Blob> {
public static final BlobType INSTANCE = new BlobType();
public BlobType() {
super( org.hibernate.type.descriptor.sql.BlobTypeDescriptor.INSTANCE, BlobTypeDescriptor.INSTANCE );
super( org.hibernate.type.descriptor.sql.BlobTypeDescriptor.DEFAULT, BlobTypeDescriptor.INSTANCE );
}
/**

View File

@ -38,7 +38,7 @@ public class CharacterArrayClobType extends AbstractSingleColumnStandardBasicTyp
public static final CharacterArrayClobType INSTANCE = new CharacterArrayClobType();
public CharacterArrayClobType() {
super( ClobTypeDescriptor.INSTANCE, CharacterArrayTypeDescriptor.INSTANCE );
super( ClobTypeDescriptor.DEFAULT, CharacterArrayTypeDescriptor.INSTANCE );
}
public String getName() {

View File

@ -37,7 +37,7 @@ public class ClobType extends AbstractSingleColumnStandardBasicType<Clob> {
public static final ClobType INSTANCE = new ClobType();
public ClobType() {
super( org.hibernate.type.descriptor.sql.ClobTypeDescriptor.INSTANCE, ClobTypeDescriptor.INSTANCE );
super( org.hibernate.type.descriptor.sql.ClobTypeDescriptor.DEFAULT, ClobTypeDescriptor.INSTANCE );
}
public String getName() {

View File

@ -38,7 +38,7 @@ public class MaterializedBlobType extends AbstractSingleColumnStandardBasicType<
public static final MaterializedBlobType INSTANCE = new MaterializedBlobType();
public MaterializedBlobType() {
super( BlobTypeDescriptor.INSTANCE, PrimitiveByteArrayTypeDescriptor.INSTANCE );
super( BlobTypeDescriptor.DEFAULT, PrimitiveByteArrayTypeDescriptor.INSTANCE );
}
public String getName() {

View File

@ -37,7 +37,7 @@ public class MaterializedClobType extends AbstractSingleColumnStandardBasicType<
public static final MaterializedClobType INSTANCE = new MaterializedClobType();
public MaterializedClobType() {
super( ClobTypeDescriptor.INSTANCE, StringTypeDescriptor.INSTANCE );
super( ClobTypeDescriptor.DEFAULT, StringTypeDescriptor.INSTANCE );
}
public String getName() {

View File

@ -35,7 +35,7 @@ public class PrimitiveCharacterArrayClobType extends AbstractSingleColumnStandar
public static final CharacterArrayClobType INSTANCE = new CharacterArrayClobType();
public PrimitiveCharacterArrayClobType() {
super( ClobTypeDescriptor.INSTANCE, PrimitiveCharacterArrayTypeDescriptor.INSTANCE );
super( ClobTypeDescriptor.DEFAULT, PrimitiveCharacterArrayTypeDescriptor.INSTANCE );
}
public String getName() {

View File

@ -23,6 +23,11 @@
*/
package org.hibernate.type;
import java.util.HashSet;
import java.util.Set;
import org.hibernate.type.descriptor.sql.SqlTypeDescriptor;
/**
* Centralizes access to the standard set of basic {@link Type types}.
* <p/>
@ -37,108 +42,111 @@ package org.hibernate.type;
* @author Steve Ebersole
*/
public class StandardBasicTypes {
private static final Set<SqlTypeDescriptor> sqlTypeDescriptors = new HashSet<SqlTypeDescriptor>();
/**
* The standard Hibernate type for mapping {@link Boolean} to JDBC {@link java.sql.Types#BIT BIT}.
*
* @see BooleanType
*/
public static final BooleanType BOOLEAN = BooleanType.INSTANCE;
public static final BooleanType BOOLEAN = register( BooleanType.INSTANCE );
/**
* The standard Hibernate type for mapping {@link Boolean} to JDBC {@link java.sql.Types#INTEGER INTEGER}.
*
* @see NumericBooleanType
*/
public static final NumericBooleanType NUMERIC_BOOLEAN = NumericBooleanType.INSTANCE;
public static final NumericBooleanType NUMERIC_BOOLEAN = register( NumericBooleanType.INSTANCE );
/**
* The standard Hibernate type for mapping {@link Boolean} to JDBC {@link java.sql.Types#CHAR CHAR(1)} (using 'T'/'F').
*
* @see TrueFalseType
*/
public static final TrueFalseType TRUE_FALSE = TrueFalseType.INSTANCE;
public static final TrueFalseType TRUE_FALSE = register( TrueFalseType.INSTANCE );
/**
* The standard Hibernate type for mapping {@link Boolean} to JDBC {@link java.sql.Types#CHAR CHAR(1)} (using 'Y'/'N').
*
* @see YesNoType
*/
public static final YesNoType YES_NO = YesNoType.INSTANCE;
public static final YesNoType YES_NO = register( YesNoType.INSTANCE );
/**
* The standard Hibernate type for mapping {@link Byte} to JDBC {@link java.sql.Types#TINYINT TINYINT}.
*/
public static final ByteType BYTE = ByteType.INSTANCE;
public static final ByteType BYTE = register( ByteType.INSTANCE );
/**
* The standard Hibernate type for mapping {@link Short} to JDBC {@link java.sql.Types#SMALLINT SMALLINT}.
*
* @see ShortType
*/
public static final ShortType SHORT = ShortType.INSTANCE;
public static final ShortType SHORT = register( ShortType.INSTANCE );
/**
* The standard Hibernate type for mapping {@link Integer} to JDBC {@link java.sql.Types#INTEGER INTEGER}.
*
* @see IntegerType
*/
public static final IntegerType INTEGER = IntegerType.INSTANCE;
public static final IntegerType INTEGER = register( IntegerType.INSTANCE );
/**
* The standard Hibernate type for mapping {@link Long} to JDBC {@link java.sql.Types#BIGINT BIGINT}.
*
* @see LongType
*/
public static final LongType LONG = LongType.INSTANCE;
public static final LongType LONG = register( LongType.INSTANCE );
/**
* The standard Hibernate type for mapping {@link Float} to JDBC {@link java.sql.Types#FLOAT FLOAT}.
*
* @see FloatType
*/
public static final FloatType FLOAT = FloatType.INSTANCE;
public static final FloatType FLOAT = register( FloatType.INSTANCE );
/**
* The standard Hibernate type for mapping {@link Double} to JDBC {@link java.sql.Types#DOUBLE DOUBLE}.
*
* @see DoubleType
*/
public static final DoubleType DOUBLE = DoubleType.INSTANCE;
public static final DoubleType DOUBLE = register( DoubleType.INSTANCE );
/**
* The standard Hibernate type for mapping {@link java.math.BigInteger} to JDBC {@link java.sql.Types#NUMERIC NUMERIC}.
*
* @see BigIntegerType
*/
public static final BigIntegerType BIG_INTEGER = BigIntegerType.INSTANCE;
public static final BigIntegerType BIG_INTEGER = register( BigIntegerType.INSTANCE );
/**
* The standard Hibernate type for mapping {@link java.math.BigDecimal} to JDBC {@link java.sql.Types#NUMERIC NUMERIC}.
*
* @see BigDecimalType
*/
public static final BigDecimalType BIG_DECIMAL = BigDecimalType.INSTANCE;
public static final BigDecimalType BIG_DECIMAL = register( BigDecimalType.INSTANCE );
/**
* The standard Hibernate type for mapping {@link Character} to JDBC {@link java.sql.Types#CHAR CHAR(1)}.
*
* @see CharacterType
*/
public static final CharacterType CHARACTER = CharacterType.INSTANCE;
public static final CharacterType CHARACTER = register( CharacterType.INSTANCE );
/**
* The standard Hibernate type for mapping {@link String} to JDBC {@link java.sql.Types#VARCHAR VARCHAR}.
*
* @see StringType
*/
public static final StringType STRING = StringType.INSTANCE;
public static final StringType STRING = register( StringType.INSTANCE );
/**
* The standard Hibernate type for mapping {@link java.net.URL} to JDBC {@link java.sql.Types#VARCHAR VARCHAR}.
*
* @see UrlType
*/
public static final UrlType URL = UrlType.INSTANCE;
public static final UrlType URL = register( UrlType.INSTANCE );
/**
* The standard Hibernate type for mapping {@link java.util.Date} ({@link java.sql.Time}) to JDBC
@ -146,7 +154,7 @@ public class StandardBasicTypes {
*
* @see TimeType
*/
public static final TimeType TIME = TimeType.INSTANCE;
public static final TimeType TIME = register( TimeType.INSTANCE );
/**
* The standard Hibernate type for mapping {@link java.util.Date} ({@link java.sql.Date}) to JDBC
@ -154,7 +162,7 @@ public class StandardBasicTypes {
*
* @see TimeType
*/
public static final DateType DATE = DateType.INSTANCE;
public static final DateType DATE = register( DateType.INSTANCE );
/**
* The standard Hibernate type for mapping {@link java.util.Date} ({@link java.sql.Timestamp}) to JDBC
@ -162,7 +170,7 @@ public class StandardBasicTypes {
*
* @see TimeType
*/
public static final TimestampType TIMESTAMP = TimestampType.INSTANCE;
public static final TimestampType TIMESTAMP = register( TimestampType.INSTANCE );
/**
* The standard Hibernate type for mapping {@link java.util.Calendar} to JDBC
@ -170,7 +178,7 @@ public class StandardBasicTypes {
*
* @see CalendarType
*/
public static final CalendarType CALENDAR = CalendarType.INSTANCE;
public static final CalendarType CALENDAR = register( CalendarType.INSTANCE );
/**
* The standard Hibernate type for mapping {@link java.util.Calendar} to JDBC
@ -178,63 +186,63 @@ public class StandardBasicTypes {
*
* @see CalendarDateType
*/
public static final CalendarDateType CALENDAR_DATE = CalendarDateType.INSTANCE;
public static final CalendarDateType CALENDAR_DATE = register( CalendarDateType.INSTANCE );
/**
* The standard Hibernate type for mapping {@link Class} to JDBC {@link java.sql.Types#VARCHAR VARCHAR}.
*
* @see ClassType
*/
public static final ClassType CLASS = ClassType.INSTANCE;
public static final ClassType CLASS = register( ClassType.INSTANCE );
/**
* The standard Hibernate type for mapping {@link java.util.Locale} to JDBC {@link java.sql.Types#VARCHAR VARCHAR}.
*
* @see LocaleType
*/
public static final LocaleType LOCALE = LocaleType.INSTANCE;
public static final LocaleType LOCALE = register( LocaleType.INSTANCE );
/**
* The standard Hibernate type for mapping {@link java.util.Currency} to JDBC {@link java.sql.Types#VARCHAR VARCHAR}.
*
* @see CurrencyType
*/
public static final CurrencyType CURRENCY = CurrencyType.INSTANCE;
public static final CurrencyType CURRENCY = register( CurrencyType.INSTANCE );
/**
* The standard Hibernate type for mapping {@link java.util.TimeZone} to JDBC {@link java.sql.Types#VARCHAR VARCHAR}.
*
* @see TimeZoneType
*/
public static final TimeZoneType TIMEZONE = TimeZoneType.INSTANCE;
public static final TimeZoneType TIMEZONE = register( TimeZoneType.INSTANCE );
/**
* The standard Hibernate type for mapping {@link java.util.UUID} to JDBC {@link java.sql.Types#BINARY BINARY}.
*
* @see UUIDBinaryType
*/
public static final UUIDBinaryType UUID_BINARY = UUIDBinaryType.INSTANCE;
public static final UUIDBinaryType UUID_BINARY = register( UUIDBinaryType.INSTANCE );
/**
* The standard Hibernate type for mapping {@link java.util.UUID} to JDBC {@link java.sql.Types#CHAR CHAR}.
*
* @see UUIDCharType
*/
public static final UUIDCharType UUID_CHAR = UUIDCharType.INSTANCE;
public static final UUIDCharType UUID_CHAR = register( UUIDCharType.INSTANCE );
/**
* The standard Hibernate type for mapping {@code byte[]} to JDBC {@link java.sql.Types#VARBINARY VARBINARY}.
*
* @see BinaryType
*/
public static final BinaryType BINARY = BinaryType.INSTANCE;
public static final BinaryType BINARY = register( BinaryType.INSTANCE );
/**
* The standard Hibernate type for mapping {@link Byte Byte[]} to JDBC {@link java.sql.Types#VARBINARY VARBINARY}.
*
* @see WrapperBinaryType
*/
public static final WrapperBinaryType WRAPPER_BINARY = WrapperBinaryType.INSTANCE;
public static final WrapperBinaryType WRAPPER_BINARY = register( WrapperBinaryType.INSTANCE );
/**
* The standard Hibernate type for mapping {@code byte[]} to JDBC {@link java.sql.Types#LONGVARBINARY LONGVARBINARY}.
@ -242,7 +250,7 @@ public class StandardBasicTypes {
* @see ImageType
* @see #MATERIALIZED_BLOB
*/
public static final ImageType IMAGE = ImageType.INSTANCE;
public static final ImageType IMAGE = register( ImageType.INSTANCE );
/**
* The standard Hibernate type for mapping {@link java.sql.Blob} to JDBC {@link java.sql.Types#BLOB BLOB}.
@ -250,7 +258,7 @@ public class StandardBasicTypes {
* @see BlobType
* @see #MATERIALIZED_BLOB
*/
public static final BlobType BLOB = BlobType.INSTANCE;
public static final BlobType BLOB = register( BlobType.INSTANCE );
/**
* The standard Hibernate type for mapping {@code byte[]} to JDBC {@link java.sql.Types#BLOB BLOB}.
@ -259,14 +267,14 @@ public class StandardBasicTypes {
* @see #MATERIALIZED_BLOB
* @see #IMAGE
*/
public static final MaterializedBlobType MATERIALIZED_BLOB = MaterializedBlobType.INSTANCE;
public static final MaterializedBlobType MATERIALIZED_BLOB = register( MaterializedBlobType.INSTANCE );
/**
* The standard Hibernate type for mapping {@code char[]} to JDBC {@link java.sql.Types#VARCHAR VARCHAR}.
*
* @see CharArrayType
*/
public static final CharArrayType CHAR_ARRAY = CharArrayType.INSTANCE;
public static final CharArrayType CHAR_ARRAY = register( CharArrayType.INSTANCE );
/**
* The standard Hibernate type for mapping {@link Character Character[]} to JDBC
@ -274,7 +282,7 @@ public class StandardBasicTypes {
*
* @see CharacterArrayType
*/
public static final CharacterArrayType CHARACTER_ARRAY = CharacterArrayType.INSTANCE;
public static final CharacterArrayType CHARACTER_ARRAY = register( CharacterArrayType.INSTANCE );
/**
* The standard Hibernate type for mapping {@link String} to JDBC {@link java.sql.Types#LONGVARCHAR LONGVARCHAR}.
@ -283,7 +291,7 @@ public class StandardBasicTypes {
*
* @see TextType
*/
public static final TextType TEXT = TextType.INSTANCE;
public static final TextType TEXT = register( TextType.INSTANCE );
/**
* The standard Hibernate type for mapping {@link java.sql.Clob} to JDBC {@link java.sql.Types#CLOB CLOB}.
@ -291,7 +299,7 @@ public class StandardBasicTypes {
* @see ClobType
* @see #MATERIALIZED_CLOB
*/
public static final ClobType CLOB = ClobType.INSTANCE;
public static final ClobType CLOB = register( ClobType.INSTANCE );
/**
* The standard Hibernate type for mapping {@link String} to JDBC {@link java.sql.Types#CLOB CLOB}.
@ -300,7 +308,7 @@ public class StandardBasicTypes {
* @see #MATERIALIZED_CLOB
* @see #TEXT
*/
public static final MaterializedClobType MATERIALIZED_CLOB = MaterializedClobType.INSTANCE;
public static final MaterializedClobType MATERIALIZED_CLOB = register( MaterializedClobType.INSTANCE );
/**
* The standard Hibernate type for mapping {@link java.io.Serializable} to JDBC {@link java.sql.Types#VARBINARY VARBINARY}.
@ -309,5 +317,14 @@ public class StandardBasicTypes {
*
* @see SerializableType
*/
public static final SerializableType SERIALIZABLE = SerializableType.INSTANCE;
public static final SerializableType SERIALIZABLE = register( SerializableType.INSTANCE );
private static <T extends AbstractSingleColumnStandardBasicType> T register(T type) {
sqlTypeDescriptors.add( type.getSqlTypeDescriptor() );
return type;
}
public static final boolean isStandardBasicSqlTypeDescriptor(SqlTypeDescriptor sqlTypeDescriptor) {
return sqlTypeDescriptors.contains( sqlTypeDescriptor );
}
}

View File

@ -86,6 +86,10 @@ public final class TypeFactory implements Serializable {
typeScope.injectSessionFactory( factory );
}
public SessionFactoryImplementor resolveSessionFactory() {
return typeScope.resolveFactory();
}
public Type byClass(Class clazz, Properties parameters) {
if ( Type.class.isAssignableFrom( clazz ) ) {
return type( (Class<Type>) clazz, parameters );

View File

@ -24,11 +24,17 @@
package org.hibernate.type;
import java.io.Serializable;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.hibernate.MappingException;
import org.hibernate.classic.Lifecycle;
import org.hibernate.engine.SessionFactoryImplementor;
import org.hibernate.type.descriptor.sql.SqlTypeDescriptor;
import org.hibernate.usertype.CompositeUserType;
import org.hibernate.usertype.UserType;
import org.hibernate.util.ReflectHelper;
@ -39,21 +45,31 @@ import org.hibernate.util.ReflectHelper;
* @author Steve Ebersole
*/
public class TypeResolver implements Serializable {
private static final Logger log = LoggerFactory.getLogger( TypeResolver.class );
private final BasicTypeRegistry basicTypeRegistry;
private final TypeFactory typeFactory;
private final Map<SqlTypeDescriptor, SqlTypeDescriptor> resolvedSqlTypeDescriptors;
public TypeResolver() {
this( new BasicTypeRegistry(), new TypeFactory() );
this( new BasicTypeRegistry(), new TypeFactory(), null );
}
public TypeResolver(BasicTypeRegistry basicTypeRegistry, TypeFactory typeFactory) {
public TypeResolver(BasicTypeRegistry basicTypeRegistry,
TypeFactory typeFactory,
Map<SqlTypeDescriptor, SqlTypeDescriptor> resolvedSqlTypeDescriptors) {
this.basicTypeRegistry = basicTypeRegistry;
this.typeFactory = typeFactory;
this.resolvedSqlTypeDescriptors = resolvedSqlTypeDescriptors;
}
public TypeResolver scope(SessionFactoryImplementor factory) {
typeFactory.injectSessionFactory( factory );
return new TypeResolver( basicTypeRegistry.shallowCopy(), typeFactory );
return new TypeResolver(
basicTypeRegistry.shallowCopy(),
typeFactory,
new HashMap<SqlTypeDescriptor, SqlTypeDescriptor>( 25 )
);
}
public void registerTypeOverride(BasicType type) {
@ -135,4 +151,39 @@ public class TypeResolver implements Serializable {
return null;
}
public SqlTypeDescriptor resolveSqlTypeDescriptor(SqlTypeDescriptor sqlTypeDescriptor) {
if ( resolvedSqlTypeDescriptors == null ) {
throw new IllegalStateException( "cannot resolve a SqlTypeDescriptor until the TypeResolver is scoped." );
}
SqlTypeDescriptor resolvedDescriptor = resolvedSqlTypeDescriptors.get( sqlTypeDescriptor );
if ( resolvedDescriptor == null ) {
resolvedDescriptor =
typeFactory.resolveSessionFactory().getDialect().resolveSqlTypeDescriptor( sqlTypeDescriptor );
if ( resolvedDescriptor == null ) {
throw new IllegalStateException( "dialect returned a resolved SqlTypeDescriptor that was null." );
}
if ( sqlTypeDescriptor != resolvedDescriptor ) {
log.info(
"Adding override for {}: {}",
new String[] {
sqlTypeDescriptor.getClass().getName(),
resolvedDescriptor.getClass().getName(),
}
);
if ( sqlTypeDescriptor.getSqlType() != resolvedDescriptor.getSqlType() ) {
log.warn( "Resolved SqlTypeDescriptor is for a different SQL code. {} has sqlCode={}; type override {} has sqlCode={}",
new String[] {
sqlTypeDescriptor.getClass().getName(),
String.valueOf( sqlTypeDescriptor.getSqlType() ),
resolvedDescriptor.getClass().getName(),
String.valueOf( resolvedDescriptor.getSqlType() ),
}
);
}
}
resolvedSqlTypeDescriptors.put( sqlTypeDescriptor, resolvedDescriptor );
}
return resolvedDescriptor;
}
}

View File

@ -36,7 +36,7 @@ public class WrappedMaterializedBlobType extends AbstractSingleColumnStandardBas
public static final WrappedMaterializedBlobType INSTANCE = new WrappedMaterializedBlobType();
public WrappedMaterializedBlobType() {
super( BlobTypeDescriptor.INSTANCE, ByteArrayTypeDescriptor.INSTANCE );
super( BlobTypeDescriptor.DEFAULT, ByteArrayTypeDescriptor.INSTANCE );
}
public String getName() {

View File

@ -23,7 +23,10 @@
*/
package org.hibernate.type.descriptor;
import org.hibernate.dialect.Dialect;
import org.hibernate.engine.jdbc.LobCreator;
import org.hibernate.type.TypeResolver;
import org.hibernate.type.descriptor.sql.SqlTypeDescriptor;
/**
* TODO : javadoc
@ -33,4 +36,5 @@ import org.hibernate.engine.jdbc.LobCreator;
public interface WrapperOptions {
public boolean useStreamForLobBinding();
public LobCreator getLobCreator();
public SqlTypeDescriptor resolveSqlTypeDescriptor(SqlTypeDescriptor sqlTypeDescriptor);
}

View File

@ -31,6 +31,7 @@ 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.WrapperOptions;
/**
@ -107,7 +108,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 +116,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,72 @@ 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, javaTypeDescriptor.unwrap( value, byte[].class, options ) );
}
};
}
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 +115,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

@ -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;

View File

@ -0,0 +1,39 @@
<?xml version="1.0"?>
<!--
~ 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
-->
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="org.hibernate.test.typeoverride">
<class name="Entity">
<id name="id">
<generator class="native"/>
</id>
<property name="name"/>
</class>
</hibernate-mapping>

View File

@ -0,0 +1,55 @@
/*
* 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.test.typeoverride;
/**
* @author Gail Badner
*/
public class Entity {
private long id;
private String name;
public Entity() {
}
public Entity(String name) {
this.name = name;
}
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}

View File

@ -0,0 +1,41 @@
/*
* 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.test.typeoverride;
import org.hibernate.dialect.H2Dialect;
import org.hibernate.type.descriptor.sql.SqlTypeDescriptor;
import org.hibernate.type.descriptor.sql.VarcharTypeDescriptor;
/**
*
* @author Gail Badner
*/
public class H2DialectOverridePrefixedVarcharSqlTypeDesc extends H2Dialect {
public SqlTypeDescriptor resolveSqlTypeDescriptor(SqlTypeDescriptor sqlTypeDescriptor) {
return sqlTypeDescriptor == StoredPrefixedStringType.INSTANCE.getSqlTypeDescriptor() ?
VarcharTypeDescriptor.INSTANCE :
super.resolveSqlTypeDescriptor( sqlTypeDescriptor );
}
}

View File

@ -0,0 +1,42 @@
/*
* 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.test.typeoverride;
import java.sql.Types;
import org.hibernate.dialect.H2Dialect;
import org.hibernate.type.descriptor.sql.SqlTypeDescriptor;
/**
*
* @author Gail Badner
*/
public class H2DialectOverrideVarcharSqlCode extends H2Dialect {
public SqlTypeDescriptor getSqlTypeDescriptorOverride(int sqlCode) {
return sqlCode == Types.VARCHAR ?
StoredPrefixedStringType.INSTANCE.getSqlTypeDescriptor() :
super.getSqlTypeDescriptorOverride( sqlCode );
}
}

View File

@ -0,0 +1,105 @@
/*
* 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.test.typeoverride;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import org.hibernate.AssertionFailure;
import org.hibernate.dialect.Dialect;
import org.hibernate.type.AbstractSingleColumnStandardBasicType;
import org.hibernate.type.DiscriminatorType;
import org.hibernate.type.StringType;
import org.hibernate.type.descriptor.ValueBinder;
import org.hibernate.type.descriptor.ValueExtractor;
import org.hibernate.type.descriptor.WrapperOptions;
import org.hibernate.type.descriptor.java.JavaTypeDescriptor;
import org.hibernate.type.descriptor.sql.BasicBinder;
import org.hibernate.type.descriptor.sql.BasicExtractor;
import org.hibernate.type.descriptor.sql.SqlTypeDescriptor;
import org.hibernate.type.descriptor.sql.VarcharTypeDescriptor;
/**
*
* @author Gail Badner
*/
public class StoredPrefixedStringType
extends AbstractSingleColumnStandardBasicType<String>
implements DiscriminatorType<String> {
public static final String PREFIX = "PRE:";
private static final SqlTypeDescriptor PREFIXED_VARCHAR_TYPE_DESCRIPTOR =
new VarcharTypeDescriptor() {
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 {
String stringValue = javaTypeDescriptor.unwrap( value, String.class, options );
st.setString( index, PREFIX + stringValue );
}
};
}
public <X> ValueExtractor<X> getExtractor(final JavaTypeDescriptor<X> javaTypeDescriptor) {
return new BasicExtractor<X>( javaTypeDescriptor, this ) {
@Override
protected X doExtract(ResultSet rs, String name, WrapperOptions options) throws SQLException {
String stringValue = rs.getString( name );
if ( ! stringValue.startsWith( PREFIX ) ) {
throw new AssertionFailure( "Value read from resultset does not have prefix." );
}
return javaTypeDescriptor.wrap( stringValue.substring( PREFIX.length() ), options );
}
};
}
};
public static final StoredPrefixedStringType INSTANCE = new StoredPrefixedStringType();
public StoredPrefixedStringType() {
super( PREFIXED_VARCHAR_TYPE_DESCRIPTOR, StringType.INSTANCE.getJavaTypeDescriptor() );
}
public String getName() {
return StringType.INSTANCE.getName();
}
@Override
protected boolean registerUnderJavaType() {
return true;
}
public String objectToSQLString(String value, Dialect dialect) throws Exception {
return StringType.INSTANCE.objectToSQLString( value, dialect );
}
public String stringToObject(String xml) throws Exception {
return StringType.INSTANCE.stringToObject( xml );
}
public String toString(String value) {
return StringType.INSTANCE.toString( value );
}
}

View File

@ -0,0 +1,208 @@
/*
* 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.test.typeoverride;
import org.hibernate.Session;
import org.hibernate.cfg.Configuration;
import org.hibernate.dialect.PostgreSQLDialect;
import org.hibernate.engine.SessionFactoryImplementor;
import org.hibernate.engine.SessionImplementor;
import org.hibernate.testing.junit.functional.FunctionalTestCase;
import org.hibernate.type.StandardBasicTypes;
import org.hibernate.type.descriptor.sql.BlobTypeDescriptor;
import org.hibernate.type.descriptor.sql.ClobTypeDescriptor;
import org.hibernate.type.descriptor.sql.IntegerTypeDescriptor;
import org.hibernate.type.descriptor.sql.SqlTypeDescriptor;
import org.hibernate.type.descriptor.sql.VarcharTypeDescriptor;
/**
*
* @author Gail Badner
*/
public class TypeOverrideTest extends FunctionalTestCase {
public TypeOverrideTest(String string) {
super( string );
}
public String[] getMappings() {
return new String[] { "typeoverride/Entity.hbm.xml" };
}
@Override
public void configure(Configuration cfg) {
cfg.registerTypeOverride( StoredPrefixedStringType.INSTANCE );
}
public void testStandardBasicSqlTypeDescriptor() {
// no override
assertTrue( StandardBasicTypes.isStandardBasicSqlTypeDescriptor( IntegerTypeDescriptor.INSTANCE ) );
assertSame( IntegerTypeDescriptor.INSTANCE, getResolvedSqlTypeDescriptor( IntegerTypeDescriptor.INSTANCE ) );
// override depends on Dialect.useInputStreamToInsertBlob();
// Postgresql explicitly overrides BlobTypeDescriptor.DEFAULT
assertTrue( StandardBasicTypes.isStandardBasicSqlTypeDescriptor( BlobTypeDescriptor.DEFAULT ) );
if ( getDialect().useInputStreamToInsertBlob() ) {
assertSame(
BlobTypeDescriptor.STREAM_BINDING,
getDialect().resolveSqlTypeDescriptor( BlobTypeDescriptor.DEFAULT )
);
}
else if ( PostgreSQLDialect.class.isInstance( getDialect() ) ) {
assertSame(
BlobTypeDescriptor.BLOB_BINDING,
getDialect().resolveSqlTypeDescriptor( BlobTypeDescriptor.DEFAULT )
);
}
else {
assertSame(
BlobTypeDescriptor.DEFAULT,
getDialect().resolveSqlTypeDescriptor( BlobTypeDescriptor.DEFAULT )
);
}
}
public void testNonStandardSqlTypeDescriptor() {
// no override
SqlTypeDescriptor sqlTypeDescriptor = new IntegerTypeDescriptor();
assertFalse( StandardBasicTypes.isStandardBasicSqlTypeDescriptor( sqlTypeDescriptor ) );
assertSame( sqlTypeDescriptor, getResolvedSqlTypeDescriptor( sqlTypeDescriptor ) );
// no override; (ClobTypeDescriptor.DEFAULT is overridden
// if Dialect.useInputStreamToInsertBlob() is true)
assertFalse( StandardBasicTypes.isStandardBasicSqlTypeDescriptor( ClobTypeDescriptor.CLOB_BINDING ) );
assertSame( ClobTypeDescriptor.CLOB_BINDING, getResolvedSqlTypeDescriptor( ClobTypeDescriptor.CLOB_BINDING ) );
}
public void testDialectWithNonStandardSqlTypeDescriptor() {
assertNotSame( VarcharTypeDescriptor.INSTANCE, StoredPrefixedStringType.INSTANCE.getSqlTypeDescriptor() );
if ( H2DialectOverridePrefixedVarcharSqlTypeDesc.class.isInstance( getDialect() ) ) {
// TODO: dialect is currently a global; how can this be tested in the testsuite?
assertSame(
VarcharTypeDescriptor.INSTANCE,
getResolvedSqlTypeDescriptor( StoredPrefixedStringType.INSTANCE.getSqlTypeDescriptor() )
);
}
else {
assertSame(
StoredPrefixedStringType.INSTANCE.getSqlTypeDescriptor(),
getResolvedSqlTypeDescriptor( StoredPrefixedStringType.INSTANCE.getSqlTypeDescriptor() )
);
}
if ( H2DialectOverrideVarcharSqlCode.class.isInstance( getDialect() ) ) {
// TODO: dialect is currently a global; how can this be tested in the testsuite?
assertSame(
StoredPrefixedStringType.INSTANCE.getSqlTypeDescriptor(),
getResolvedSqlTypeDescriptor( VarcharTypeDescriptor.INSTANCE )
);
}
else {
assertSame(
VarcharTypeDescriptor.INSTANCE,
getResolvedSqlTypeDescriptor( VarcharTypeDescriptor.INSTANCE )
);
}
}
private SqlTypeDescriptor getResolvedSqlTypeDescriptor(SqlTypeDescriptor sqlTypeDescriptor) {
return ( ( SessionFactoryImplementor ) getSessions() )
.getTypeResolver()
.resolveSqlTypeDescriptor( sqlTypeDescriptor );
}
public void testInsert() {
Session s = openSession();
s.getTransaction().begin();
Entity e = new Entity( "name" );
s.save( e );
s.getTransaction().commit();
s.close();
s = openSession();
s.getTransaction().begin();
e = ( Entity ) s.get( Entity.class, e.getId() );
assertFalse( e.getName().startsWith( StoredPrefixedStringType.PREFIX ) );
assertEquals( "name", e.getName() );
s.delete( e );
s.getTransaction().commit();
s.close();
}
public void testRegisteredFunction() {
Session s = openSession();
s.getTransaction().begin();
Entity e = new Entity( "name " );
s.save( e );
s.getTransaction().commit();
s.close();
s = openSession();
s.getTransaction().begin();
e = ( Entity ) s.get( Entity.class, e.getId() );
assertFalse( e.getName().startsWith( StoredPrefixedStringType.PREFIX ) );
assertEquals( "name ", e.getName() );
s.getTransaction().commit();
s.close();
s = openSession();
s.getTransaction().begin();
String trimmedName = ( String ) s.createQuery( "select trim( TRAILING from e.name ) from Entity e" ).uniqueResult();
// trim(...) is a "standard" DB function returning VarcharTypeDescriptor.INSTANCE,
// so the prefix will not be removed unless
// 1) getDialect().getSqlTypeDescriptorOverride( VarcharTypeDescriptor.INSTANCE )
// returns StoredPrefixedStringType.INSTANCE.getSqlTypeDescriptor()
// (H2DialectOverrideVarcharSqlCode does this)
// or 2) getDialect().getSqlTypeDescriptorOverride( StoredPrefixedStringType.INSTANCE.getSqlTypeDescriptor() )
// returns VarcharTypeDescriptor.INSTANCE
// (H2DialectOverridePrefixedVarcharSqlTypeDesc does this)
// TODO: dialect is currently a global; how can this be tested in the testsuite?
assertNotSame( VarcharTypeDescriptor.INSTANCE, StoredPrefixedStringType.INSTANCE.getSqlTypeDescriptor() );
if ( getDialect().resolveSqlTypeDescriptor( VarcharTypeDescriptor.INSTANCE ) ==
StoredPrefixedStringType.INSTANCE.getSqlTypeDescriptor() ||
getDialect().resolveSqlTypeDescriptor( StoredPrefixedStringType.INSTANCE.getSqlTypeDescriptor() ) ==
VarcharTypeDescriptor.INSTANCE ) {
assertFalse( trimmedName.startsWith( StoredPrefixedStringType.PREFIX ) );
assertEquals( "name", trimmedName );
}
else {
assertSame(
VarcharTypeDescriptor.INSTANCE,
( ( SessionFactoryImplementor ) getSessions() )
.getTypeResolver()
.resolveSqlTypeDescriptor( VarcharTypeDescriptor.INSTANCE )
);
assertTrue( trimmedName.startsWith( StoredPrefixedStringType.PREFIX ) );
assertEquals( StoredPrefixedStringType.PREFIX + "name", trimmedName );
}
s.delete( e );
s.getTransaction().commit();
s.close();
}
}

View File

@ -36,8 +36,6 @@ import org.hibernate.type.descriptor.ValueBinder;
import org.hibernate.type.descriptor.ValueExtractor;
import org.hibernate.type.descriptor.WrapperOptions;
import org.hibernate.type.descriptor.java.StringTypeDescriptor;
import org.hibernate.type.descriptor.sql.ClobTypeDescriptor;
import org.hibernate.type.descriptor.sql.VarcharTypeDescriptor;
/**
* TODO : javadoc
@ -48,7 +46,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() {
@ -58,6 +56,10 @@ public class StringValueMappingTest extends TestCase {
public LobCreator getLobCreator() {
return NonContextualLobCreator.INSTANCE;
}
public SqlTypeDescriptor resolveSqlTypeDescriptor(SqlTypeDescriptor sqlTypeDescriptor) {
return sqlTypeDescriptor;
}
};
public static final String COLUMN_NAME = "n/a";