[hibernate-dev] [Fwd: Re: no support for retrieving a native generated identifier using a custom user type]

Steve Ebersole steve at hibernate.org
Mon Dec 22 10:48:35 EST 2008


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
http://hibernate.org
steve at hibernate.org

Principal Software Engineer
JBoss, a division of Red Hat
http://jboss.com
http://redhat.com
steve.ebersole at jboss.com
steve.ebersole at redhat.com


-------- Forwarded Message --------
> From: Steve Ebersole <steve at hibernate.org>
> To: Chris Webb <chris at 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 at hibernate.org
> 
> Principal Software Engineer
> JBoss, a division of Red Hat
> http://jboss.com
> http://redhat.com
> steve.ebersole at jboss.com
> steve.ebersole at 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 at 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 at hullomail.com
> > m) +44 77 8639 2359




More information about the hibernate-dev mailing list