Commits

Bryan Turner committed 9b0ab79

Refining Hibernate 3, 3.6 and 4 support.

- Refactored the SqlServerdialectResolver to allow for subclasses to
return customised versions of the different dialects, simplifying
returning Unicode-aware dialects for systems that need them
- Added serialisation proxy to GenericEnumUserType to make the type
serialisable, since (at least under Hibernate 4) Hibernate appears
to serialise the type along with the data
- Made EmptyStringUserType serialisable, a simple change since it has
no state to maintain

Comments (0)

Files changed (9)

atlassian-hibernate3-extras/src/main/java/com/atlassian/hibernate/extras/dialect/resolver/SqlServerDialectResolver.java

             int databaseMajorVersion = metaData.getDatabaseMajorVersion();
             switch (databaseMajorVersion)
             {
-                case 8: //SQL Server 2000
-                case 9: //SQL Server 2005 (this is buggy, but to fix we must upgrade to Hibernate 3.6+)
-                    log.debug("Returning SQL Server 2000 dialect for {} (Version: {})",
-                            databaseName, databaseMajorVersion);
-                    return new SQLServerDialect();
-                case 10: //SQL Server 2008
-                case 11: //SQL Server 2012 is more like the 2008 dialect than the 2000 dialect
-                    log.debug("Returning SQL Server 2008 dialect for {} (Version: {})",
-                            databaseName, databaseMajorVersion);
-                    return new SQLServer2008Dialect();
+                case 8:
+                    return createSqlServer2000Dialect(databaseName, databaseMajorVersion);
+                case 9:
+                    return createSqlServer2005Dialect(databaseName, databaseMajorVersion);
+                case 10:
+                    return createSqlServer2008Dialect(databaseName, databaseMajorVersion);
+                case 11:
+                    return createSqlServer2012Dialect(databaseName, databaseMajorVersion);
             }
         }
 
         log.trace("No dialect is known for {}; deferring to standard dialect resolver", databaseName);
         return null;
     }
+
+    /**
+     * Creates a {@code SQLServerDialect} suitable for use with SQL Server 2000.
+     *
+     * @param databaseName         the database product name
+     * @param databaseMajorVersion the database major version
+     * @return the created dialect
+     */
+    protected Dialect createSqlServer2000Dialect(String databaseName, int databaseMajorVersion)
+    {
+        log.debug("Returning SQL Server 2000 dialect for {} (Version: {})",
+                databaseName, databaseMajorVersion);
+        return new SQLServerDialect();
+    }
+
+    /**
+     * Delegates to the {@link #createSqlServer2000Dialect(String, int) SQLServerDialect} as Hibernate does not have
+     * a distinct dialect for SQL Server 2005 prior to Hibernate 3.6.
+     *
+     * @param databaseName         the database product name
+     * @param databaseMajorVersion the database major version
+     * @return the created dialect
+     * @see #createSqlServer2000Dialect(String, int)
+     */
+    protected Dialect createSqlServer2005Dialect(String databaseName, int databaseMajorVersion)
+    {
+        //This is buggy, but to fix we must upgrade to Hibernate 3.6+
+        return createSqlServer2000Dialect(databaseName, databaseMajorVersion);
+    }
+
+    /**
+     * Creates a {@code SQLServer2008Dialect} suitable for use with SQL Server 2008.
+     *
+     * @param databaseName         the database product name
+     * @param databaseMajorVersion the database major version
+     * @return the created dialect
+     */
+    protected Dialect createSqlServer2008Dialect(String databaseName, int databaseMajorVersion)
+    {
+        log.debug("Returning SQL Server 2008 dialect for {} (Version: {})",
+                databaseName, databaseMajorVersion);
+        return new SQLServer2008Dialect();
+    }
+
+    /**
+     * Delegates to the {@link #createSqlServer2008Dialect(String, int) SQLServer2008Dialect} as Hibernate does not
+     * have a distinct dialect for SQL Server 2012.
+     *
+     * @param databaseName         the database product name
+     * @param databaseMajorVersion the database major version
+     * @return the created dialect
+     * @see #createSqlServer2008Dialect(String, int)
+     */
+    protected Dialect createSqlServer2012Dialect(String databaseName, int databaseMajorVersion)
+    {
+        //SQL Server 2012 is more like the 2008 dialect than the 2000 dialect, which is what Hibernate's
+        //StandardDialectResolver returns for it
+        return createSqlServer2008Dialect(databaseName, databaseMajorVersion);
+    }
 }

