[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