David, for some reason it never registered that Chris sent me this email
in private; I should have responded here on list.
I prefer the interface approach.
-
Steve Ebersole
Project Lead
steve(a)hibernate.org
Principal Software Engineer
JBoss, a division of Red Hat
steve.ebersole(a)jboss.com
steve.ebersole(a)redhat.com
-------- Forwarded Message --------
From: Steve Ebersole <steve(a)hibernate.org>
To: Chris Webb <chris(a)hullomail.com>
Subject: Re: no support for retrieving a native generated identifier
using a custom user type
Date: Thu, 04 Dec 2008 16:30:12 -0600
The "more correct" solution is along the lines a contract to allow the
type to handle this like you said. I'm thinking something like an
optional interface for the type to publish that fact:
public interface ResultSetIdentifierConsumer {
public Serializable consumeIdentifier(ResultSet resultSet);
}
now IdentifierGeneratorFactory (called IdentifierGeneratorHelper now
btw) can say:
public static Serializable get(ResultSet rs, Type type) ... {
if ( type instanceof ResultSetIdentifierConsumer ) {
return ( ( ResultSetIdentifierConsumer )
type ).consumeIdentifier( rs );
}
Class clazz = type.getReturnedClass();
if ( clazz == Long.class ) {
return new Long( rs.getLong( 1 ) );
}
else if ( clazz == Integer.class ) {
return new Integer( rs.getInt( 1 ) );
}
else if ( clazz == Short.class ) {
return new Short( rs.getShort( 1 ) );
}
else if ( clazz == String.class ) {
return rs.getString( 1 );
}
else {
throw ...
}
}
and you can define a type :
public class VxsContactIdLongType ... implements
ResultSetIdentifierConsumer ... {
public Serializable consumeIdentifier(ResultSet resultSet) {
return new VxsContactIdLong( resultSet.getLong( 1 ) );
}
...
}
--
Steve Ebersole
Project Lead
http://hibernate.org
steve(a)hibernate.org
Principal Software Engineer
JBoss, a division of Red Hat
http://jboss.com
http://redhat.com
steve.ebersole(a)jboss.com
steve.ebersole(a)redhat.com
On Thu, 2008-11-27 at 01:42 +0000, Chris Webb wrote:
> Steve,
>
> I've come up with a generic solution that makes the slight assumption
> that any custom user type identifier that supports native generation
> provides an appropriate constructor to take in the returned result set
> field. The constructor is called using reflection in
> org.hibernate.id.IdentifierGeneratorFactory.get(ResultSet rs, Type
> type) as follows:
>
> public static Serializable get(ResultSet rs, Type type) throws
> SQLException, IdentifierGenerationException {
> Class clazz = type.getReturnedClass();
> if ( clazz == Long.class ) {
> return new Long( rs.getLong( 1 ) );
> }
> else if ( clazz == Integer.class ) {
> return new Integer( rs.getInt( 1 ) );
> }
> else if ( clazz == Short.class ) {
> return new Short( rs.getShort( 1 ) );
> }
> else if ( clazz == String.class ) {
> return rs.getString( 1 );
> }
> else {
> // >>>>>> START NEW LOGIC
>
> // Now check to see if the returned class wraps the
> returned result
> // set field. Assumes the returned class defines an
> appropriate
> // public constructor.
> // TODO: Rather then assuming an appropriate constructor
> and using
> // reflection maybe the type should define a method that
> takes a
> // result set and returns the appropriate identifier type.
>
> Object identifierValue = null;
>
> try {
> try {
> Constructor<Serializable> constructor =
> clazz.getConstructor(new Class[] { long.class});
> identifierValue = new Long(rs.getLong(1));
> return constructor.newInstance(new Object[]
> { identifierValue });
> } catch (NoSuchMethodException e) {
> try {
> Constructor<Serializable> constructor =
> clazz.getConstructor(new Class[] { int.class});
> identifierValue = new Integer(rs.getInt(1));
> return constructor.newInstance(new Object[]
> { identifierValue });
> } catch (NoSuchMethodException e1) {
> try {
> Constructor<Serializable> constructor =
> clazz.getConstructor(new Class[] { short.class});
> identifierValue = new
> Short(rs.getShort(1));
> return constructor.newInstance(new
> Object[] { identifierValue });
> } catch (NoSuchMethodException e2) {
> try {
> Constructor<Serializable> constructor
> = clazz.getConstructor(new Class[] { String.class});
> identifierValue = rs.getString(1);
> return constructor.newInstance(new
> Object[] { identifierValue });
> } catch (NoSuchMethodException e3) {
> // Do nothing.
> }
> }
> }
> }
> } catch (SecurityException e) {
> throw new IdentifierGenerationException("Failed to
> instantiate identifier class '" + clazz + "' with identifier value
'"
> + identifierValue + "'", e);
> } catch (IllegalArgumentException e) {
> throw new IdentifierGenerationException("Failed to
> instantiate identifier class '" + clazz + "' with identifier value
'"
> + identifierValue + "'", e);
> } catch (InstantiationException e) {
> throw new IdentifierGenerationException("Failed to
> instantiate identifier class '" + clazz + "' with identifier value
'"
> + identifierValue + "'", e);
> } catch (IllegalAccessException e) {
> throw new IdentifierGenerationException("Failed to
> instantiate identifier class '" + clazz + "' with identifier value
'"
> + identifierValue + "'", e);
> } catch (InvocationTargetException e) {
> throw new IdentifierGenerationException("Failed to
> instantiate identifier class '" + clazz + "' with identifier value
'"
> + identifierValue + "'", e);
> }
>
> // <<<<<< END NEW LOGIC
>
> throw new IdentifierGenerationException( "this id
> generator generates long, integer, short or string or classes that
> wrap those types" );
> }
>
> }
>
> I've left a comment in HB-92 to request the issue to be reopened and
> the change described applied. Is the assumption I'm making acceptable?
> Looking forward to hearing your comments.
>
> cheers,
> chris
>
>
> On Tue, Nov 11, 2008 at 3:52 PM, Chris Webb <chris(a)hullomail.com>
> wrote:
> Steve,
>
> I hope you don't mind me approaching you directly with regard
> to an issue I have. Please direct me to a more appropriate
> channel if necessary.
>
> Back on the 21/05/03 I raised an issue (HB-92) to do with no
> support for retrieving a native generated identifier using a
> custom user type identifier. In a nutshell I had a custom user
> type that wrapped a long and for all intensive acted like a
> long but would not work with retrieving the native
> auto-increment identifier because it wasn't exactly the Long
> class. In Feb 2005 Gavin provided a solution regarding using a
> PostInsertIdentifierGenerator but I didn't quite understand
> how to utilise this. Now in 2008 I am revisiting this problem
> and the issue seems to still exist with the original config.
>
> <class name="VxsContact" table="VXS_CONTACT">
>
> <id name="id" column="CONTACT_ID"
>
type="com.voxsurf.pim.contacts.persistence.hibernate.VxsContactIdLongType">
> <generator class="native"/>
> </id>
> .
> .
> .
> </class>
>
> The Hibernate code has moved on quite a bit since 2003 but
> looking at the current GA release (v3.3.1) the problem seems
> to be in
> org.hibernate.id.IdentifierGeneratorFactory.get(ResultSet rs,
> Type type). Despite the 'type' being passed into this method
> the fact it may be a custom type is ignored and only types
> that return Long, Integer, Short or String classes are
> accepted.
>
> I have managed to hack the code to hardcode creating my custom
> type if the relevant type is passed in to this method and this
> appears to work.
>
> // unhappy about this being public ... is there a better
> way?
> public static Serializable get(ResultSet rs, Type type)
> throws SQLException, IdentifierGenerationException {
> // NOTE: This is a hack to return a specifc custom
> type identifier.
> if
> (type.getReturnedClass().equals(VxsContactIdLong.class)) {
> return new VxsContactIdLong(rs.getLong( 1 ));
> }
>
> Class clazz = type.getReturnedClass();
> if ( clazz == Long.class ) {
> return new Long( rs.getLong( 1 ) );
> }
> else if ( clazz == Integer.class ) {
> return new Integer( rs.getInt( 1 ) );
> }
> else if ( clazz == Short.class ) {
> return new Short( rs.getShort( 1 ) );
> }
> else if ( clazz == String.class ) {
> return rs.getString( 1 );
> }
> else {
> throw new IdentifierGenerationException( "this id
> generator generates long, integer, short or string" );
> }
>
> }
>
> So the question is, could the 'type' passed in be used to
> create the appropriate identifer type rather than only
> supporting a fixed set of basic types. Looking at the TRUNK it
> appears you have been making further changes to this area and
> maybe this functionality has already been enabled.
>
> I have attached relevant classes. Let me know if you need any
> further information.
>
> Look forward to hearing from you.
>
> Best regards,
> Chris
>
> --
> Voicemail that's yours to keep!
> Sign up @
www.hullomail.com
>
> Chris Webb
> CTO
> HulloMail Ltd
> e) chris.webb(a)hullomail.com
> m) +44 77 8639 2359