atlassian-hibernate3-extras/src/main/java/com/atlassian/hibernate/extras/type/EmptyStringUserType.java

  * for empty strings, see http://download-west.oracle.com/docs/cd/B12037_01/server.101/b10759/sql_elements005.htm.
  */
 @SuppressWarnings({"deprecation", "unused"})
-public class EmptyStringUserType implements UserType
+public class EmptyStringUserType implements Serializable, UserType
 {
     private static final String EMPTY_STRING = "";
     private static final int[] TYPES = {Types.VARCHAR};
 
     public void nullSafeSet(PreparedStatement st, Object value, int index) throws HibernateException, SQLException
     {
-        Hibernate.STRING.nullSafeSet(st, (String) value, index);
+        Hibernate.STRING.nullSafeSet(st, value, index);
     }
 
     /* (non-Javadoc)
     {
         return TYPES;
     }
-}
+}

atlassian-hibernate3-extras/src/main/java/com/atlassian/hibernate/extras/type/GenericEnumUserType.java

 import org.hibernate.usertype.EnhancedUserType;
 import org.hibernate.usertype.ParameterizedType;
 
+import java.io.ObjectInputStream;
 import java.io.Serializable;
 import java.lang.reflect.Method;
 import java.sql.PreparedStatement;
  * @since 4.0
  */
 @SuppressWarnings({"deprecation", "unused"})
-public class GenericEnumUserType implements EnhancedUserType, ParameterizedType
+public class GenericEnumUserType implements EnhancedUserType, ParameterizedType, Serializable
 {
     private static final String DEFAULT_IDENTIFIER_METHOD_NAME = "getId";
     private static final String DEFAULT_VALUE_OF_METHOD_NAME = "fromId";
     public void setParameterValues(Properties parameters)
     {
         String enumClassName = parameters.getProperty("enumClass");
+        String identifierMethodName = parameters.getProperty("identifierMethod", DEFAULT_IDENTIFIER_METHOD_NAME);
+        String valueOfMethodName = parameters.getProperty("valueOfMethod", DEFAULT_VALUE_OF_METHOD_NAME);
+
+        initialize(enumClassName, identifierMethodName, valueOfMethodName);
+    }
+
+    public int[] sqlTypes()
+    {
+        return sqlTypes;
+    }
+
+    public String toXMLString(Object value)
+    {
+        return ((Enum<?>) value).name();
+    }
+
+    @SuppressWarnings("unchecked")
+    private void initialize(String enumClassName, String identifierMethodName, String valueOfMethodName)
+    {
         try
         {
             enumClass = Class.forName(enumClassName).asSubclass(Enum.class);
             throw new HibernateException("Enum class not found", exception);
         }
 
-        String identifierMethodName = parameters.getProperty("identifierMethod", DEFAULT_IDENTIFIER_METHOD_NAME);
         try
         {
             identifierMethod = enumClass.getMethod(identifierMethodName, NULL_CLASS_VARARG);
         }
         Class<?> identifierType = identifierMethod.getReturnType();
 
-        String valueOfMethodName = parameters.getProperty("valueOfMethod", DEFAULT_VALUE_OF_METHOD_NAME);
         try
         {
             valueOfMethod = enumClass.getMethod(valueOfMethodName, identifierType);
         sqlTypes = new int[]{type.sqlType()};
     }
 
-    public int[] sqlTypes()
+    /**
+     * Prevents attempts to deserialize the {@code GenericEnumUserType} directly, as {@link SerializationProxy}
+     * should have been written in its place.
+     *
+     * @param stream ignored
+     */
+    private void readObject(ObjectInputStream stream)
     {
-        return sqlTypes;
+        throw new UnsupportedOperationException(getClass().getName() + " cannot be deserialized directly");
     }
 
-    public String toXMLString(Object value)
+    /**
+     * Replaces this {@code GenericEnumUserType} (which is not really serializable due to the {@code Method} fields)
+     * with a simple {@link SerializationProxy} containing the names for the enum class, the identifier method and
+     * the valueOf method.
+     *
+     * @return a new {@link SerializationProxy} to be serialized in this type's place
+     */
+    private Object writeReplace()
     {
-        return ((Enum<?>) value).name();
+        return new SerializationProxy(enumClass.getName(), identifierMethod.getName(), valueOfMethod.getName());
+    }
+
+    /**
+     * Simple proxy to serialize in place of the more complicated {@link GenericEnumUserType}. This proxy contains
+     * all the data necessary to recreate/reinitialize the full {@link GenericEnumUserType}.
+     */
+    private static class SerializationProxy implements Serializable
+    {
+
+        private final String enumClassName;
+        private final String identifierMethodName;
+        private final String valueOfMethodName;
+
+        private SerializationProxy(String enumClassName, String identifierMethodName, String valueOfMethodName)
+        {
+            this.enumClassName = enumClassName;
+            this.identifierMethodName = identifierMethodName;
+            this.valueOfMethodName = valueOfMethodName;
+        }
+
+        private Object readResolve()
+        {
+            GenericEnumUserType type = new GenericEnumUserType();
+            type.initialize(enumClassName, identifierMethodName, valueOfMethodName);
+
+            return type;
+        }
     }
 }

atlassian-hibernate3.6-extras/src/main/java/com/atlassian/hibernate/extras/dialect/resolver/SqlServerDialectResolver.java

             int databaseMajorVersion = metaData.getDatabaseMajorVersion();
             switch (databaseMajorVersion)
             {
-                case 8: //SQL Server 2000
-                    log.debug("Returning SQL Server 2000 dialect for {} (Version: {})",
-                            databaseName, databaseMajorVersion);
-                    return new SQLServerDialect();
-                case 9: //SQL Server 2005
-                    log.debug("Returning SQL Server 2005 dialect for {} (Version: {})",
-                            databaseName, databaseMajorVersion);
-
-                    return new SQLServer2005Dialect();
-                case 10: //SQL Server 2008
-                case 11: //SQL Server 2012 is more like the 2008 dialect than the 2000 dialect
-                    log.debug("Returning SQL Server 2008 dialect for {} (Version: {})",
-                            databaseName, databaseMajorVersion);
-                    return new SQLServer2008Dialect();
+                case 8:
+                    return createSqlServer2000Dialect(databaseName, databaseMajorVersion);
+                case 9:
+                    return createSqlServer2005Dialect(databaseName, databaseMajorVersion);
+                case 10:
+                    return createSqlServer2008Dialect(databaseName, databaseMajorVersion);
+                case 11:
+                    return createSqlServer2012Dialect(databaseName, databaseMajorVersion);
             }
         }
 
         log.trace("No dialect is known for {}; deferring to standard dialect resolver", databaseName);
         return null;
     }
