HHH-5262 - Allow UserType and CompositeUserType to be registered with BasicTypeRegistry

git-svn-id: https://svn.jboss.org/repos/hibernate/core/trunk@19649 1b8cb986-b30d-0410-93ca-fae66ebed9b2
This commit is contained in:
Steve Ebersole 2010-06-02 05:15:54 +00:00
parent 3dd364c716
commit 5af328cb4a
6 changed files with 265 additions and 73 deletions

View File

@ -58,6 +58,7 @@ import org.w3c.dom.Document;
import org.xml.sax.EntityResolver; import org.xml.sax.EntityResolver;
import org.xml.sax.InputSource; import org.xml.sax.InputSource;
import org.hibernate.DuplicateMappingException;
import org.hibernate.EmptyInterceptor; import org.hibernate.EmptyInterceptor;
import org.hibernate.HibernateException; import org.hibernate.HibernateException;
import org.hibernate.Interceptor; import org.hibernate.Interceptor;
@ -66,9 +67,6 @@ import org.hibernate.MappingException;
import org.hibernate.MappingNotFoundException; import org.hibernate.MappingNotFoundException;
import org.hibernate.SessionFactory; import org.hibernate.SessionFactory;
import org.hibernate.SessionFactoryObserver; import org.hibernate.SessionFactoryObserver;
import org.hibernate.DuplicateMappingException;
import org.hibernate.id.IdentifierGeneratorAggregator;
import org.hibernate.tuple.entity.EntityTuplizerFactory;
import org.hibernate.dialect.Dialect; import org.hibernate.dialect.Dialect;
import org.hibernate.dialect.MySQLDialect; import org.hibernate.dialect.MySQLDialect;
import org.hibernate.dialect.function.SQLFunction; import org.hibernate.dialect.function.SQLFunction;
@ -107,36 +105,39 @@ import org.hibernate.event.RefreshEventListener;
import org.hibernate.event.ReplicateEventListener; import org.hibernate.event.ReplicateEventListener;
import org.hibernate.event.SaveOrUpdateEventListener; import org.hibernate.event.SaveOrUpdateEventListener;
import org.hibernate.id.IdentifierGenerator; import org.hibernate.id.IdentifierGenerator;
import org.hibernate.id.IdentifierGeneratorAggregator;
import org.hibernate.id.PersistentIdentifierGenerator; import org.hibernate.id.PersistentIdentifierGenerator;
import org.hibernate.id.factory.IdentifierGeneratorFactory;
import org.hibernate.id.factory.DefaultIdentifierGeneratorFactory; import org.hibernate.id.factory.DefaultIdentifierGeneratorFactory;
import org.hibernate.id.factory.IdentifierGeneratorFactory;
import org.hibernate.impl.SessionFactoryImpl; import org.hibernate.impl.SessionFactoryImpl;
import org.hibernate.mapping.AuxiliaryDatabaseObject; import org.hibernate.mapping.AuxiliaryDatabaseObject;
import org.hibernate.mapping.Collection; import org.hibernate.mapping.Collection;
import org.hibernate.mapping.Column;
import org.hibernate.mapping.DenormalizedTable;
import org.hibernate.mapping.FetchProfile;
import org.hibernate.mapping.ForeignKey; import org.hibernate.mapping.ForeignKey;
import org.hibernate.mapping.IdentifierCollection; import org.hibernate.mapping.IdentifierCollection;
import org.hibernate.mapping.Index; import org.hibernate.mapping.Index;
import org.hibernate.mapping.MappedSuperclass;
import org.hibernate.mapping.PersistentClass; import org.hibernate.mapping.PersistentClass;
import org.hibernate.mapping.Property; import org.hibernate.mapping.Property;
import org.hibernate.mapping.RootClass; import org.hibernate.mapping.RootClass;
import org.hibernate.mapping.SimpleValue; import org.hibernate.mapping.SimpleValue;
import org.hibernate.mapping.Table; import org.hibernate.mapping.Table;
import org.hibernate.mapping.UniqueKey;
import org.hibernate.mapping.FetchProfile;
import org.hibernate.mapping.DenormalizedTable;
import org.hibernate.mapping.TypeDef; import org.hibernate.mapping.TypeDef;
import org.hibernate.mapping.Column; import org.hibernate.mapping.UniqueKey;
import org.hibernate.mapping.MappedSuperclass;
import org.hibernate.proxy.EntityNotFoundDelegate; import org.hibernate.proxy.EntityNotFoundDelegate;
import org.hibernate.secure.JACCConfiguration; import org.hibernate.secure.JACCConfiguration;
import org.hibernate.tool.hbm2ddl.DatabaseMetadata; import org.hibernate.tool.hbm2ddl.DatabaseMetadata;
import org.hibernate.tool.hbm2ddl.TableMetadata;
import org.hibernate.tool.hbm2ddl.IndexMetadata; import org.hibernate.tool.hbm2ddl.IndexMetadata;
import org.hibernate.tool.hbm2ddl.TableMetadata;
import org.hibernate.tuple.entity.EntityTuplizerFactory;
import org.hibernate.type.BasicType; import org.hibernate.type.BasicType;
import org.hibernate.type.BasicTypeRegistry;
import org.hibernate.type.SerializationException; import org.hibernate.type.SerializationException;
import org.hibernate.type.Type; import org.hibernate.type.Type;
import org.hibernate.type.TypeResolver; import org.hibernate.type.TypeResolver;
import org.hibernate.usertype.CompositeUserType;
import org.hibernate.usertype.UserType;
import org.hibernate.util.ArrayHelper; import org.hibernate.util.ArrayHelper;
import org.hibernate.util.CollectionHelper; import org.hibernate.util.CollectionHelper;
import org.hibernate.util.ConfigHelper; import org.hibernate.util.ConfigHelper;
@ -2287,10 +2288,25 @@ public class Configuration implements Serializable {
return typeResolver; return typeResolver;
} }
/**
* Allows registration of a type into the type regsitry. The phrase 'override' in the method name simply
* reminds that registration *potentially* replaces a previously registered type .
*
* @param type The type to register.
*/
public void registerTypeOverride(BasicType type) { public void registerTypeOverride(BasicType type) {
getTypeResolver().registerTypeOverride( type ); getTypeResolver().registerTypeOverride( type );
} }
public void registerTypeOverride(UserType type, String[] keys) {
getTypeResolver().registerTypeOverride( type, keys );
}
public void registerTypeOverride(CompositeUserType type, String[] keys) {
getTypeResolver().registerTypeOverride( type, keys );
}
public SessionFactoryObserver getSessionFactoryObserver() { public SessionFactoryObserver getSessionFactoryObserver() {
return sessionFactoryObserver; return sessionFactoryObserver;
} }

