HHH-7387 - Integrate Draft 6 of the JPA 2.1 spec : AttributeConverter

This commit is contained in:
Steve Ebersole 2012-07-05 17:48:02 -05:00
parent 153eb4a913
commit ad2a9ef651
57 changed files with 1827 additions and 147 deletions

View File

@ -279,6 +279,7 @@ public final class AnnotationBinder {
bindTypeDefs( pckg, mappings );
bindFetchProfiles( pckg, mappings );
BinderHelper.bindAnyMetaDefs( pckg, mappings );
}
private static void bindGenericGenerators(XAnnotatedElement annotatedElement, Mappings mappings) {

View File

@ -0,0 +1,124 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2012, 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.cfg;
import javax.persistence.AttributeConverter;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.lang.reflect.TypeVariable;
import org.jboss.logging.Logger;
import org.hibernate.AnnotationException;
import org.hibernate.AssertionFailure;
/**
* @author Steve Ebersole
*/
public class AttributeConverterDefinition {
private static final Logger log = Logger.getLogger( AttributeConverterDefinition.class );
private final AttributeConverter attributeConverter;
private final boolean autoApply;
private final Class entityAttributeType;
private final Class databaseColumnType;
public AttributeConverterDefinition(AttributeConverter attributeConverter, boolean autoApply) {
this.attributeConverter = attributeConverter;
this.autoApply = autoApply;
final Class attributeConverterClass = attributeConverter.getClass();
final ParameterizedType attributeConverterSignature = extractAttributeConverterParameterizedType( attributeConverterClass );
if ( attributeConverterSignature.getActualTypeArguments().length < 2 ) {
throw new AnnotationException(
"AttributeConverter [" + attributeConverterClass.getName()
+ "] did not retain parameterized type information"
);
}
if ( attributeConverterSignature.getActualTypeArguments().length > 2 ) {
throw new AnnotationException(
"AttributeConverter [" + attributeConverterClass.getName()
+ "] specified more than 2 parameterized types"
);
}
entityAttributeType = (Class) attributeConverterSignature.getActualTypeArguments()[0];
if ( entityAttributeType == null ) {
throw new AnnotationException(
"Could not determine 'entity attribute' type from given AttributeConverter [" +
attributeConverterClass.getName() + "]"
);
}
databaseColumnType = (Class) attributeConverterSignature.getActualTypeArguments()[1];
if ( databaseColumnType == null ) {
throw new AnnotationException(
"Could not determine 'database column' type from given AttributeConverter [" +
attributeConverterClass.getName() + "]"
);
}
}
private ParameterizedType extractAttributeConverterParameterizedType(Class attributeConverterClass) {
for ( Type type : attributeConverterClass.getGenericInterfaces() ) {
if ( ParameterizedType.class.isInstance( type ) ) {
final ParameterizedType parameterizedType = (ParameterizedType) type;
if ( AttributeConverter.class.equals( parameterizedType.getRawType() ) ) {
return parameterizedType;
}
}
}
throw new AssertionFailure(
"Could not extract ParameterizedType representation of AttributeConverter definition " +
"from AttributeConverter implementation class [" + attributeConverterClass.getName() + "]"
);
}
public AttributeConverter getAttributeConverter() {
return attributeConverter;
}
public boolean isAutoApply() {
return autoApply;
}
public Class getEntityAttributeType() {
return entityAttributeType;
}
public Class getDatabaseColumnType() {
return databaseColumnType;
}
private static Class extractType(TypeVariable typeVariable) {
java.lang.reflect.Type[] boundTypes = typeVariable.getBounds();
if ( boundTypes == null || boundTypes.length != 1 ) {
return null;
}
return (Class) boundTypes[0];
}
}

View File

@ -48,8 +48,10 @@ import java.util.Properties;
import java.util.Set;
import java.util.StringTokenizer;
import java.util.TreeMap;
import java.util.concurrent.ConcurrentHashMap;
import java.util.jar.JarFile;
import java.util.zip.ZipEntry;
import javax.persistence.AttributeConverter;
import javax.persistence.Embeddable;
import javax.persistence.Entity;
import javax.persistence.MapsId;
@ -63,6 +65,7 @@ import org.xml.sax.EntityResolver;
import org.xml.sax.InputSource;
import org.hibernate.AnnotationException;
import org.hibernate.AssertionFailure;
import org.hibernate.DuplicateMappingException;
import org.hibernate.EmptyInterceptor;
import org.hibernate.HibernateException;
@ -259,6 +262,7 @@ public class Configuration implements Serializable {
private CurrentTenantIdentifierResolver currentTenantIdentifierResolver;
private boolean specjProprietarySyntaxEnabled;
private ConcurrentHashMap<Class,AttributeConverterDefinition> attributeConverterDefinitionsByClass;
protected Configuration(SettingsFactory settingsFactory) {
this.settingsFactory = settingsFactory;
@ -2450,6 +2454,52 @@ public class Configuration implements Serializable {
this.currentTenantIdentifierResolver = currentTenantIdentifierResolver;
}
/**
* Adds the AttributeConverter Class to this Configuration.
*
* @param attributeConverterClass The AttributeConverter class.
* @param autoApply Should the AttributeConverter be auto applied to property types as specified
* by its "entity attribute" parameterized type?
*/
public void addAttributeConverter(Class<? extends AttributeConverter> attributeConverterClass, boolean autoApply) {
final AttributeConverter attributeConverter;
try {
attributeConverter = attributeConverterClass.newInstance();
}
catch (Exception e) {
throw new AnnotationException(
"Unable to instantiate AttributeConverter [" + attributeConverterClass.getName() + "]"
);
}
addAttributeConverter( attributeConverter, autoApply );
}
/**
* Adds the AttributeConverter instance to this Configuration. This form is mainly intended for developers
* to programatically add their own AttributeConverter instance. HEM, instead, uses the
* {@link #addAttributeConverter(Class, boolean)} form
*
* @param attributeConverter The AttributeConverter instance.
* @param autoApply Should the AttributeConverter be auto applied to property types as specified
* by its "entity attribute" parameterized type?
*/
public void addAttributeConverter(AttributeConverter attributeConverter, boolean autoApply) {
if ( attributeConverterDefinitionsByClass == null ) {
attributeConverterDefinitionsByClass = new ConcurrentHashMap<Class, AttributeConverterDefinition>();
}
final Object old = attributeConverterDefinitionsByClass.put(
attributeConverter.getClass(),
new AttributeConverterDefinition( attributeConverter, autoApply )
);
if ( old != null ) {
throw new AssertionFailure(
"AttributeConverter class [" + attributeConverter.getClass() + "] registered multiple times"
);
}
}
// Mappings impl ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@ -2984,6 +3034,22 @@ public class Configuration implements Serializable {
}
}
@Override
public AttributeConverterDefinition locateAttributeConverter(Class converterClass) {
if ( attributeConverterDefinitionsByClass == null ) {
return null;
}
return attributeConverterDefinitionsByClass.get( converterClass );
}
@Override
public java.util.Collection<AttributeConverterDefinition> getAttributeConverters() {
if ( attributeConverterDefinitionsByClass == null ) {
return Collections.emptyList();
}
return attributeConverterDefinitionsByClass.values();
}
public void addPropertyReference(String referencedClass, String propertyName) {
propertyReferences.add( new PropertyReference( referencedClass, propertyName, false ) );
}

View File

@ -23,6 +23,7 @@
*/
package org.hibernate.cfg;
import javax.persistence.AttributeConverter;
import java.io.Serializable;
import java.util.Iterator;
import java.util.List;
@ -509,6 +510,23 @@ public interface Mappings {
*/
public void addSecondPass(SecondPass sp, boolean onTopOfTheQueue);
/**
* Locate the AttributeConverterDefinition corresponding to the given AttributeConverter Class.
*
* @param attributeConverterClass The AttributeConverter Class for which to get the definition
*
* @return The corresponding AttributeConverter definition; will return {@code null} if no corresponding
* definition found.
*/
public AttributeConverterDefinition locateAttributeConverter(Class attributeConverterClass);
/**
* All all AttributeConverter definitions
*
* @return The collection of all AttributeConverter definitions.
*/
public java.util.Collection<AttributeConverterDefinition> getAttributeConverters();
/**
* Represents a property-ref mapping.
* <p/>

View File

@ -24,11 +24,16 @@
package org.hibernate.cfg.annotations;
import java.io.Serializable;
import java.lang.reflect.TypeVariable;
import java.sql.Types;
import java.util.Calendar;
import java.util.Date;
import java.util.Properties;
import javax.persistence.AttributeConverter;
import javax.persistence.Convert;
import javax.persistence.Converts;
import javax.persistence.Enumerated;
import javax.persistence.Id;
import javax.persistence.Lob;
import javax.persistence.MapKeyEnumerated;
import javax.persistence.MapKeyTemporal;
@ -43,6 +48,8 @@ import org.hibernate.annotations.Parameter;
import org.hibernate.annotations.Type;
import org.hibernate.annotations.common.reflection.XClass;
import org.hibernate.annotations.common.reflection.XProperty;
import org.hibernate.annotations.common.util.ReflectHelper;
import org.hibernate.cfg.AttributeConverterDefinition;
import org.hibernate.cfg.BinderHelper;
import org.hibernate.cfg.Ejb3Column;
import org.hibernate.cfg.Ejb3JoinColumn;
@ -82,6 +89,8 @@ public class SimpleValueBinder {
private boolean key;
private String referencedEntityName;
private AttributeConverterDefinition attributeConverterDefinition;
public void setReferencedEntityName(String referencedEntityName) {
this.referencedEntityName = referencedEntityName;
}
@ -172,7 +181,6 @@ public class SimpleValueBinder {
}
}
else if ( property.isAnnotationPresent( Lob.class ) ) {
if ( mappings.getReflectionManager().equals( returnedClassOrElement, java.sql.Clob.class ) ) {
type = "clob";
}
@ -243,6 +251,201 @@ public class SimpleValueBinder {
this.typeParameters = typeParameters;
Type annType = property.getAnnotation( Type.class );
setExplicitType( annType );
applyAttributeConverter( property );
}
private void applyAttributeConverter(XProperty property) {
final boolean canBeConverted = ! property.isAnnotationPresent( Id.class )
&& ! isVersion
&& ! isAssociation()
&& ! property.isAnnotationPresent( Temporal.class )
&& ! property.isAnnotationPresent( Enumerated.class );
if ( canBeConverted ) {
// @Convert annotations take precedence
final Convert convertAnnotation = locateConvertAnnotation( property );
if ( convertAnnotation != null ) {
if ( ! convertAnnotation.disableConversion() ) {
attributeConverterDefinition = mappings.locateAttributeConverter( convertAnnotation.converter() );
}
}
else {
attributeConverterDefinition = locateAutoApplyAttributeConverter( property );
}
}
}
private AttributeConverterDefinition locateAutoApplyAttributeConverter(XProperty property) {
final Class propertyType = mappings.getReflectionManager().toClass( property.getType() );
for ( AttributeConverterDefinition attributeConverterDefinition : mappings.getAttributeConverters() ) {
if ( areTypeMatch( attributeConverterDefinition.getEntityAttributeType(), propertyType ) ) {
return attributeConverterDefinition;
}
}
return null;
}
private boolean isAssociation() {
// todo : this information is only known to caller(s), need to pass that information in somehow.
// or, is this enough?
return referencedEntityName != null;
}
@SuppressWarnings("unchecked")
private Convert locateConvertAnnotation(XProperty property) {
// first look locally on the property for @Convert
Convert localConvertAnnotation = property.getAnnotation( Convert.class );
if ( localConvertAnnotation != null ) {
return localConvertAnnotation;
}
if ( persistentClassName == null ) {
LOG.debug( "Persistent Class name not known during attempt to locate @Convert annotations" );
return null;
}
final XClass owner;
try {
final Class ownerClass = ReflectHelper.classForName( persistentClassName );
owner = mappings.getReflectionManager().classForName( persistentClassName, ownerClass );
}
catch (ClassNotFoundException e) {
throw new AnnotationException( "Unable to resolve Class reference during attempt to locate @Convert annotations" );
}
return lookForEntityDefinedConvertAnnotation( property, owner );
}
private Convert lookForEntityDefinedConvertAnnotation(XProperty property, XClass owner) {
if ( owner == null ) {
// we have hit the root of the entity hierarchy
return null;
}
{
Convert convertAnnotation = owner.getAnnotation( Convert.class );
if ( convertAnnotation != null && isMatch( convertAnnotation, property ) ) {
return convertAnnotation;
}
}
{
Converts convertsAnnotation = owner.getAnnotation( Converts.class );
if ( convertsAnnotation != null ) {
for ( Convert convertAnnotation : convertsAnnotation.value() ) {
if ( isMatch( convertAnnotation, property ) ) {
return convertAnnotation;
}
}
}
}
// finally, look on superclass
return lookForEntityDefinedConvertAnnotation( property, owner.getSuperclass() );
}
@SuppressWarnings("unchecked")
private boolean isMatch(Convert convertAnnotation, XProperty property) {
return property.getName().equals( convertAnnotation.attributeName() )
&& isTypeMatch( convertAnnotation.converter(), property );
}
private boolean isTypeMatch(Class<? extends AttributeConverter> attributeConverterClass, XProperty property) {
return areTypeMatch(
extractEntityAttributeType( attributeConverterClass ),
mappings.getReflectionManager().toClass( property.getType() )
);
}
private Class extractEntityAttributeType(Class<? extends AttributeConverter> attributeConverterClass) {
// this is duplicated in SimpleValue...
final TypeVariable[] attributeConverterTypeInformation = attributeConverterClass.getTypeParameters();
if ( attributeConverterTypeInformation == null || attributeConverterTypeInformation.length < 2 ) {
throw new AnnotationException(
"AttributeConverter [" + attributeConverterClass.getName()
+ "] did not retain parameterized type information"
);
}
if ( attributeConverterTypeInformation.length > 2 ) {
LOG.debug(
"AttributeConverter [" + attributeConverterClass.getName()
+ "] specified more than 2 parameterized types"
);
}
final Class entityAttributeJavaType = extractType( attributeConverterTypeInformation[0] );
if ( entityAttributeJavaType == null ) {
throw new AnnotationException(
"Could not determine 'entity attribute' type from given AttributeConverter [" +
attributeConverterClass.getName() + "]"
);
}
return entityAttributeJavaType;
}
private Class extractType(TypeVariable typeVariable) {
java.lang.reflect.Type[] boundTypes = typeVariable.getBounds();
if ( boundTypes == null || boundTypes.length != 1 ) {
return null;
}
return (Class) boundTypes[0];
}
private boolean areTypeMatch(Class converterDefinedType, Class propertyType) {
if ( converterDefinedType == null ) {
throw new AnnotationException( "AttributeConverter defined java type cannot be null" );
}
if ( propertyType == null ) {
throw new AnnotationException( "Property defined java type cannot be null" );
}
return converterDefinedType.equals( propertyType )
|| arePrimitiveWrapperEquivalents( converterDefinedType, propertyType );
}
private boolean arePrimitiveWrapperEquivalents(Class converterDefinedType, Class propertyType) {
if ( converterDefinedType.isPrimitive() ) {
return getWrapperEquivalent( converterDefinedType ).equals( propertyType );
}
else if ( propertyType.isPrimitive() ) {
return getWrapperEquivalent( propertyType ).equals( converterDefinedType );
}
return false;
}
private static Class getWrapperEquivalent(Class primitive) {
if ( ! primitive.isPrimitive() ) {
throw new AssertionFailure( "Passed type for which to locate wrapper equivalent was not a primitive" );
}
if ( boolean.class.equals( primitive ) ) {
return Boolean.class;
}
else if ( char.class.equals( primitive ) ) {
return Character.class;
}
else if ( byte.class.equals( primitive ) ) {
return Byte.class;
}
else if ( short.class.equals( primitive ) ) {
return Short.class;
}
else if ( int.class.equals( primitive ) ) {
return Integer.class;
}
else if ( long.class.equals( primitive ) ) {
return Long.class;
}
else if ( float.class.equals( primitive ) ) {
return Float.class;
}
else if ( double.class.equals( primitive ) ) {
return Double.class;
}
throw new AssertionFailure( "Unexpected primitive type (VOID most likely) passed to getWrapperEquivalent" );
}
private javax.persistence.EnumType getEnumType(XProperty property) {
@ -338,21 +541,36 @@ public class SimpleValueBinder {
}
public void fillSimpleValue() {
LOG.debugf( "Setting SimpleValue typeName for %s", propertyName );
String type = BinderHelper.isEmptyAnnotationValue( explicitType ) ? returnedClassName : explicitType;
org.hibernate.mapping.TypeDef typeDef = mappings.getTypeDef( type );
if ( typeDef != null ) {
type = typeDef.getTypeClass();
simpleValue.setTypeParameters( typeDef.getParameters() );
if ( attributeConverterDefinition != null ) {
if ( ! BinderHelper.isEmptyAnnotationValue( explicitType ) ) {
throw new AnnotationException(
String.format(
"AttributeConverter and explicit Type cannot be applied to same attribute [%s.%s];" +
"remove @Type or specify @Convert(disableConversion = true)",
persistentClassName,
propertyName
)
);
}
simpleValue.setJpaAttributeConverterDefinition( attributeConverterDefinition );
}
if ( typeParameters != null && typeParameters.size() != 0 ) {
//explicit type params takes precedence over type def params
simpleValue.setTypeParameters( typeParameters );
else {
String type = BinderHelper.isEmptyAnnotationValue( explicitType ) ? returnedClassName : explicitType;
org.hibernate.mapping.TypeDef typeDef = mappings.getTypeDef( type );
if ( typeDef != null ) {
type = typeDef.getTypeClass();
simpleValue.setTypeParameters( typeDef.getParameters() );
}
if ( typeParameters != null && typeParameters.size() != 0 ) {
//explicit type params takes precedence over type def params
simpleValue.setTypeParameters( typeParameters );
}
simpleValue.setTypeName( type );
}
simpleValue.setTypeName( type );
if ( persistentClassName != null ) {
if ( persistentClassName != null || attributeConverterDefinition != null ) {
simpleValue.setTypeUsingReflection( persistentClassName, propertyName );
}
@ -369,4 +587,5 @@ public class SimpleValueBinder {
public void setKey(boolean key) {
this.key = key;
}
}

View File

@ -22,13 +22,24 @@
* Boston, MA 02110-1301 USA
*/
package org.hibernate.mapping;
import javax.persistence.AttributeConverter;
import java.lang.reflect.TypeVariable;
import java.sql.CallableStatement;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Properties;
import org.jboss.logging.Logger;
import org.hibernate.AnnotationException;
import org.hibernate.FetchMode;
import org.hibernate.MappingException;
import org.hibernate.cfg.AttributeConverterDefinition;
import org.hibernate.cfg.Environment;
import org.hibernate.cfg.Mappings;
import org.hibernate.dialect.Dialect;
@ -38,13 +49,26 @@ import org.hibernate.id.IdentityGenerator;
import org.hibernate.id.PersistentIdentifierGenerator;
import org.hibernate.id.factory.IdentifierGeneratorFactory;
import org.hibernate.internal.util.ReflectHelper;
import org.hibernate.type.AbstractSingleColumnStandardBasicType;
import org.hibernate.type.Type;
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.java.JavaTypeDescriptorRegistry;
import org.hibernate.type.descriptor.sql.BasicBinder;
import org.hibernate.type.descriptor.sql.BasicExtractor;
import org.hibernate.type.descriptor.sql.JdbcTypeJavaClassMappings;
import org.hibernate.type.descriptor.sql.SqlTypeDescriptor;
import org.hibernate.type.descriptor.sql.SqlTypeDescriptorRegistry;
/**
* Any value that maps to columns.
* @author Gavin King
*/
public class SimpleValue implements KeyValue {
private static final Logger log = Logger.getLogger( SimpleValue.class );
public static final String DEFAULT_ID_GEN_STRATEGY = "assigned";
private final Mappings mappings;
@ -60,6 +84,9 @@ public class SimpleValue implements KeyValue {
private Properties typeParameters;
private boolean cascadeDeleteEnabled;
private AttributeConverterDefinition jpaAttributeConverterDefinition;
private Type type;
public SimpleValue(Mappings mappings) {
this.mappings = mappings;
}
@ -290,30 +317,100 @@ public class SimpleValue implements KeyValue {
}
public Type getType() throws MappingException {
if (typeName==null) {
throw new MappingException("No type name");
if ( type != null ) {
return type;
}
Type result = mappings.getTypeResolver().heuristicType(typeName, typeParameters);
if (result==null) {
if ( typeName == null ) {
throw new MappingException( "No type name" );
}
Type result = mappings.getTypeResolver().heuristicType( typeName, typeParameters );
if ( result == null ) {
String msg = "Could not determine type for: " + typeName;
if(table != null){
if ( table != null ) {
msg += ", at table: " + table.getName();
}
if(columns!=null && columns.size()>0) {
if ( columns!=null && columns.size()>0 ) {
msg += ", for columns: " + columns;
}
throw new MappingException(msg);
throw new MappingException( msg );
}
return result;
}
@SuppressWarnings("unchecked")
public void setTypeUsingReflection(String className, String propertyName) throws MappingException {
if (typeName==null) {
if (className==null) {
throw new MappingException("you must specify types for a dynamic entity: " + propertyName);
}
typeName = ReflectHelper.reflectedPropertyClass(className, propertyName).getName();
// NOTE : this is called as the last piece in setting SimpleValue type information, and implementations
// rely on that fact, using it as a signal that all information it is going to get is defined at this point...
if ( typeName != null ) {
// assume either (a) explicit type was specified or (b) determine was already performed
return;
}
if ( type != null ) {
return;
}
if ( jpaAttributeConverterDefinition == null ) {
// this is here to work like legacy. This should change when we integrate with metamodel to
// look for SqlTypeDescriptor and JavaTypeDescriptor individually and create the BasicType (well, really
// keep a registry of [SqlTypeDescriptor,JavaTypeDescriptor] -> BasicType...)
if ( className == null ) {
throw new MappingException( "you must specify types for a dynamic entity: " + propertyName );
}
typeName = ReflectHelper.reflectedPropertyClass( className, propertyName ).getName();
return;
}
// we had an AttributeConverter...
// todo : we should validate the number of columns present
// todo : ultimately I want to see attributeConverterJavaType and attributeConverterJdbcTypeCode specify-able separately
// then we can "play them against each other" in terms of determining proper typing
// todo : see if we already have previously built a custom on-the-fly BasicType for this AttributeConverter; see note below about caching
// AttributeConverter works totally in memory, meaning it converts between one Java representation (the entity
// attribute representation) and another (the value bound into JDBC statements or extracted from results).
// However, the Hibernate Type system operates at the lower level of actually dealing with those JDBC objects.
// So even though we have an AttributeConverter, we still need to "fill out" the rest of the BasicType
// data. For the JavaTypeDescriptor portion we simply resolve the "entity attribute representation" part of
// the AttributeConverter to resolve the corresponding descriptor. For the SqlTypeDescriptor portion we use the
// "database column representation" part of the AttributeConverter to resolve the "recommended" JDBC type-code
// and use that type-code to resolve the SqlTypeDescriptor to use.
final Class entityAttributeJavaType = jpaAttributeConverterDefinition.getEntityAttributeType();
final Class databaseColumnJavaType = jpaAttributeConverterDefinition.getDatabaseColumnType();
final int jdbcTypeCode = JdbcTypeJavaClassMappings.INSTANCE.determineJdbcTypeCodeForJavaClass( databaseColumnJavaType );
final JavaTypeDescriptor javaTypeDescriptor = JavaTypeDescriptorRegistry.INSTANCE.getDescriptor( entityAttributeJavaType );
final SqlTypeDescriptor sqlTypeDescriptor = SqlTypeDescriptorRegistry.INSTANCE.getDescriptor( jdbcTypeCode );
// the adapter here injects the AttributeConverter calls into the binding/extraction process...
final SqlTypeDescriptor sqlTypeDescriptorAdapter = new AttributeConverterSqlTypeDescriptorAdapter(
jpaAttributeConverterDefinition.getAttributeConverter(),
sqlTypeDescriptor
);
final String name = "BasicType adapter for AttributeConverter<" + entityAttributeJavaType + "," + databaseColumnJavaType + ">";
type = new AbstractSingleColumnStandardBasicType( sqlTypeDescriptorAdapter, javaTypeDescriptor ) {
@Override
public String getName() {
return name;
}
};
log.debug( "Created : " + name );
// todo : cache the BasicType we just created in case that AttributeConverter is applied multiple times.
}
private Class extractType(TypeVariable typeVariable) {
java.lang.reflect.Type[] boundTypes = typeVariable.getBounds();
if ( boundTypes == null || boundTypes.length != 1 ) {
return null;
}
return (Class) boundTypes[0];
}
public boolean isTypeSpecified() {
@ -351,4 +448,60 @@ public class SimpleValue implements KeyValue {
public boolean[] getColumnUpdateability() {
return getColumnInsertability();
}
public void setJpaAttributeConverterDefinition(AttributeConverterDefinition jpaAttributeConverterDefinition) {
this.jpaAttributeConverterDefinition = jpaAttributeConverterDefinition;
}
public static class AttributeConverterSqlTypeDescriptorAdapter implements SqlTypeDescriptor {
private final AttributeConverter converter;
private final SqlTypeDescriptor delegate;
public AttributeConverterSqlTypeDescriptorAdapter(AttributeConverter converter, SqlTypeDescriptor delegate) {
this.converter = converter;
this.delegate = delegate;
}
@Override
public int getSqlType() {
return delegate.getSqlType();
}
@Override
public boolean canBeRemapped() {
return delegate.canBeRemapped();
}
@Override
public <X> ValueBinder<X> getBinder(JavaTypeDescriptor<X> javaTypeDescriptor) {
final ValueBinder realBinder = delegate.getBinder( javaTypeDescriptor );
return new BasicBinder<X>( javaTypeDescriptor, this ) {
@Override
@SuppressWarnings("unchecked")
protected void doBind(PreparedStatement st, X value, int index, WrapperOptions options)
throws SQLException {
realBinder.bind( st, converter.convertToDatabaseColumn( value ), index, options );
}
};
}
@Override
public <X> ValueExtractor<X> getExtractor(JavaTypeDescriptor<X> javaTypeDescriptor) {
final ValueExtractor realExtractor = delegate.getExtractor( javaTypeDescriptor );
return new BasicExtractor<X>( javaTypeDescriptor, this ) {
@Override
@SuppressWarnings("unchecked")
protected X doExtract(ResultSet rs, String name, WrapperOptions options) throws SQLException {
return (X) converter.convertToEntityAttribute( realExtractor.extract( rs, name, options ) );
}
@Override
@SuppressWarnings("unchecked")
protected X doExtract(CallableStatement statement, int index, WrapperOptions options)
throws SQLException {
return (X) converter.convertToEntityAttribute( realExtractor.extract( statement, index, options ) );
}
};
}
}
}

View File

@ -44,13 +44,12 @@ public abstract class AbstractSingleColumnStandardBasicType<T>
super( sqlTypeDescriptor, javaTypeDescriptor );
}
@Override
public final int sqlType() {
return getSqlTypeDescriptor().getSqlType();
}
/**
* {@inheritDoc}
*/
@Override
public final void nullSafeSet(PreparedStatement st, Object value, int index, boolean[] settable, SessionImplementor session)
throws HibernateException, SQLException {
if ( settable[0] ) {

View File

@ -24,7 +24,6 @@
package org.hibernate.type.descriptor;
import java.lang.reflect.Field;
import java.sql.Types;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
@ -35,7 +34,7 @@ import org.hibernate.HibernateException;
import org.hibernate.internal.CoreMessageLogger;
/**
* TODO : javadoc
* (Badly named) helper for dealing with standard JDBC types as defined by {@link java.sql.Types}
*
* @author Steve Ebersole
*/
@ -46,7 +45,7 @@ public class JdbcTypeNameMapper {
private static Map<Integer, String> buildJdbcTypeMap() {
HashMap<Integer, String> map = new HashMap<Integer, String>();
Field[] fields = Types.class.getFields();
Field[] fields = java.sql.Types.class.getFields();
if ( fields == null ) {
throw new HibernateException( "Unexpected problem extracting JDBC type mapping codes from java.sql.Types" );
}
@ -54,7 +53,9 @@ public class JdbcTypeNameMapper {
try {
final int code = field.getInt( null );
String old = map.put( code, field.getName() );
if (old != null) LOG.JavaSqlTypesMappedSameCodeMultipleTimes(code, old, field.getName());
if ( old != null ) {
LOG.JavaSqlTypesMappedSameCodeMultipleTimes( code, old, field.getName() );
}
}
catch ( IllegalAccessException e ) {
throw new HibernateException( "Unable to access JDBC type mapping [" + field.getName() + "]", e );
@ -63,10 +64,43 @@ public class JdbcTypeNameMapper {
return Collections.unmodifiableMap( map );
}
public static String getTypeName(Integer code) {
String name = JDBC_TYPE_MAP.get( code );
/**
* Determine whether the given JDBC type code represents a standard JDBC type ("standard" being those defined on
* {@link java.sql.Types}).
*
* NOTE : {@link java.sql.Types#OTHER} is also "filtered out" as being non-standard.
*
* @param typeCode The JDBC type code to check
*
* @return {@code true} to indicate the type code is a standard type code; {@code false} otherwise.
*/
public static boolean isStandardTypeCode(int typeCode) {
return isStandardTypeCode( Integer.valueOf( typeCode ) );
}
/**
* Same as call to {@link #isStandardTypeCode(int)}
*
* @see #isStandardTypeCode(int)
*/
public static boolean isStandardTypeCode(Integer typeCode) {
return JDBC_TYPE_MAP.containsKey( typeCode );
}
/**
* Get the type name as in the static field names defined on {@link java.sql.Types}. If a type code is not
* recognized, it is reported as {@code UNKNOWN(?)} where '?' is replace with the given type code.
*
* Intended as useful for logging purposes...
*
* @param typeCode The type code to find the name for.
*
* @return The type name.
*/
public static String getTypeName(Integer typeCode) {
String name = JDBC_TYPE_MAP.get( typeCode );
if ( name == null ) {
return "UNKNOWN(" + code + ")";
return "UNKNOWN(" + typeCode + ")";
}
return name;
}

View File

@ -65,46 +65,36 @@ public abstract class AbstractTypeDescriptor<T> implements JavaTypeDescriptor<T>
this.comparator = Comparable.class.isAssignableFrom( type )
? (Comparator<T>) ComparableComparator.INSTANCE
: null;
JavaTypeDescriptorRegistry.INSTANCE.addDescriptor( this );
}
/**
* {@inheritDoc}
*/
@Override
public MutabilityPlan<T> getMutabilityPlan() {
return mutabilityPlan;
}
/**
* {@inheritDoc}
*/
@Override
public Class<T> getJavaTypeClass() {
return type;
}
/**
* {@inheritDoc}
*/
@Override
public int extractHashCode(T value) {
return value.hashCode();
}
/**
* {@inheritDoc}
*/
@Override
public boolean areEqual(T one, T another) {
return EqualsHelper.equals( one, another );
}
/**
* {@inheritDoc}
*/
@Override
public Comparator<T> getComparator() {
return comparator;
}
/**
* {@inheritDoc}
*/
@Override
public String extractLoggableRepresentation(T value) {
return (value == null) ? "null" : value.toString();
}

View File

@ -33,7 +33,7 @@ import org.hibernate.type.descriptor.BinaryStream;
import org.hibernate.type.descriptor.WrapperOptions;
/**
* TODO : javadoc
* Descriptor for {@code Byte[]} handling.
*
* @author Steve Ebersole
*/

View File

@ -32,7 +32,7 @@ import org.hibernate.type.descriptor.CharacterStream;
import org.hibernate.type.descriptor.WrapperOptions;
/**
* TODO : javadoc
* Descriptor for {@code Character[]} handling.
*
* @author Steve Ebersole
*/

View File

@ -27,7 +27,7 @@ import org.hibernate.internal.util.ReflectHelper;
import org.hibernate.type.descriptor.WrapperOptions;
/**
* TODO : javadoc
* Descriptor for {@link Class} handling.
*
* @author Steve Ebersole
*/

View File

@ -28,7 +28,7 @@ import java.util.Currency;
import org.hibernate.type.descriptor.WrapperOptions;
/**
* TODO : javadoc
* Descriptor for {@link Currency} handling.
*
* @author Steve Ebersole
*/
@ -39,10 +39,12 @@ public class CurrencyTypeDescriptor extends AbstractTypeDescriptor<Currency> {
super( Currency.class );
}
@Override
public String toString(Currency value) {
return value.getCurrencyCode();
}
@Override
public Currency fromString(String string) {
return Currency.getInstance( string );
}
@ -58,6 +60,7 @@ public class CurrencyTypeDescriptor extends AbstractTypeDescriptor<Currency> {
throw unknownUnwrap( type );
}
@Override
public <X> Currency wrap(X value, WrapperOptions options) {
if ( value == null ) {
return null;

View File

@ -33,7 +33,7 @@ import org.hibernate.HibernateException;
import org.hibernate.type.descriptor.WrapperOptions;
/**
* TODO : javadoc
* Descriptor for {@link Date} handling.
*
* @author Steve Ebersole
*/

View File

@ -22,6 +22,7 @@
* Boston, MA 02110-1301 USA
*/
package org.hibernate.type.descriptor.java;
import java.io.Serializable;
/**
@ -32,30 +33,22 @@ import java.io.Serializable;
public class ImmutableMutabilityPlan<T> implements MutabilityPlan<T> {
public static final ImmutableMutabilityPlan INSTANCE = new ImmutableMutabilityPlan();
/**
* {@inheritDoc}
*/
@Override
public boolean isMutable() {
return false;
}
/**
* {@inheritDoc}
*/
@Override
public T deepCopy(T value) {
return value;
}
/**
* {@inheritDoc}
*/
@Override
public Serializable disassemble(T value) {
return (Serializable) value;
}
/**
* {@inheritDoc}
*/
@Override
@SuppressWarnings({ "unchecked" })
public T assemble(Serializable cached) {
return (T) cached;

View File

@ -22,16 +22,19 @@
* Boston, MA 02110-1301 USA
*/
package org.hibernate.type.descriptor.java;
import java.util.Comparator;
/**
* TODO : javadoc
* Comparator for things that cannot be compared (in a way we know about).
*
* @author Steve Ebersole
*/
public class IncomparableComparator implements Comparator {
public static final IncomparableComparator INSTANCE = new IncomparableComparator();
@Override
@SuppressWarnings("ComparatorMethodParameterNotUsed")
public int compare(Object o1, Object o2) {
return 0;
}

View File

@ -0,0 +1,112 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2012, 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.descriptor.java;
import java.io.Serializable;
import java.util.Comparator;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import org.hibernate.HibernateException;
import org.hibernate.type.descriptor.WrapperOptions;
/**
* Basically a map from {@link Class} -> {@link JavaTypeDescriptor}
*
* @author Steve Ebersole
*/
public class JavaTypeDescriptorRegistry {
public static final JavaTypeDescriptorRegistry INSTANCE = new JavaTypeDescriptorRegistry();
private ConcurrentHashMap<Class,JavaTypeDescriptor> descriptorsByClass = new ConcurrentHashMap<Class, JavaTypeDescriptor>();
/**
* Adds the given descriptor to this registry
*
* @param descriptor The descriptor to add.
*/
public void addDescriptor(JavaTypeDescriptor descriptor) {
descriptorsByClass.put( descriptor.getJavaTypeClass(), descriptor );
}
@SuppressWarnings("unchecked")
public <T> JavaTypeDescriptor<T> getDescriptor(Class<T> cls) {
if ( cls == null ) {
throw new IllegalArgumentException( "Class passed to locate Java type descriptor cannot be null" );
}
JavaTypeDescriptor<T> descriptor = descriptorsByClass.get( cls );
if ( descriptor != null ) {
return descriptor;
}
// find the first "assignable" match
for ( Map.Entry<Class,JavaTypeDescriptor> entry : descriptorsByClass.entrySet() ) {
if ( cls.isAssignableFrom( entry.getKey() ) ) {
return entry.getValue();
}
}
// we could not find one; warn the user (as stuff is likely to break later) and create a fallback instance...
if ( Serializable.class.isAssignableFrom( cls ) ) {
return new SerializableTypeDescriptor( cls );
}
else {
return new FallbackJavaTypeDescriptor<T>( cls );
}
}
public static class FallbackJavaTypeDescriptor<T> extends AbstractTypeDescriptor<T> {
@SuppressWarnings("unchecked")
protected FallbackJavaTypeDescriptor(Class<T> type) {
// MutableMutabilityPlan would be the "safest" option, but we do not necessarily know how to deepCopy etc...
super( type, ImmutableMutabilityPlan.INSTANCE );
}
@Override
public String toString(T value) {
return value == null ? "<null>" : value.toString();
}
@Override
public T fromString(String string) {
throw new HibernateException(
"Not known how to convert String to given type [" + getJavaTypeClass().getName() + "]"
);
}
@Override
@SuppressWarnings("unchecked")
public <X> X unwrap(T value, Class<X> type, WrapperOptions options) {
return (X) value;
}
@Override
@SuppressWarnings("unchecked")
public <X> T wrap(X value, WrapperOptions options) {
return (T) value;
}
}
}

View File

@ -33,7 +33,7 @@ import org.hibernate.HibernateException;
import org.hibernate.type.descriptor.WrapperOptions;
/**
* TODO : javadoc
* Descriptor for {@link java.sql.Date} handling.
*
* @author Steve Ebersole
*/

View File

@ -34,7 +34,7 @@ import org.hibernate.HibernateException;
import org.hibernate.type.descriptor.WrapperOptions;
/**
* TODO : javadoc
* Descriptor for {@link Time} handling.
*
* @author Steve Ebersole
*/

View File

@ -34,7 +34,7 @@ import org.hibernate.HibernateException;
import org.hibernate.type.descriptor.WrapperOptions;
/**
* TODO : javadoc
* Descriptor for {@link Timestamp} handling.
*
* @author Steve Ebersole
*/
@ -53,8 +53,7 @@ public class JdbcTimestampTypeDescriptor extends AbstractTypeDescriptor<Date> {
return ts;
}
else {
Date orig = value;
return new Date( orig.getTime() );
return new Date( value.getTime() );
}
}
}

View File

@ -30,7 +30,7 @@ import java.util.StringTokenizer;
import org.hibernate.type.descriptor.WrapperOptions;
/**
* TODO : javadoc
* Descriptor for {@link Locale} handling.
*
* @author Steve Ebersole
*/

View File

@ -22,10 +22,12 @@
* Boston, MA 02110-1301 USA
*/
package org.hibernate.type.descriptor.java;
import java.io.Serializable;
/**
* TODO : javadoc
* Describes the mutability aspects of a Java type. The term mutability refers to the fact that generally speaking
* the aspects described by this contract are defined by whether the Java type's internal state is mutable or not.
*
* @author Steve Ebersole
*/

View File

@ -22,6 +22,7 @@
* Boston, MA 02110-1301 USA
*/
package org.hibernate.type.descriptor.java;
import java.io.Serializable;
/**
@ -30,29 +31,23 @@ import java.io.Serializable;
* @author Steve Ebersole
*/
public abstract class MutableMutabilityPlan<T> implements MutabilityPlan<T> {
/**
* {@inheritDoc}
*/
@Override
public boolean isMutable() {
return true;
}
/**
* {@inheritDoc}
*/
@Override
public Serializable disassemble(T value) {
return (Serializable) deepCopy( value );
}
/**
* {@inheritDoc}
*/
@Override
@SuppressWarnings({ "unchecked" })
public T assemble(Serializable cached) {
return (T) deepCopy( (T) cached );
}
@Override
public final T deepCopy(T value) {
return value == null ? null : deepCopyNotNull( value );
}

View File

@ -32,7 +32,7 @@ import org.hibernate.type.descriptor.CharacterStream;
import org.hibernate.type.descriptor.WrapperOptions;
/**
* TODO : javadoc
* Descriptor for {@code char[]} handling.
*
* @author Steve Ebersole
*/

View File

@ -32,7 +32,7 @@ import org.hibernate.type.descriptor.BinaryStream;
import org.hibernate.type.descriptor.WrapperOptions;
/**
* TODO : javadoc
* Descriptor for general {@link Serializable} handling.
*
* @author Steve Ebersole
*/

View File

@ -42,6 +42,11 @@ import org.hibernate.type.descriptor.java.JavaTypeDescriptor;
public class BigIntTypeDescriptor implements SqlTypeDescriptor {
public static final BigIntTypeDescriptor INSTANCE = new BigIntTypeDescriptor();
public BigIntTypeDescriptor() {
SqlTypeDescriptorRegistry.INSTANCE.addDescriptor( this );
}
@Override
public int getSqlType() {
return Types.BIGINT;
}
@ -51,6 +56,7 @@ public class BigIntTypeDescriptor implements SqlTypeDescriptor {
return true;
}
@Override
public <X> ValueBinder<X> getBinder(final JavaTypeDescriptor<X> javaTypeDescriptor) {
return new BasicBinder<X>( javaTypeDescriptor, this ) {
@Override
@ -60,6 +66,7 @@ public class BigIntTypeDescriptor implements SqlTypeDescriptor {
};
}
@Override
public <X> ValueExtractor<X> getExtractor(final JavaTypeDescriptor<X> javaTypeDescriptor) {
return new BasicExtractor<X>( javaTypeDescriptor, this ) {
@Override

View File

@ -32,6 +32,10 @@ import java.sql.Types;
public class BinaryTypeDescriptor extends VarbinaryTypeDescriptor {
public static final BinaryTypeDescriptor INSTANCE = new BinaryTypeDescriptor();
public BinaryTypeDescriptor() {
SqlTypeDescriptorRegistry.INSTANCE.addDescriptor( this );
}
@Override
public int getSqlType() {
return Types.BINARY;

View File

@ -45,6 +45,10 @@ import org.hibernate.type.descriptor.java.JavaTypeDescriptor;
public class BitTypeDescriptor implements SqlTypeDescriptor {
public static final BitTypeDescriptor INSTANCE = new BitTypeDescriptor();
public BitTypeDescriptor() {
SqlTypeDescriptorRegistry.INSTANCE.addDescriptor( this );
}
public int getSqlType() {
return Types.BIT;
}
@ -54,6 +58,7 @@ public class BitTypeDescriptor implements SqlTypeDescriptor {
return true;
}
@Override
public <X> ValueBinder<X> getBinder(final JavaTypeDescriptor<X> javaTypeDescriptor) {
return new BasicBinder<X>( javaTypeDescriptor, this ) {
@Override
@ -63,6 +68,7 @@ public class BitTypeDescriptor implements SqlTypeDescriptor {
};
}
@Override
public <X> ValueExtractor<X> getExtractor(final JavaTypeDescriptor<X> javaTypeDescriptor) {
return new BasicExtractor<X>( javaTypeDescriptor, this ) {
@Override

View File

@ -31,7 +31,6 @@ import java.sql.SQLException;
import java.sql.Types;
import org.hibernate.type.descriptor.BinaryStream;
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;
@ -40,13 +39,50 @@ import org.hibernate.type.descriptor.java.JavaTypeDescriptor;
* Descriptor for {@link Types#BLOB BLOB} handling.
*
* @author Steve Ebersole
* @author Gail Badner
*/
public abstract class BlobTypeDescriptor implements SqlTypeDescriptor {
private BlobTypeDescriptor() {}
private BlobTypeDescriptor() {
}
@Override
public int getSqlType() {
return Types.BLOB;
}
@Override
public boolean canBeRemapped() {
return true;
}
@Override
public <X> ValueExtractor<X> getExtractor(final JavaTypeDescriptor<X> javaTypeDescriptor) {
return new BasicExtractor<X>( javaTypeDescriptor, this ) {
@Override
protected X doExtract(ResultSet rs, String name, WrapperOptions options) throws SQLException {
return javaTypeDescriptor.wrap( rs.getBlob( name ), options );
}
@Override
protected X doExtract(CallableStatement statement, int index, WrapperOptions options) throws SQLException {
return javaTypeDescriptor.wrap( statement.getBlob( index ), options );
}
};
}
protected abstract <X> BasicBinder<X> getBlobBinder(final JavaTypeDescriptor<X> javaTypeDescriptor);
public <X> BasicBinder<X> getBinder(final JavaTypeDescriptor<X> javaTypeDescriptor) {
return getBlobBinder( javaTypeDescriptor );
}
public static final BlobTypeDescriptor DEFAULT =
new BlobTypeDescriptor() {
{
SqlTypeDescriptorRegistry.INSTANCE.addDescriptor( this );
}
@Override
public <X> BasicBinder<X> getBlobBinder(final JavaTypeDescriptor<X> javaTypeDescriptor) {
return new BasicBinder<X>( javaTypeDescriptor, this ) {
@ -110,32 +146,4 @@ public abstract class BlobTypeDescriptor implements SqlTypeDescriptor {
}
};
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 ) {
@Override
protected X doExtract(ResultSet rs, String name, WrapperOptions options) throws SQLException {
return javaTypeDescriptor.wrap( rs.getBlob( name ), options );
}
@Override
protected X doExtract(CallableStatement statement, int index, WrapperOptions options) throws SQLException {
return javaTypeDescriptor.wrap( statement.getBlob( index ), options );
}
};
}
public int getSqlType() {
return Types.BLOB;
}
@Override
public boolean canBeRemapped() {
return true;
}
public <X> ValueBinder<X> getBinder(final JavaTypeDescriptor<X> javaTypeDescriptor) {
return getBlobBinder( javaTypeDescriptor );
}
}

View File

@ -42,6 +42,10 @@ import org.hibernate.type.descriptor.java.JavaTypeDescriptor;
public class BooleanTypeDescriptor implements SqlTypeDescriptor {
public static final BooleanTypeDescriptor INSTANCE = new BooleanTypeDescriptor();
public BooleanTypeDescriptor() {
SqlTypeDescriptorRegistry.INSTANCE.addDescriptor( this );
}
public int getSqlType() {
return Types.BOOLEAN;
}

View File

@ -32,6 +32,10 @@ import java.sql.Types;
public class CharTypeDescriptor extends VarcharTypeDescriptor {
public static final CharTypeDescriptor INSTANCE = new CharTypeDescriptor();
public CharTypeDescriptor() {
SqlTypeDescriptorRegistry.INSTANCE.addDescriptor( this );
}
@Override
public int getSqlType() {
return Types.CHAR;

View File

@ -40,11 +40,47 @@ import org.hibernate.type.descriptor.java.JavaTypeDescriptor;
* Descriptor for {@link Types#CLOB CLOB} handling.
*
* @author Steve Ebersole
* @author Gail Badner
*/
public abstract class ClobTypeDescriptor implements SqlTypeDescriptor {
@Override
public int getSqlType() {
return Types.CLOB;
}
@Override
public boolean canBeRemapped() {
return true;
}
@Override
public <X> ValueExtractor<X> getExtractor(final JavaTypeDescriptor<X> javaTypeDescriptor) {
return new BasicExtractor<X>( javaTypeDescriptor, this ) {
@Override
protected X doExtract(ResultSet rs, String name, WrapperOptions options) throws SQLException {
return javaTypeDescriptor.wrap( rs.getClob( name ), options );
}
@Override
protected X doExtract(CallableStatement statement, int index, WrapperOptions options) throws SQLException {
return javaTypeDescriptor.wrap( statement.getClob( index ), options );
}
};
}
protected abstract <X> BasicBinder<X> getClobBinder(final JavaTypeDescriptor<X> javaTypeDescriptor);
public <X> ValueBinder<X> getBinder(final JavaTypeDescriptor<X> javaTypeDescriptor) {
return getClobBinder( javaTypeDescriptor );
}
public static final ClobTypeDescriptor DEFAULT =
new ClobTypeDescriptor() {
{
SqlTypeDescriptorRegistry.INSTANCE.addDescriptor( this );
}
public <X> BasicBinder<X> getClobBinder(final JavaTypeDescriptor<X> javaTypeDescriptor) {
return new BasicBinder<X>( javaTypeDescriptor, this ) {
@Override
@ -87,32 +123,4 @@ public abstract class ClobTypeDescriptor implements SqlTypeDescriptor {
}
};
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;
}
@Override
public boolean canBeRemapped() {
return true;
}
public <X> ValueExtractor<X> getExtractor(final JavaTypeDescriptor<X> javaTypeDescriptor) {
return new BasicExtractor<X>( javaTypeDescriptor, this ) {
@Override
protected X doExtract(ResultSet rs, String name, WrapperOptions options) throws SQLException {
return javaTypeDescriptor.wrap( rs.getClob( name ), options );
}
@Override
protected X doExtract(CallableStatement statement, int index, WrapperOptions options) throws SQLException {
return javaTypeDescriptor.wrap( statement.getClob( index ), options );
}
};
}
}

View File

@ -43,6 +43,11 @@ import org.hibernate.type.descriptor.java.JavaTypeDescriptor;
public class DateTypeDescriptor implements SqlTypeDescriptor {
public static final DateTypeDescriptor INSTANCE = new DateTypeDescriptor();
public DateTypeDescriptor() {
SqlTypeDescriptorRegistry.INSTANCE.addDescriptor( this );
}
@Override
public int getSqlType() {
return Types.DATE;
}
@ -52,6 +57,7 @@ public class DateTypeDescriptor implements SqlTypeDescriptor {
return true;
}
@Override
public <X> ValueBinder<X> getBinder(final JavaTypeDescriptor<X> javaTypeDescriptor) {
return new BasicBinder<X>( javaTypeDescriptor, this ) {
@Override
@ -61,6 +67,7 @@ public class DateTypeDescriptor implements SqlTypeDescriptor {
};
}
@Override
public <X> ValueExtractor<X> getExtractor(final JavaTypeDescriptor<X> javaTypeDescriptor) {
return new BasicExtractor<X>( javaTypeDescriptor, this ) {
@Override

View File

@ -43,6 +43,11 @@ import org.hibernate.type.descriptor.java.JavaTypeDescriptor;
public class DecimalTypeDescriptor implements SqlTypeDescriptor {
public static final DecimalTypeDescriptor INSTANCE = new DecimalTypeDescriptor();
public DecimalTypeDescriptor() {
SqlTypeDescriptorRegistry.INSTANCE.addDescriptor( this );
}
@Override
public int getSqlType() {
return Types.DECIMAL;
}
@ -52,6 +57,7 @@ public class DecimalTypeDescriptor implements SqlTypeDescriptor {
return true;
}
@Override
public <X> ValueBinder<X> getBinder(final JavaTypeDescriptor<X> javaTypeDescriptor) {
return new BasicBinder<X>( javaTypeDescriptor, this ) {
@Override
@ -61,6 +67,7 @@ public class DecimalTypeDescriptor implements SqlTypeDescriptor {
};
}
@Override
public <X> ValueExtractor<X> getExtractor(final JavaTypeDescriptor<X> javaTypeDescriptor) {
return new BasicExtractor<X>( javaTypeDescriptor, this ) {
@Override

View File

@ -42,6 +42,11 @@ import org.hibernate.type.descriptor.java.JavaTypeDescriptor;
public class DoubleTypeDescriptor implements SqlTypeDescriptor {
public static final DoubleTypeDescriptor INSTANCE = new DoubleTypeDescriptor();
public DoubleTypeDescriptor() {
SqlTypeDescriptorRegistry.INSTANCE.addDescriptor( this );
}
@Override
public int getSqlType() {
return Types.DOUBLE;
}
@ -51,6 +56,7 @@ public class DoubleTypeDescriptor implements SqlTypeDescriptor {
return true;
}
@Override
public <X> ValueBinder<X> getBinder(final JavaTypeDescriptor<X> javaTypeDescriptor) {
return new BasicBinder<X>( javaTypeDescriptor, this ) {
@Override
@ -60,6 +66,7 @@ public class DoubleTypeDescriptor implements SqlTypeDescriptor {
};
}
@Override
public <X> ValueExtractor<X> getExtractor(final JavaTypeDescriptor<X> javaTypeDescriptor) {
return new BasicExtractor<X>( javaTypeDescriptor, this ) {
@Override

View File

@ -22,6 +22,7 @@
* Boston, MA 02110-1301 USA
*/
package org.hibernate.type.descriptor.sql;
import java.sql.Types;
/**
@ -32,6 +33,11 @@ import java.sql.Types;
public class FloatTypeDescriptor extends RealTypeDescriptor {
public static final FloatTypeDescriptor INSTANCE = new FloatTypeDescriptor();
public FloatTypeDescriptor() {
SqlTypeDescriptorRegistry.INSTANCE.addDescriptor( this );
}
@Override
public int getSqlType() {
return Types.FLOAT;
}

View File

@ -42,6 +42,11 @@ import org.hibernate.type.descriptor.java.JavaTypeDescriptor;
public class IntegerTypeDescriptor implements SqlTypeDescriptor {
public static final IntegerTypeDescriptor INSTANCE = new IntegerTypeDescriptor();
public IntegerTypeDescriptor() {
SqlTypeDescriptorRegistry.INSTANCE.addDescriptor( this );
}
@Override
public int getSqlType() {
return Types.INTEGER;
}
@ -51,6 +56,7 @@ public class IntegerTypeDescriptor implements SqlTypeDescriptor {
return true;
}
@Override
public <X> ValueBinder<X> getBinder(final JavaTypeDescriptor<X> javaTypeDescriptor) {
return new BasicBinder<X>( javaTypeDescriptor, this ) {
@Override
@ -60,6 +66,7 @@ public class IntegerTypeDescriptor implements SqlTypeDescriptor {
};
}
@Override
public <X> ValueExtractor<X> getExtractor(final JavaTypeDescriptor<X> javaTypeDescriptor) {
return new BasicExtractor<X>( javaTypeDescriptor, this ) {
@Override

View File

@ -0,0 +1,75 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2012, 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.descriptor.sql;
import java.sql.Types;
import java.util.concurrent.ConcurrentHashMap;
/**
* Information pertaining to JDBC type families.
*
* @author Steve Ebersole
*/
public class JdbcTypeFamilyInformation {
public static final JdbcTypeFamilyInformation INSTANCE = new JdbcTypeFamilyInformation();
// todo : make Family non-enum so it can be expanded by Dialects?
public static enum Family {
BINARY( Types.BINARY, Types.VARBINARY, Types.LONGVARBINARY ),
NUMERIC( Types.BIGINT, Types.DECIMAL, Types.DOUBLE, Types.FLOAT, Types.INTEGER, Types.NUMERIC, Types.REAL, Types.SMALLINT, Types.TINYINT ),
CHARACTER( Types.CHAR, Types.LONGNVARCHAR, Types.LONGVARCHAR, Types.NCHAR, Types.NVARCHAR, Types.VARCHAR ),
DATETIME( Types.DATE, Types.TIME, Types.TIMESTAMP ),
CLOB( Types.CLOB, Types.NCLOB );
private final int[] typeCodes;
@SuppressWarnings("UnnecessaryBoxing")
private Family(int... typeCodes) {
this.typeCodes = typeCodes;
for ( int typeCode : typeCodes ) {
JdbcTypeFamilyInformation.INSTANCE.typeCodeToFamilyMap.put( Integer.valueOf( typeCode ), this );
}
}
public int[] getTypeCodes() {
return typeCodes;
}
}
private ConcurrentHashMap<Integer,Family> typeCodeToFamilyMap = new ConcurrentHashMap<Integer, Family>();
/**
* Will return {@code null} if no match is found.
*
* @param typeCode The JDBC type code.
*
* @return The family of datatypes the type code belongs to, or {@code null} if it belongs to no known families.
*/
@SuppressWarnings("UnnecessaryBoxing")
public Family locateJdbcTypeFamilyByTypeCode(int typeCode) {
return typeCodeToFamilyMap.get( Integer.valueOf( typeCode ) );
}
}

View File

@ -0,0 +1,134 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2012, 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.descriptor.sql;
import java.math.BigDecimal;
import java.sql.Blob;
import java.sql.Clob;
import java.sql.Date;
import java.sql.Ref;
import java.sql.Struct;
import java.sql.Time;
import java.sql.Timestamp;
import java.sql.Types;
import java.util.Calendar;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import org.jboss.logging.Logger;
import org.hibernate.mapping.Array;
/**
* Presents recommended {@literal JDCB typecode <-> Java Class} mappings. Currently the recommendations
* contained here come from the JDBC spec itself, as outlined at <a href="http://docs.oracle.com/javase/1.5.0/docs/guide/jdbc/getstart/mapping.html#1034737"/>
* Eventually, the plan is to have {@link org.hibernate.dialect.Dialect} contribute this information.
*
* @author Steve Ebersole
*/
public class JdbcTypeJavaClassMappings {
private static final Logger log = Logger.getLogger( JdbcTypeJavaClassMappings.class );
private static final ConcurrentHashMap<Class, Integer> javaClassToJdbcTypeCodeMap = buildJdbcJavaClassMappings();
private static final ConcurrentHashMap<Integer, Class> jdbcTypeCodeToJavaClassMap = transpose( javaClassToJdbcTypeCodeMap );
public static final JdbcTypeJavaClassMappings INSTANCE = new JdbcTypeJavaClassMappings();
private JdbcTypeJavaClassMappings() {
}
@SuppressWarnings("UnnecessaryUnboxing")
public int determineJdbcTypeCodeForJavaClass(Class cls) {
Integer typeCode = JdbcTypeJavaClassMappings.javaClassToJdbcTypeCodeMap.get( cls );
if ( typeCode != null ) {
return typeCode.intValue();
}
int specialCode = cls.hashCode();
log.debug(
"JDBC type code mapping not known for class [" + cls.getName() + "]; using custom code [" + specialCode + "]"
);
return specialCode;
}
@SuppressWarnings("UnnecessaryUnboxing")
public Class determineJavaClassForJdbcTypeCode(int typeCode) {
Class cls = jdbcTypeCodeToJavaClassMap.get( Integer.valueOf( typeCode ) );
if ( cls != null ) {
return cls;
}
log.debugf(
"Java Class mapping not known for JDBC type code [%s]; using java.lang.Object",
typeCode
);
return Object.class;
}
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
private static ConcurrentHashMap<Class, Integer> buildJdbcJavaClassMappings() {
ConcurrentHashMap<Class, Integer> jdbcJavaClassMappings = new ConcurrentHashMap<Class, Integer>();
// these mappings are the ones outlined specifically in the spec
jdbcJavaClassMappings.put( String.class, Types.VARCHAR );
jdbcJavaClassMappings.put( BigDecimal.class, Types.NUMERIC );
jdbcJavaClassMappings.put( Boolean.class, Types.BIT );
jdbcJavaClassMappings.put( Integer.class, Types.INTEGER );
jdbcJavaClassMappings.put( Long.class, Types.BIGINT );
jdbcJavaClassMappings.put( Float.class, Types.REAL );
jdbcJavaClassMappings.put( Double.class, Types.DOUBLE );
jdbcJavaClassMappings.put( byte[].class, Types.LONGVARBINARY );
jdbcJavaClassMappings.put( Date.class, Types.DATE );
jdbcJavaClassMappings.put( Time.class, Types.TIME );
jdbcJavaClassMappings.put( Timestamp.class, Types.TIMESTAMP );
jdbcJavaClassMappings.put( Blob.class, Types.BLOB );
jdbcJavaClassMappings.put( Clob.class, Types.CLOB );
jdbcJavaClassMappings.put( Array.class, Types.ARRAY );
jdbcJavaClassMappings.put( Struct.class, Types.STRUCT );
jdbcJavaClassMappings.put( Ref.class, Types.REF );
jdbcJavaClassMappings.put( Class.class, Types.JAVA_OBJECT );
// additional "common sense" registrations
jdbcJavaClassMappings.put( Character.class, Types.CHAR );
jdbcJavaClassMappings.put( char[].class, Types.VARCHAR );
jdbcJavaClassMappings.put( Character[].class, Types.VARCHAR );
jdbcJavaClassMappings.put( Byte[].class, Types.LONGVARBINARY );
jdbcJavaClassMappings.put( Date.class, Types.TIMESTAMP );
jdbcJavaClassMappings.put( Calendar.class, Types.TIMESTAMP );
return jdbcJavaClassMappings;
}
private static ConcurrentHashMap<Integer, Class> transpose(ConcurrentHashMap<Class, Integer> javaClassToJdbcTypeCodeMap) {
final ConcurrentHashMap<Integer, Class> transposed = new ConcurrentHashMap<Integer, Class>();
for ( Map.Entry<Class,Integer> entry : javaClassToJdbcTypeCodeMap.entrySet() ) {
transposed.put( entry.getValue(), entry.getKey() );
}
return transposed;
}
}

View File

@ -0,0 +1,44 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2012, 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.descriptor.sql;
import java.sql.Types;
/**
* Descriptor for {@link Types#LONGNVARCHAR LONGNVARCHAR} handling.
*
* @author Steve Ebersole
*/
public class LongNVarcharTypeDescriptor extends NVarcharTypeDescriptor {
public static final LongNVarcharTypeDescriptor INSTANCE = new LongNVarcharTypeDescriptor();
public LongNVarcharTypeDescriptor() {
SqlTypeDescriptorRegistry.INSTANCE.addDescriptor( this );
}
@Override
public int getSqlType() {
return Types.LONGNVARCHAR;
}
}

View File

@ -22,6 +22,7 @@
* Boston, MA 02110-1301 USA
*/
package org.hibernate.type.descriptor.sql;
import java.sql.Types;
/**
@ -32,6 +33,10 @@ import java.sql.Types;
public class LongVarbinaryTypeDescriptor extends VarbinaryTypeDescriptor {
public static final LongVarbinaryTypeDescriptor INSTANCE = new LongVarbinaryTypeDescriptor();
public LongVarbinaryTypeDescriptor() {
SqlTypeDescriptorRegistry.INSTANCE.addDescriptor( this );
}
@Override
public int getSqlType() {
return Types.LONGVARBINARY;

View File

@ -22,6 +22,7 @@
* Boston, MA 02110-1301 USA
*/
package org.hibernate.type.descriptor.sql;
import java.sql.Types;
/**
@ -32,6 +33,10 @@ import java.sql.Types;
public class LongVarcharTypeDescriptor extends VarcharTypeDescriptor {
public static final LongVarcharTypeDescriptor INSTANCE = new LongVarcharTypeDescriptor();
public LongVarcharTypeDescriptor() {
SqlTypeDescriptorRegistry.INSTANCE.addDescriptor( this );
}
@Override
public int getSqlType() {
return Types.LONGVARCHAR;

View File

@ -0,0 +1,44 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2012, 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.descriptor.sql;
import java.sql.Types;
/**
* Descriptor for {@link Types#NCHAR NCHAR} handling.
*
* @author Steve Ebersole
*/
public class NCharTypeDescriptor extends NVarcharTypeDescriptor {
public static final NCharTypeDescriptor INSTANCE = new NCharTypeDescriptor();
public NCharTypeDescriptor() {
SqlTypeDescriptorRegistry.INSTANCE.addDescriptor( this );
}
@Override
public int getSqlType() {
return Types.NCHAR;
}
}

View File

@ -0,0 +1,125 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2012, 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.descriptor.sql;
import java.sql.CallableStatement;
import java.sql.NClob;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Types;
import org.hibernate.type.descriptor.CharacterStream;
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;
/**
* Descriptor for {@link Types#NCLOB NCLOB} handling.
*
* @author Steve Ebersole
* @author Gail Badner
*/
public abstract class NClobTypeDescriptor implements SqlTypeDescriptor {
@Override
public int getSqlType() {
return Types.NCLOB;
}
@Override
public boolean canBeRemapped() {
return true;
}
@Override
public <X> ValueExtractor<X> getExtractor(final JavaTypeDescriptor<X> javaTypeDescriptor) {
return new BasicExtractor<X>( javaTypeDescriptor, this ) {
@Override
protected X doExtract(ResultSet rs, String name, WrapperOptions options) throws SQLException {
return javaTypeDescriptor.wrap( rs.getNClob( name ), options );
}
@Override
protected X doExtract(CallableStatement statement, int index, WrapperOptions options) throws SQLException {
return javaTypeDescriptor.wrap( statement.getNClob( index ), options );
}
};
}
protected abstract <X> BasicBinder<X> getClobBinder(final JavaTypeDescriptor<X> javaTypeDescriptor);
public <X> ValueBinder<X> getBinder(final JavaTypeDescriptor<X> javaTypeDescriptor) {
return getClobBinder( javaTypeDescriptor );
}
public static final ClobTypeDescriptor DEFAULT =
new ClobTypeDescriptor() {
{
SqlTypeDescriptorRegistry.INSTANCE.addDescriptor( this );
}
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.setNClob( index, javaTypeDescriptor.unwrap( value, NClob.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() );
}
};
}
};
}

View File

@ -0,0 +1,83 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2012, 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.descriptor.sql;
import java.sql.CallableStatement;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Types;
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;
/**
* Descriptor for {@link Types#NVARCHAR NVARCHAR} handling.
*
* @author Steve Ebersole
*/
public class NVarcharTypeDescriptor implements SqlTypeDescriptor {
public static final NVarcharTypeDescriptor INSTANCE = new NVarcharTypeDescriptor();
public NVarcharTypeDescriptor() {
SqlTypeDescriptorRegistry.INSTANCE.addDescriptor( this );
}
@Override
public int getSqlType() {
return Types.NVARCHAR;
}
@Override
public boolean canBeRemapped() {
return true;
}
@Override
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 {
st.setNString( index, javaTypeDescriptor.unwrap( value, String.class, options ) );
}
};
}
@Override
public <X> ValueExtractor<X> getExtractor(final JavaTypeDescriptor<X> javaTypeDescriptor) {
return new BasicExtractor<X>( javaTypeDescriptor, this ) {
@Override
protected X doExtract(ResultSet rs, String name, WrapperOptions options) throws SQLException {
return javaTypeDescriptor.wrap( rs.getNString( name ), options );
}
@Override
protected X doExtract(CallableStatement statement, int index, WrapperOptions options) throws SQLException {
return javaTypeDescriptor.wrap( statement.getNString( index ), options );
}
};
}
}

View File

@ -22,6 +22,7 @@
* Boston, MA 02110-1301 USA
*/
package org.hibernate.type.descriptor.sql;
import java.sql.Types;
/**
@ -32,6 +33,10 @@ import java.sql.Types;
public class NumericTypeDescriptor extends DecimalTypeDescriptor {
public static final NumericTypeDescriptor INSTANCE = new NumericTypeDescriptor();
public NumericTypeDescriptor() {
SqlTypeDescriptorRegistry.INSTANCE.addDescriptor( this );
}
@Override
public int getSqlType() {
return Types.NUMERIC;

View File

@ -42,6 +42,11 @@ import org.hibernate.type.descriptor.java.JavaTypeDescriptor;
public class RealTypeDescriptor implements SqlTypeDescriptor {
public static final RealTypeDescriptor INSTANCE = new RealTypeDescriptor();
public RealTypeDescriptor() {
SqlTypeDescriptorRegistry.INSTANCE.addDescriptor( this );
}
@Override
public int getSqlType() {
return Types.REAL;
}
@ -51,6 +56,7 @@ public class RealTypeDescriptor implements SqlTypeDescriptor {
return true;
}
@Override
public <X> ValueBinder<X> getBinder(final JavaTypeDescriptor<X> javaTypeDescriptor) {
return new BasicBinder<X>( javaTypeDescriptor, this ) {
@Override
@ -60,6 +66,7 @@ public class RealTypeDescriptor implements SqlTypeDescriptor {
};
}
@Override
public <X> ValueExtractor<X> getExtractor(final JavaTypeDescriptor<X> javaTypeDescriptor) {
return new BasicExtractor<X>( javaTypeDescriptor, this ) {
@Override

View File

@ -42,6 +42,11 @@ import org.hibernate.type.descriptor.java.JavaTypeDescriptor;
public class SmallIntTypeDescriptor implements SqlTypeDescriptor {
public static final SmallIntTypeDescriptor INSTANCE = new SmallIntTypeDescriptor();
public SmallIntTypeDescriptor() {
SqlTypeDescriptorRegistry.INSTANCE.addDescriptor( this );
}
@Override
public int getSqlType() {
return Types.SMALLINT;
}
@ -51,6 +56,7 @@ public class SmallIntTypeDescriptor implements SqlTypeDescriptor {
return true;
}
@Override
public <X> ValueBinder<X> getBinder(final JavaTypeDescriptor<X> javaTypeDescriptor) {
return new BasicBinder<X>( javaTypeDescriptor, this ) {
@Override
@ -60,6 +66,7 @@ public class SmallIntTypeDescriptor implements SqlTypeDescriptor {
};
}
@Override
public <X> ValueExtractor<X> getExtractor(final JavaTypeDescriptor<X> javaTypeDescriptor) {
return new BasicExtractor<X>( javaTypeDescriptor, this ) {
@Override

View File

@ -31,6 +31,9 @@ import org.hibernate.type.descriptor.java.JavaTypeDescriptor;
/**
* Descriptor for the <tt>SQL</tt>/<tt>JDBC</tt> side of a value mapping.
* <p/>
* NOTE : Implementations should be registered with the {@link SqlTypeDescriptor}. The built-in Hibernate
* implementations register themselves on construction.
*
* @author Steve Ebersole
*/
@ -52,7 +55,23 @@ public interface SqlTypeDescriptor extends Serializable {
*/
public boolean canBeRemapped();
/**
* Get the binder (setting JDBC in-going parameter values) capable of handling values of the type described by the
* passed descriptor.
*
* @param javaTypeDescriptor The descriptor describing the types of Java values to be bound
*
* @return The appropriate binder.
*/
public <X> ValueBinder<X> getBinder(JavaTypeDescriptor<X> javaTypeDescriptor);
/**
* Get the extractor (pulling out-going values from JDBC objects) capable of handling values of the type described
* by the passed descriptor.
*
* @param javaTypeDescriptor The descriptor describing the types of Java values to be extracted
*
* @return The appropriate extractor
*/
public <X> ValueExtractor<X> getExtractor(JavaTypeDescriptor<X> javaTypeDescriptor);
}

View File

@ -0,0 +1,152 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2012, 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.descriptor.sql;
import java.io.Serializable;
import java.sql.CallableStatement;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.concurrent.ConcurrentHashMap;
import org.jboss.logging.Logger;
import org.hibernate.type.descriptor.JdbcTypeNameMapper;
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;
/**
* Basically a map from JDBC type code (int) -> {@link SqlTypeDescriptor}
*
* @author Steve Ebersole
*/
public class SqlTypeDescriptorRegistry {
public static final SqlTypeDescriptorRegistry INSTANCE = new SqlTypeDescriptorRegistry();
private static final Logger log = Logger.getLogger( SqlTypeDescriptorRegistry.class );
private ConcurrentHashMap<Integer,SqlTypeDescriptor> descriptorMap = new ConcurrentHashMap<Integer, SqlTypeDescriptor>();
@SuppressWarnings("UnnecessaryBoxing")
public void addDescriptor(SqlTypeDescriptor sqlTypeDescriptor) {
descriptorMap.put( Integer.valueOf( sqlTypeDescriptor.getSqlType() ), sqlTypeDescriptor );
}
@SuppressWarnings("UnnecessaryBoxing")
public SqlTypeDescriptor getDescriptor(int jdbcTypeCode) {
SqlTypeDescriptor descriptor = descriptorMap.get( Integer.valueOf( jdbcTypeCode ) );
if ( descriptor != null ) {
return descriptor;
}
if ( JdbcTypeNameMapper.isStandardTypeCode( jdbcTypeCode ) ) {
log.debugf(
"A standard JDBC type code [%s] was not defined in SqlTypeDescriptorRegistry",
jdbcTypeCode
);
}
// see if the typecode is part of a known type family...
JdbcTypeFamilyInformation.Family family = JdbcTypeFamilyInformation.INSTANCE.locateJdbcTypeFamilyByTypeCode( jdbcTypeCode );
if ( family != null ) {
for ( int potentialAlternateTypeCode : family.getTypeCodes() ) {
if ( potentialAlternateTypeCode != jdbcTypeCode ) {
final SqlTypeDescriptor potentialAlternateDescriptor = descriptorMap.get( Integer.valueOf( potentialAlternateTypeCode ) );
if ( potentialAlternateDescriptor != null ) {
// todo : add a SqlTypeDescriptor.canBeAssignedFrom method...
return potentialAlternateDescriptor;
}
if ( JdbcTypeNameMapper.isStandardTypeCode( potentialAlternateTypeCode ) ) {
log.debugf(
"A standard JDBC type code [%s] was not defined in SqlTypeDescriptorRegistry",
potentialAlternateTypeCode
);
}
}
}
}
// finally, create a new descriptor mapping to getObject/setObject for this type code...
final ObjectSqlTypeDescriptor fallBackDescriptor = new ObjectSqlTypeDescriptor( jdbcTypeCode );
addDescriptor( fallBackDescriptor );
return fallBackDescriptor;
}
public static class ObjectSqlTypeDescriptor implements SqlTypeDescriptor {
private final int jdbcTypeCode;
public ObjectSqlTypeDescriptor(int jdbcTypeCode) {
this.jdbcTypeCode = jdbcTypeCode;
}
@Override
public int getSqlType() {
return jdbcTypeCode;
}
@Override
public boolean canBeRemapped() {
return true;
}
@Override
public <X> ValueBinder<X> getBinder(JavaTypeDescriptor<X> javaTypeDescriptor) {
if ( Serializable.class.isAssignableFrom( javaTypeDescriptor.getJavaTypeClass() ) ) {
return VarbinaryTypeDescriptor.INSTANCE.getBinder( javaTypeDescriptor );
}
return new BasicBinder<X>( javaTypeDescriptor, this ) {
@Override
protected void doBind(PreparedStatement st, X value, int index, WrapperOptions options)
throws SQLException {
st.setObject( index, value, jdbcTypeCode );
}
};
}
@Override
@SuppressWarnings("unchecked")
public ValueExtractor getExtractor(JavaTypeDescriptor javaTypeDescriptor) {
if ( Serializable.class.isAssignableFrom( javaTypeDescriptor.getJavaTypeClass() ) ) {
return VarbinaryTypeDescriptor.INSTANCE.getExtractor( javaTypeDescriptor );
}
return new BasicExtractor( javaTypeDescriptor, this ) {
@Override
protected Object doExtract(ResultSet rs, String name, WrapperOptions options) throws SQLException {
return rs.getObject( name );
}
@Override
protected Object doExtract(CallableStatement statement, int index, WrapperOptions options)
throws SQLException {
return statement.getObject( index );
}
};
}
}
}

View File

@ -43,6 +43,11 @@ import org.hibernate.type.descriptor.java.JavaTypeDescriptor;
public class TimeTypeDescriptor implements SqlTypeDescriptor {
public static final TimeTypeDescriptor INSTANCE = new TimeTypeDescriptor();
public TimeTypeDescriptor() {
SqlTypeDescriptorRegistry.INSTANCE.addDescriptor( this );
}
@Override
public int getSqlType() {
return Types.TIME;
}
@ -52,6 +57,7 @@ public class TimeTypeDescriptor implements SqlTypeDescriptor {
return true;
}
@Override
public <X> ValueBinder<X> getBinder(final JavaTypeDescriptor<X> javaTypeDescriptor) {
return new BasicBinder<X>( javaTypeDescriptor, this ) {
@Override
@ -61,6 +67,7 @@ public class TimeTypeDescriptor implements SqlTypeDescriptor {
};
}
@Override
public <X> ValueExtractor<X> getExtractor(final JavaTypeDescriptor<X> javaTypeDescriptor) {
return new BasicExtractor<X>( javaTypeDescriptor, this ) {
@Override

View File

@ -43,6 +43,11 @@ import org.hibernate.type.descriptor.java.JavaTypeDescriptor;
public class TimestampTypeDescriptor implements SqlTypeDescriptor {
public static final TimestampTypeDescriptor INSTANCE = new TimestampTypeDescriptor();
public TimestampTypeDescriptor() {
SqlTypeDescriptorRegistry.INSTANCE.addDescriptor( this );
}
@Override
public int getSqlType() {
return Types.TIMESTAMP;
}
@ -52,6 +57,7 @@ public class TimestampTypeDescriptor implements SqlTypeDescriptor {
return true;
}
@Override
public <X> ValueBinder<X> getBinder(final JavaTypeDescriptor<X> javaTypeDescriptor) {
return new BasicBinder<X>( javaTypeDescriptor, this ) {
@Override
@ -61,6 +67,7 @@ public class TimestampTypeDescriptor implements SqlTypeDescriptor {
};
}
@Override
public <X> ValueExtractor<X> getExtractor(final JavaTypeDescriptor<X> javaTypeDescriptor) {
return new BasicExtractor<X>( javaTypeDescriptor, this ) {
@Override

View File

@ -45,6 +45,11 @@ import org.hibernate.type.descriptor.java.JavaTypeDescriptor;
public class TinyIntTypeDescriptor implements SqlTypeDescriptor {
public static final TinyIntTypeDescriptor INSTANCE = new TinyIntTypeDescriptor();
public TinyIntTypeDescriptor() {
SqlTypeDescriptorRegistry.INSTANCE.addDescriptor( this );
}
@Override
public int getSqlType() {
return Types.TINYINT;
}
@ -54,6 +59,7 @@ public class TinyIntTypeDescriptor implements SqlTypeDescriptor {
return true;
}
@Override
public <X> ValueBinder<X> getBinder(final JavaTypeDescriptor<X> javaTypeDescriptor) {
return new BasicBinder<X>( javaTypeDescriptor, this ) {
@Override
@ -63,6 +69,7 @@ public class TinyIntTypeDescriptor implements SqlTypeDescriptor {
};
}
@Override
public <X> ValueExtractor<X> getExtractor(final JavaTypeDescriptor<X> javaTypeDescriptor) {
return new BasicExtractor<X>( javaTypeDescriptor, this ) {
@Override

View File

@ -42,6 +42,10 @@ import org.hibernate.type.descriptor.java.JavaTypeDescriptor;
public class VarbinaryTypeDescriptor implements SqlTypeDescriptor {
public static final VarbinaryTypeDescriptor INSTANCE = new VarbinaryTypeDescriptor();
public VarbinaryTypeDescriptor() {
SqlTypeDescriptorRegistry.INSTANCE.addDescriptor( this );
}
public int getSqlType() {
return Types.VARBINARY;
}

View File

@ -42,6 +42,11 @@ import org.hibernate.type.descriptor.java.JavaTypeDescriptor;
public class VarcharTypeDescriptor implements SqlTypeDescriptor {
public static final VarcharTypeDescriptor INSTANCE = new VarcharTypeDescriptor();
public VarcharTypeDescriptor() {
SqlTypeDescriptorRegistry.INSTANCE.addDescriptor( this );
}
@Override
public int getSqlType() {
return Types.VARCHAR;
}
@ -51,6 +56,7 @@ public class VarcharTypeDescriptor implements SqlTypeDescriptor {
return true;
}
@Override
public <X> ValueBinder<X> getBinder(final JavaTypeDescriptor<X> javaTypeDescriptor) {
return new BasicBinder<X>( javaTypeDescriptor, this ) {
@Override
@ -60,6 +66,7 @@ public class VarcharTypeDescriptor implements SqlTypeDescriptor {
};
}
@Override
public <X> ValueExtractor<X> getExtractor(final JavaTypeDescriptor<X> javaTypeDescriptor) {
return new BasicExtractor<X>( javaTypeDescriptor, this ) {
@Override

View File

@ -0,0 +1,143 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2012, 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 javax.persistence.AttributeConverter;
import javax.persistence.Convert;
import javax.persistence.Converter;
import javax.persistence.Entity;
import javax.persistence.Id;
import java.sql.Clob;
import java.sql.Types;
import org.hibernate.IrrelevantEntity;
import org.hibernate.cfg.AttributeConverterDefinition;
import org.hibernate.cfg.Configuration;
import org.hibernate.mapping.PersistentClass;
import org.hibernate.mapping.Property;
import org.hibernate.mapping.SimpleValue;
import org.hibernate.type.descriptor.java.StringTypeDescriptor;
import org.junit.Test;
import org.hibernate.testing.junit4.BaseUnitTestCase;
import static org.hibernate.testing.junit4.ExtraAssertions.assertTyping;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertSame;
/**
* Tests the principle of adding "AttributeConverter" to the mix of {@link Type} resolution
*
* @author Steve Ebersole
*/
public class AttributeConverterTest extends BaseUnitTestCase {
@Test
public void testBasicOperation() {
Configuration cfg = new Configuration();
SimpleValue simpleValue = new SimpleValue( cfg.createMappings() );
simpleValue.setJpaAttributeConverterDefinition(
new AttributeConverterDefinition( new StringClobConverter(), true )
);
simpleValue.setTypeUsingReflection( IrrelevantEntity.class.getName(), "name" );
Type type = simpleValue.getType();
assertNotNull( type );
assertTyping( BasicType.class, type );
AbstractStandardBasicType basicType = assertTyping( AbstractStandardBasicType.class, type );
assertSame( StringTypeDescriptor.INSTANCE, basicType.getJavaTypeDescriptor() );
assertEquals( Types.CLOB, basicType.getSqlTypeDescriptor().getSqlType() );
}
@Test
public void testNormalOperation() {
Configuration cfg = new Configuration();
cfg.addAttributeConverter( StringClobConverter.class, true );
cfg.addAnnotatedClass( Tester.class );
cfg.addAnnotatedClass( Tester2.class );
cfg.buildMappings();
{
PersistentClass tester = cfg.getClassMapping( Tester.class.getName() );
Property nameProp = tester.getProperty( "name" );
SimpleValue nameValue = (SimpleValue) nameProp.getValue();
Type type = nameValue.getType();
assertNotNull( type );
assertTyping( BasicType.class, type );
AbstractStandardBasicType basicType = assertTyping( AbstractStandardBasicType.class, type );
assertSame( StringTypeDescriptor.INSTANCE, basicType.getJavaTypeDescriptor() );
assertEquals( Types.CLOB, basicType.getSqlTypeDescriptor().getSqlType() );
}
{
PersistentClass tester = cfg.getClassMapping( Tester2.class.getName() );
Property nameProp = tester.getProperty( "name" );
SimpleValue nameValue = (SimpleValue) nameProp.getValue();
Type type = nameValue.getType();
assertNotNull( type );
assertTyping( BasicType.class, type );
AbstractStandardBasicType basicType = assertTyping( AbstractStandardBasicType.class, type );
assertSame( StringTypeDescriptor.INSTANCE, basicType.getJavaTypeDescriptor() );
assertEquals( Types.VARCHAR, basicType.getSqlTypeDescriptor().getSqlType() );
}
}
@Entity
public static class Tester {
@Id
private Long id;
private String name;
}
@Entity
public static class Tester2 {
@Id
private Long id;
@Convert(disableConversion = true)
private String name;
}
@Entity
public static class Tester3 {
@Id
private Long id;
@org.hibernate.annotations.Type( type = "string" )
private String name;
}
@Converter( autoApply = true )
public static class StringClobConverter implements AttributeConverter<String,Clob> {
@Override
public Clob convertToDatabaseColumn(String attribute) {
return null;
}
@Override
public String convertToEntityAttribute(Clob dbData) {
return null;
}
}
}

View File

@ -37,4 +37,18 @@ public class ExtraAssertions {
);
}
}
@SuppressWarnings("unchecked")
public static <T> T assertTyping(Class<T> expectedType, Object value) {
if ( ! expectedType.isInstance( value ) ) {
Assert.fail(
String.format(
"Expecting value of type [%s], but found [%s]",
expectedType.getName(),
value == null ? "<null>" : value
)
);
}
return (T) value;
}
}