+
+    /**
+     * Creates a {@code SQLServerDialect} suitable for use with SQL Server 2000.
+     *
+     * @param databaseName         the database product name
+     * @param databaseMajorVersion the database major version
+     * @return the created dialect
+     */
+    protected Dialect createSqlServer2000Dialect(String databaseName, int databaseMajorVersion)
+    {
+        log.debug("Returning SQL Server 2000 dialect for {} (Version: {})",
+                databaseName, databaseMajorVersion);
+        return new SQLServerDialect();
+    }
+
+    /**
+     * Creates a {@code SQLServer2005Dialect} suitable for use with SQL Server 2005.
+     *
+     * @param databaseName         the database product name
+     * @param databaseMajorVersion the database major version
+     * @return the created dialect
+     */
+    protected Dialect createSqlServer2005Dialect(String databaseName, int databaseMajorVersion)
+    {
+        log.debug("Returning SQL Server 2005 dialect for {} (Version: {})",
+                databaseName, databaseMajorVersion);
+        return new SQLServer2005Dialect();
+    }
+
+    /**
+     * Creates a {@code SQLServer2008Dialect} suitable for use with SQL Server 2008.
+     *
+     * @param databaseName         the database product name
+     * @param databaseMajorVersion the database major version
+     * @return the created dialect
+     */
+    protected Dialect createSqlServer2008Dialect(String databaseName, int databaseMajorVersion)
+    {
+        log.debug("Returning SQL Server 2008 dialect for {} (Version: {})",
+                databaseName, databaseMajorVersion);
+        return new SQLServer2008Dialect();
+    }
+
+    /**
+     * Delegates to the {@link #createSqlServer2008Dialect(String, int) SQLServer2008Dialect} as Hibernate does not
+     * have a distinct dialect for SQL Server 2012.
+     *
+     * @param databaseName         the database product name
+     * @param databaseMajorVersion the database major version
+     * @return the created dialect
+     * @see #createSqlServer2008Dialect(String, int)
+     */
+    protected Dialect createSqlServer2012Dialect(String databaseName, int databaseMajorVersion)
+    {
+        //SQL Server 2012 is more like the 2008 dialect than the 2000 dialect, which is what Hibernate's
+        //StandardDialectResolver returns for it
+        return createSqlServer2008Dialect(databaseName, databaseMajorVersion);
+    }
 }