View File

@ -31,6 +31,8 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.hibernate.HibernateException; import org.hibernate.HibernateException;
import org.hibernate.usertype.CompositeUserType;
import org.hibernate.usertype.UserType;
/** /**
* A registry of {@link BasicType} instances * A registry of {@link BasicType} instances
@ -146,6 +148,14 @@ public class BasicTypeRegistry implements Serializable {
} }
} }
public void register(UserType type, String[] keys) {
register( new CustomType( type, keys ) );
}
public void register(CompositeUserType type, String[] keys) {
register( new CompositeCustomType( type, keys ) );
}
public BasicType getRegisteredType(String key) { public BasicType getRegisteredType(String key) {
return registry.get( key ); return registry.get( key );
} }

View File

@ -29,10 +29,10 @@ import java.sql.PreparedStatement;
import java.sql.ResultSet; import java.sql.ResultSet;
import java.sql.SQLException; import java.sql.SQLException;
import java.util.Map; import java.util.Map;
import java.util.Properties;
import org.dom4j.Element; import org.dom4j.Element;
import org.dom4j.Node; import org.dom4j.Node;
import org.hibernate.EntityMode; import org.hibernate.EntityMode;
import org.hibernate.FetchMode; import org.hibernate.FetchMode;
import org.hibernate.HibernateException; import org.hibernate.HibernateException;
@ -42,18 +42,38 @@ import org.hibernate.engine.Mapping;
import org.hibernate.engine.SessionFactoryImplementor; import org.hibernate.engine.SessionFactoryImplementor;
import org.hibernate.engine.SessionImplementor; import org.hibernate.engine.SessionImplementor;
import org.hibernate.usertype.CompositeUserType; import org.hibernate.usertype.CompositeUserType;
import org.hibernate.usertype.LoggableUserType;
import org.hibernate.util.ArrayHelper;
/** /**
* Adapts <tt>CompositeUserType</tt> to <tt>Type</tt> interface * Adapts {@link CompositeUserType} to the {@link Type} interface
*
* @author Gavin King * @author Gavin King
* @author Steve Ebersole
*/ */
public class CompositeCustomType extends AbstractType implements CompositeType { public class CompositeCustomType extends AbstractType implements CompositeType, BasicType {
private final CompositeUserType userType; private final CompositeUserType userType;
private final String[] registrationKeys;
private final String name; private final String name;
private final boolean customLogging;
public CompositeCustomType(CompositeUserType userType) { public CompositeCustomType(CompositeUserType userType) {
this( userType, ArrayHelper.EMPTY_STRING_ARRAY );
}
public CompositeCustomType(CompositeUserType userType, String[] registrationKeys) {
this.userType = userType; this.userType = userType;
this.name = userType.getClass().getName(); this.name = userType.getClass().getName();
this.customLogging = LoggableUserType.class.isInstance( userType );
this.registrationKeys = registrationKeys;
}
public String[] getRegistrationKeys() {
return registrationKeys;
}
public CompositeUserType getUserType() {
return userType;
} }
public boolean isMethodOf(Method method) { public boolean isMethodOf(Method method) {
@ -155,8 +175,8 @@ public class CompositeCustomType extends AbstractType implements CompositeType {
public int getColumnSpan(Mapping mapping) throws MappingException { public int getColumnSpan(Mapping mapping) throws MappingException {
Type[] types = userType.getPropertyTypes(); Type[] types = userType.getPropertyTypes();
int n=0; int n=0;
for (int i=0; i<types.length; i++) { for ( Type type : types ) {
n+=types[i].getColumnSpan(mapping); n += type.getColumnSpan( mapping );
} }
return n; return n;
} }
@ -217,20 +237,26 @@ public class CompositeCustomType extends AbstractType implements CompositeType {
} }
public int[] sqlTypes(Mapping mapping) throws MappingException { public int[] sqlTypes(Mapping mapping) throws MappingException {
Type[] types = userType.getPropertyTypes();
int[] result = new int[ getColumnSpan(mapping) ]; int[] result = new int[ getColumnSpan(mapping) ];
int n=0; int n=0;
for (int i=0; i<types.length; i++) { for ( Type type : userType.getPropertyTypes() ) {
int[] sqlTypes = types[i].sqlTypes(mapping); for ( int sqlType : type.sqlTypes( mapping ) ) {
for ( int k=0; k<sqlTypes.length; k++ ) result[n++] = sqlTypes[k]; result[n++] = sqlType;
}
} }
return result; return result;
} }
public String toLoggableString(Object value, SessionFactoryImplementor factory) public String toLoggableString(Object value, SessionFactoryImplementor factory) throws HibernateException {
throws HibernateException { if ( value == null ) {
return "null";
return value==null ? "null" : value.toString(); }
else if ( customLogging ) {
return ( (LoggableUserType) userType ).toLoggableString( value, factory );
}
else {
return value.toString();
}
} }
public boolean[] getPropertyNullability() { public boolean[] getPropertyNullability() {

View File

@ -44,32 +44,42 @@ import org.hibernate.usertype.EnhancedUserType;
import org.hibernate.usertype.LoggableUserType; import org.hibernate.usertype.LoggableUserType;
import org.hibernate.usertype.UserType; import org.hibernate.usertype.UserType;
import org.hibernate.usertype.UserVersionType; import org.hibernate.usertype.UserVersionType;
import org.hibernate.util.ArrayHelper;
/** /**
* Adapts {@link UserType} to the generic {@link Type} interface, in order * Adapts {@link UserType} to the generic {@link Type} interface, in order
* to isolate user code from changes in the internal Type contracts. * to isolate user code from changes in the internal Type contracts.
* *
* @see org.hibernate.usertype.UserType
* @author Gavin King * @author Gavin King
* @author Steve Ebersole
*/ */
public class CustomType extends AbstractType implements IdentifierType, DiscriminatorType, VersionType { public class CustomType extends AbstractType implements IdentifierType, DiscriminatorType, VersionType, BasicType {
private final UserType userType; private final UserType userType;
private final String name; private final String name;
private final int[] types; private final int[] types;
private final boolean customLogging; private final boolean customLogging;
private final String[] registrationKeys;
public CustomType(UserType userType) throws MappingException { public CustomType(UserType userType) throws MappingException {
this( userType, ArrayHelper.EMPTY_STRING_ARRAY );
}
public CustomType(UserType userType, String[] registrationKeys) throws MappingException {
this.userType = userType; this.userType = userType;
this.name = userType.getClass().getName(); this.name = userType.getClass().getName();
this.types = userType.sqlTypes(); this.types = userType.sqlTypes();
this.customLogging = LoggableUserType.class.isInstance( userType ); this.customLogging = LoggableUserType.class.isInstance( userType );
this.registrationKeys = registrationKeys;
} }
public UserType getUserType() { public UserType getUserType() {
return userType; return userType;
} }
public String[] getRegistrationKeys() {
return registrationKeys;
}
public int[] sqlTypes(Mapping pi) { public int[] sqlTypes(Mapping pi) {
return types; return types;
} }
@ -86,8 +96,7 @@ public class CustomType extends AbstractType implements IdentifierType, Discrimi
return userType.equals(x, y); return userType.equals(x, y);
} }
public boolean isEqual(Object x, Object y, EntityMode entityMode) public boolean isEqual(Object x, Object y, EntityMode entityMode) throws HibernateException {
throws HibernateException {
return isEqual(x, y); return isEqual(x, y);
} }
@ -95,33 +104,24 @@ public class CustomType extends AbstractType implements IdentifierType, Discrimi
return userType.hashCode(x); return userType.hashCode(x);
} }
public Object nullSafeGet( public Object nullSafeGet(ResultSet rs, String[] names, SessionImplementor session, Object owner)
ResultSet rs, throws HibernateException, SQLException {
String[] names,
SessionImplementor session,
Object owner
) throws HibernateException, SQLException {
return userType.nullSafeGet(rs, names, owner); return userType.nullSafeGet(rs, names, owner);
} }
public Object nullSafeGet( public Object nullSafeGet(ResultSet rs, String columnName, SessionImplementor session, Object owner)
ResultSet rs, throws HibernateException, SQLException {
String columnName,
SessionImplementor session,
Object owner
) throws HibernateException, SQLException {
return nullSafeGet(rs, new String[] { columnName }, session, owner); return nullSafeGet(rs, new String[] { columnName }, session, owner);
} }
public Object assemble(Serializable cached, SessionImplementor session, Object owner) public Object assemble(Serializable cached, SessionImplementor session, Object owner)
throws HibernateException { throws HibernateException {
return userType.assemble(cached, owner); return userType.assemble(cached, owner);
} }
public Serializable disassemble(Object value, SessionImplementor session, Object owner) public Serializable disassemble(Object value, SessionImplementor session, Object owner)
throws HibernateException { throws HibernateException {
return userType.disassemble(value); return userType.disassemble(value);
} }
@ -130,42 +130,36 @@ public class CustomType extends AbstractType implements IdentifierType, Discrimi
Object target, Object target,
SessionImplementor session, SessionImplementor session,
Object owner, Object owner,
Map copyCache) Map copyCache) throws HibernateException {
throws HibernateException {
return userType.replace(original, target, owner); return userType.replace(original, target, owner);
} }
public void nullSafeSet( public void nullSafeSet(PreparedStatement st, Object value, int index, boolean[] settable, SessionImplementor session)
PreparedStatement st, throws HibernateException, SQLException {
Object value, if ( settable[0] ) {
int index, userType.nullSafeSet( st, value, index );
boolean[] settable, }
SessionImplementor session
) throws HibernateException, SQLException {
if ( settable[0] ) userType.nullSafeSet(st, value, index);
} }
public void nullSafeSet( public void nullSafeSet(PreparedStatement st, Object value, int index, SessionImplementor session)
PreparedStatement st, throws HibernateException, SQLException {
Object value, userType.nullSafeSet( st, value, index );
int index,
SessionImplementor session
) throws HibernateException, SQLException {
userType.nullSafeSet(st, value, index);
} }
@SuppressWarnings({ "UnusedDeclaration" })
public String toXMLString(Object value, SessionFactoryImplementor factory) { public String toXMLString(Object value, SessionFactoryImplementor factory) {
if (value==null) return null; if ( value == null ) {
if (userType instanceof EnhancedUserType) { return null;
return ( (EnhancedUserType) userType ).toXMLString(value); }
if ( userType instanceof EnhancedUserType ) {
return ( (EnhancedUserType) userType ).toXMLString( value );
} }
else { else {
return value.toString(); return value.toString();
} }
} }
@SuppressWarnings({ "UnusedDeclaration" })
public Object fromXMLString(String xml, Mapping factory) { public Object fromXMLString(String xml, Mapping factory) {
return ( (EnhancedUserType) userType ).fromXMLString(xml); return ( (EnhancedUserType) userType ).fromXMLString(xml);
} }
@ -175,7 +169,7 @@ public class CustomType extends AbstractType implements IdentifierType, Discrimi
} }
public Object deepCopy(Object value, EntityMode entityMode, SessionFactoryImplementor factory) public Object deepCopy(Object value, EntityMode entityMode, SessionFactoryImplementor factory)
throws HibernateException { throws HibernateException {
return userType.deepCopy(value); return userType.deepCopy(value);
} }
@ -208,12 +202,12 @@ public class CustomType extends AbstractType implements IdentifierType, Discrimi
} }
public void setToXMLNode(Node node, Object value, SessionFactoryImplementor factory) public void setToXMLNode(Node node, Object value, SessionFactoryImplementor factory)
throws HibernateException { throws HibernateException {
node.setText( toXMLString(value, factory) ); node.setText( toXMLString(value, factory) );
} }
public String toLoggableString(Object value, SessionFactoryImplementor factory) public String toLoggableString(Object value, SessionFactoryImplementor factory)
throws HibernateException { throws HibernateException {
if ( value == null ) { if ( value == null ) {
return "null"; return "null";
} }
@ -227,12 +221,14 @@ public class CustomType extends AbstractType implements IdentifierType, Discrimi
public boolean[] toColumnNullness(Object value, Mapping mapping) { public boolean[] toColumnNullness(Object value, Mapping mapping) {
boolean[] result = new boolean[ getColumnSpan(mapping) ]; boolean[] result = new boolean[ getColumnSpan(mapping) ];
if (value!=null) Arrays.fill(result, true); if ( value != null ) {
Arrays.fill(result, true);
}
return result; return result;
} }
public boolean isDirty(Object old, Object current, boolean[] checkable, SessionImplementor session) throws HibernateException { public boolean isDirty(Object old, Object current, boolean[] checkable, SessionImplementor session)
throws HibernateException {
return checkable[0] && isDirty(old, current, session); return checkable[0] && isDirty(old, current, session);
} }
} }

