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 st...@hibernate.org Principal Software Engineer JBoss, a division of Red Hat http://jboss.com http://redhat.com steve.ebers...@jboss.com steve.ebers...@redhat.com -------- Forwarded Message -------- > From: Steve Ebersole <st...@hibernate.org> > To: Chris Webb <ch...@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 > st...@hibernate.org > > Principal Software Engineer > JBoss, a division of Red Hat > http://jboss.com > http://redhat.com > steve.ebers...@jboss.com > steve.ebers...@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 <ch...@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.w...@hullomail.com > > m) +44 77 8639 2359 _______________________________________________ hibernate-dev mailing list hibernate-dev@lists.jboss.org https://lists.jboss.org/mailman/listinfo/hibernate-dev