atlassian-hibernate3.6-extras/src/main/java/com/atlassian/hibernate/extras/type/EmptyStringUserType.java

 //      Hibernate 3.6 to avoid these warnings, so they must simply be suppressed until we upgrade to Hibernate 4.
 //SEE:  https://hibernate.onjira.com/browse/HHH-5968
 @SuppressWarnings({"deprecation", "unused"})
-public class EmptyStringUserType implements UserType
+public class EmptyStringUserType implements Serializable, UserType
 {
     private static final String EMPTY_STRING = "";
     private static final int[] TYPES = {Types.VARCHAR};

atlassian-hibernate3.6-extras/src/main/java/com/atlassian/hibernate/extras/type/GenericEnumUserType.java

 import org.hibernate.usertype.EnhancedUserType;
 import org.hibernate.usertype.ParameterizedType;
 
+import java.io.ObjectInputStream;
 import java.io.Serializable;
 import java.lang.reflect.Method;
 import java.sql.PreparedStatement;
 //      Hibernate 3.6 to avoid these warnings, so they must simply be suppressed until we upgrade to Hibernate 4.
 //SEE:  https://hibernate.onjira.com/browse/HHH-5968
 @SuppressWarnings({"deprecation", "unused"})
-public class GenericEnumUserType implements EnhancedUserType, ParameterizedType
+public class GenericEnumUserType implements EnhancedUserType, ParameterizedType, Serializable
 {
     private static final String DEFAULT_IDENTIFIER_METHOD_NAME = "getId";
     private static final String DEFAULT_VALUE_OF_METHOD_NAME = "fromId";
         return enumClass;
     }
 
-    @SuppressWarnings("unchecked")
     public void setParameterValues(Properties parameters)
     {
         String enumClassName = parameters.getProperty("enumClass");
+        String identifierMethodName = parameters.getProperty("identifierMethod", DEFAULT_IDENTIFIER_METHOD_NAME);
+        String valueOfMethodName = parameters.getProperty("valueOfMethod", DEFAULT_VALUE_OF_METHOD_NAME);
+
+        initialize(enumClassName, identifierMethodName, valueOfMethodName);
+    }
+
+    public int[] sqlTypes()
+    {
+        return sqlTypes;
+    }
+
+    public String toXMLString(Object value)
+    {
+        return ((Enum<?>) value).name();
+    }
+
+    @SuppressWarnings("unchecked")
+    private void initialize(String enumClassName, String identifierMethodName, String valueOfMethodName)
+    {
         try
         {
             enumClass = Class.forName(enumClassName).asSubclass(Enum.class);
             throw new HibernateException("Enum class not found", exception);
         }
 
-        String identifierMethodName = parameters.getProperty("identifierMethod", DEFAULT_IDENTIFIER_METHOD_NAME);
         try
         {
             identifierMethod = enumClass.getMethod(identifierMethodName, NULL_CLASS_VARARG);
         }
         Class<?> identifierType = identifierMethod.getReturnType();
 
-        String valueOfMethodName = parameters.getProperty("valueOfMethod", DEFAULT_VALUE_OF_METHOD_NAME);
         try
         {
             valueOfMethod = enumClass.getMethod(valueOfMethodName, identifierType);
         sqlTypes = new int[]{type.sqlType()};
     }
 
-    public int[] sqlTypes()
+    /**
+     * Prevents attempts to deserialize the {@code GenericEnumUserType} directly, as {@link SerializationProxy}
+     * should have been written in its place.
+     *
+     * @param stream ignored
+     */
+    private void readObject(ObjectInputStream stream)
     {
-        return sqlTypes;
+        throw new UnsupportedOperationException(getClass().getName() + " cannot be deserialized directly");
     }
 
-    public String toXMLString(Object value)
+    /**
+     * Replaces this {@code GenericEnumUserType} (which is not really serializable due to the {@code Method} fields)
+     * with a simple {@link SerializationProxy} containing the names for the enum class, the identifier method and
+     * the valueOf method.
+     *
+     * @return a new {@link SerializationProxy} to be serialized in this type's place
+     */
+    private Object writeReplace()
     {
-        return ((Enum<?>) value).name();
+        return new SerializationProxy(enumClass.getName(), identifierMethod.getName(), valueOfMethod.getName());
+    }
+
+    /**
+     * Simple proxy to serialize in place of the more complicated {@link GenericEnumUserType}. This proxy contains
+     * all the data necessary to recreate/reinitialize the full {@link GenericEnumUserType}.
+     */
+    private static class SerializationProxy implements Serializable
+    {
+
+        private final String enumClassName;
+        private final String identifierMethodName;
+        private final String valueOfMethodName;
+
+        private SerializationProxy(String enumClassName, String identifierMethodName, String valueOfMethodName)
+        {
+            this.enumClassName = enumClassName;
+            this.identifierMethodName = identifierMethodName;
+            this.valueOfMethodName = valueOfMethodName;
+        }
+
+        private Object readResolve()
+        {
+            GenericEnumUserType type = new GenericEnumUserType();
+            type.initialize(enumClassName, identifierMethodName, valueOfMethodName);
+
+            return type;
+        }
     }
 }

atlassian-hibernate4-extras/src/main/java/com/atlassian/hibernate/extras/dialect/resolver/SqlServerDialectResolver.java

             switch (databaseMajorVersion)
             {
                 case 8:
-                    log.debug("Returning SQL Server 2000 dialect for {} (Version: {})",
-                            databaseName, databaseMajorVersion);
-                    return new SQLServerDialect();
+                    return createSqlServer2000Dialect(databaseName, databaseMajorVersion);
                 case 9:
-                    log.debug("Returning SQL Server 2005 dialect for {} (Version: {})",
-                            databaseName, databaseMajorVersion);
-                    return new SQLServer2005Dialect();
+                    return createSqlServer2005Dialect(databaseName, databaseMajorVersion);
                 case 10:
-                case 11: //SQL Server 2012 is more like the 2008 dialect than the 2000 dialect
-                    log.debug("Returning SQL Server 2008 dialect for {} (Version: {})",
-                            databaseName, databaseMajorVersion);
-                    return new SQLServer2008Dialect();
+                    return createSqlServer2008Dialect(databaseName, databaseMajorVersion);
+                case 11:
+                    return createSqlServer2012Dialect(databaseName, databaseMajorVersion);
             }
         }
 
         log.trace("No dialect is known for {}; deferring to standard dialect resolver", databaseName);
         return null;
     }