View File

@ -60,6 +60,14 @@ public class TypeResolver implements Serializable {
basicTypeRegistry.register( type ); basicTypeRegistry.register( type );
} }
public void registerTypeOverride(UserType type, String[] keys) {
basicTypeRegistry.register( type, keys );
}
public void registerTypeOverride(CompositeUserType type, String[] keys) {
basicTypeRegistry.register( type, keys );
}
public TypeFactory getTypeFactory() { public TypeFactory getTypeFactory() {
return typeFactory; return typeFactory;
} }

View File

@ -23,13 +23,21 @@
*/ */
package org.hibernate.type; package org.hibernate.type;
import java.io.Serializable;
import java.net.URL; import java.net.URL;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.UUID; import java.util.UUID;
import junit.framework.TestCase; import junit.framework.TestCase;
import org.hibernate.HibernateException;
import org.hibernate.engine.SessionImplementor;
import org.hibernate.type.descriptor.java.UrlTypeDescriptor; import org.hibernate.type.descriptor.java.UrlTypeDescriptor;
import org.hibernate.type.descriptor.sql.VarcharTypeDescriptor; import org.hibernate.type.descriptor.sql.VarcharTypeDescriptor;
import org.hibernate.usertype.CompositeUserType;
import org.hibernate.usertype.UserType;
/** /**
* TODO : javadoc * TODO : javadoc
@ -67,6 +75,27 @@ public class BasicTypeRegistryTest extends TestCase {
assertSame( UrlType.INSTANCE, type ); assertSame( UrlType.INSTANCE, type );
} }
public void testRegisteringUserTypes() {
registry.register( new TotallyIrrelevantUserType(), new String[] { "key" } );
BasicType type = registry.getRegisteredType( "key" );
assertNotNull( type );
assertEquals( CustomType.class, type.getClass() );
assertEquals( TotallyIrrelevantUserType.class, ( (CustomType) type ).getUserType().getClass() );
registry.register( new TotallyIrrelevantCompositeUserType(), new String[] { "key" } );
type = registry.getRegisteredType( "key" );
assertNotNull( type );
assertEquals( CompositeCustomType.class, type.getClass() );
assertEquals( TotallyIrrelevantCompositeUserType.class, ( (CompositeCustomType) type ).getUserType().getClass() );
type = registry.getRegisteredType( UUID.class.getName() );
assertSame( UUIDBinaryType.INSTANCE, type );
registry.register( new TotallyIrrelevantUserType(), new String[] { UUID.class.getName() } );
type = registry.getRegisteredType( UUID.class.getName() );
assertNotSame( UUIDBinaryType.INSTANCE, type );
assertEquals( CustomType.class, type.getClass() );
}
public static class UrlType extends AbstractSingleColumnStandardBasicType<URL> { public static class UrlType extends AbstractSingleColumnStandardBasicType<URL> {
public static final UrlType INSTANCE = new UrlType(); public static final UrlType INSTANCE = new UrlType();
@ -83,4 +112,111 @@ public class BasicTypeRegistryTest extends TestCase {
return true; return true;
} }
} }
public static class TotallyIrrelevantUserType implements UserType {
public int[] sqlTypes() {
return new int[0];
}
public Class returnedClass() {
return null;
}
public boolean equals(Object x, Object y) throws HibernateException {
return false;
}
public int hashCode(Object x) throws HibernateException {
return 0;
}
public Object nullSafeGet(ResultSet rs, String[] names, Object owner) throws HibernateException, SQLException {
return null;
}
public void nullSafeSet(PreparedStatement st, Object value, int index) throws HibernateException, SQLException {
}
public Object deepCopy(Object value) throws HibernateException {
return null;
}
public boolean isMutable() {
return false;
}
public Serializable disassemble(Object value) throws HibernateException {
return null;
}
public Object assemble(Serializable cached, Object owner) throws HibernateException {
return null;
}
public Object replace(Object original, Object target, Object owner) throws HibernateException {
return null;
}
}
public static class TotallyIrrelevantCompositeUserType implements CompositeUserType {
public String[] getPropertyNames() {
return new String[0];
}
public Type[] getPropertyTypes() {
return new Type[0];
}
public Object getPropertyValue(Object component, int property) throws HibernateException {
return null;
}
public void setPropertyValue(Object component, int property, Object value) throws HibernateException {
}
public Class returnedClass() {
return null;
}
public boolean equals(Object x, Object y) throws HibernateException {
return false;
}
public int hashCode(Object x) throws HibernateException {
return 0;
}
public Object nullSafeGet(ResultSet rs, String[] names, SessionImplementor session, Object owner)
throws HibernateException, SQLException {
return null;
}
public void nullSafeSet(PreparedStatement st, Object value, int index, SessionImplementor session)
throws HibernateException, SQLException {
}
public Object deepCopy(Object value) throws HibernateException {
return null;
}
public boolean isMutable() {
return false;
}
public Serializable disassemble(Object value, SessionImplementor session) throws HibernateException {
return null;
}
public Object assemble(Serializable cached, SessionImplementor session, Object owner)
throws HibernateException {
return null;
}
public Object replace(Object original, Object target, SessionImplementor session, Object owner)
throws HibernateException {
return null;
}
}
} }