+
+    /**
+     * Creates a {@code SQLServerDialect} suitable for use with SQL Server 2000.
+     *
+     * @param databaseName         the database product name
+     * @param databaseMajorVersion the database major version
+     * @return the created dialect
+     */
+    protected Dialect createSqlServer2000Dialect(String databaseName, int databaseMajorVersion)
+    {
+        log.debug("Returning SQL Server 2000 dialect for {} (Version: {})",
+                databaseName, databaseMajorVersion);
+        return new SQLServerDialect();
+    }
+
+    /**
+     * Creates a {@code SQLServer2005Dialect} suitable for use with SQL Server 2005.
+     *
+     * @param databaseName         the database product name
+     * @param databaseMajorVersion the database major version
+     * @return the created dialect
+     */
+    protected Dialect createSqlServer2005Dialect(String databaseName, int databaseMajorVersion)
+    {
+        log.debug("Returning SQL Server 2005 dialect for {} (Version: {})",
+                databaseName, databaseMajorVersion);
+        return new SQLServer2005Dialect();
+    }
+
+    /**
+     * Creates a {@code SQLServer2008Dialect} suitable for use with SQL Server 2008.
+     *
+     * @param databaseName         the database product name
+     * @param databaseMajorVersion the database major version
+     * @return the created dialect
+     */
+    protected Dialect createSqlServer2008Dialect(String databaseName, int databaseMajorVersion)
+    {
+        log.debug("Returning SQL Server 2008 dialect for {} (Version: {})",
+                databaseName, databaseMajorVersion);
+        return new SQLServer2008Dialect();
+    }
+
+    /**
+     * Delegates to the {@link #createSqlServer2008Dialect(String, int) SQLServer2008Dialect} as Hibernate does not
+     * have a distinct dialect for SQL Server 2012.
+     *
+     * @param databaseName         the database product name
+     * @param databaseMajorVersion the database major version
+     * @return the created dialect
+     * @see #createSqlServer2008Dialect(String, int)
+     */
+    protected Dialect createSqlServer2012Dialect(String databaseName, int databaseMajorVersion)
+    {
+        //SQL Server 2012 is more like the 2008 dialect than the 2000 dialect, which is what Hibernate's
+        //StandardDialectResolver returns for it
+        return createSqlServer2008Dialect(databaseName, databaseMajorVersion);
+    }
 }

atlassian-hibernate4-extras/src/main/java/com/atlassian/hibernate/extras/type/EmptyStringUserType.java

  * for empty strings, see http://download-west.oracle.com/docs/cd/B12037_01/server.101/b10759/sql_elements005.htm.
  */
 @SuppressWarnings("unused")
-public class EmptyStringUserType implements UserType
+public class EmptyStringUserType implements Serializable, UserType
 {
     private static final String EMPTY_STRING = "";
     private static final int[] TYPES = {Types.VARCHAR};

atlassian-hibernate4-extras/src/main/java/com/atlassian/hibernate/extras/type/GenericEnumUserType.java

 import org.hibernate.usertype.EnhancedUserType;
 import org.hibernate.usertype.ParameterizedType;
 
+import java.io.ObjectInputStream;
 import java.io.Serializable;
 import java.lang.reflect.Method;
 import java.sql.PreparedStatement;
  * @since 4.0
  */
 @SuppressWarnings("unused")
-public class GenericEnumUserType implements EnhancedUserType, ParameterizedType
+public class GenericEnumUserType implements EnhancedUserType, ParameterizedType, Serializable
 {
     private static final String DEFAULT_IDENTIFIER_METHOD_NAME = "getId";
     private static final String DEFAULT_VALUE_OF_METHOD_NAME = "fromId";
         return enumClass;
     }
 
-    @SuppressWarnings("unchecked")
     public void setParameterValues(Properties parameters)
     {
         String enumClassName = parameters.getProperty("enumClass");
+        String identifierMethodName = parameters.getProperty("identifierMethod", DEFAULT_IDENTIFIER_METHOD_NAME);
+        String valueOfMethodName = parameters.getProperty("valueOfMethod", DEFAULT_VALUE_OF_METHOD_NAME);
+
+        initialize(enumClassName, identifierMethodName, valueOfMethodName);
+    }
+
+    public int[] sqlTypes()
+    {
+        return sqlTypes;
+    }
+
+    public String toXMLString(Object value)
+    {
+        return ((Enum<?>) value).name();
+    }
+
+    @SuppressWarnings("unchecked")
+    private void initialize(String enumClassName, String identifierMethodName, String valueOfMethodName)
+    {
         try
         {
             enumClass = Class.forName(enumClassName).asSubclass(Enum.class);
             throw new HibernateException("Enum class not found", exception);
         }
 
-        String identifierMethodName = parameters.getProperty("identifierMethod", DEFAULT_IDENTIFIER_METHOD_NAME);
         try
         {
             identifierMethod = enumClass.getMethod(identifierMethodName, NULL_CLASS_VARARG);
         }
         Class<?> identifierType = identifierMethod.getReturnType();
 
-        String valueOfMethodName = parameters.getProperty("valueOfMethod", DEFAULT_VALUE_OF_METHOD_NAME);
         try
         {
             valueOfMethod = enumClass.getMethod(valueOfMethodName, identifierType);
         sqlTypes = new int[]{type.sqlType()};
     }
 
-    public int[] sqlTypes()
+    /**
+     * Prevents attempts to deserialize the {@code GenericEnumUserType} directly, as {@link SerializationProxy}
+     * should have been written in its place.
+     *
+     * @param stream ignored
+     */
+    private void readObject(ObjectInputStream stream)
     {
-        return sqlTypes;
+        throw new UnsupportedOperationException(getClass().getName() + " cannot be deserialized directly");
     }
 
-    public String toXMLString(Object value)
+    /**
+     * Replaces this {@code GenericEnumUserType} (which is not really serializable due to the {@code Method} fields)
+     * with a simple {@link SerializationProxy} containing the names for the enum class, the identifier method and
+     * the valueOf method.
+     *
+     * @return a new {@link SerializationProxy} to be serialized in this type's place
+     */
+    private Object writeReplace()
     {
-        return ((Enum<?>) value).name();
+        return new SerializationProxy(enumClass.getName(), identifierMethod.getName(), valueOfMethod.getName());
+    }
+
+    /**
+     * Simple proxy to serialize in place of the more complicated {@link GenericEnumUserType}. This proxy contains
+     * all the data necessary to recreate/reinitialize the full {@link GenericEnumUserType}.
+     */
+    private static class SerializationProxy implements Serializable
+    {
+
+        private final String enumClassName;
+        private final String identifierMethodName;
+        private final String valueOfMethodName;
+
+        private SerializationProxy(String enumClassName, String identifierMethodName, String valueOfMethodName)
+        {
+            this.enumClassName = enumClassName;
+            this.identifierMethodName = identifierMethodName;
+            this.valueOfMethodName = valueOfMethodName;
+        }
+
+        private Object readResolve()
+        {
+            GenericEnumUserType type = new GenericEnumUserType();
+            type.initialize(enumClassName, identifierMethodName, valueOfMethodName);
+
+            return type;
+        }
     }
 }