[hibernate-issues] [Hibernate-JIRA] Commented: (HHH-6572) IdentifierGeneratorHelper.getGeneratedIdentity() assumes that id column will always be at index 1

Richard Ballard (JIRA) noreply at atlassian.com
Mon Apr 9 22:06:48 EDT 2012


    [ https://hibernate.onjira.com/browse/HHH-6572?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=46209#comment-46209 ] 

Richard Ballard commented on HHH-6572:
--------------------------------------

This should be re-tested.  I have just upgraded my hibernate version to 4.1.2 and the problem seems to have gone (i.e. I don't need my workaround anymore).  Previous version (that was broken) was 4.0.1 Final, working version is:

<dependency>
  <groupId>org.hibernate</groupId>
  <artifactId>hibernate-entitymanager</artifactId>
  <version>4.1.2.Final</version>
</dependency>

> IdentifierGeneratorHelper.getGeneratedIdentity() assumes that id column will always be at index 1
> -------------------------------------------------------------------------------------------------
>
>                 Key: HHH-6572
>                 URL: https://hibernate.onjira.com/browse/HHH-6572
>             Project: Hibernate ORM
>          Issue Type: Bug
>          Components: core
>    Affects Versions: 3.6.3
>         Environment: Using Hibernate core 3.6.3 against Postgres driver 9.0-801.jdbc4 (underlying postgres version 9.0)
>            Reporter: Richard Ballard
>
> If the database columns are created such that the serial id is *not* the first one, then the EntityManager.persist(Object) method sets the wrong value on the entity's @Id field (it sets it to the value of the first db column).  For example:
> Create the table:
> CREATE TABLE info.rmbtest_course2
> (
>   fee integer,
>   id bigserial NOT NULL,
>   starttime timestamp without time zone,
>   title character varying(100) NOT NULL,
>   CONSTRAINT rmbtest_course2_pkey PRIMARY KEY (id)
> )
> *Note that the id column is the second column*.
> Create the entity:
> package testhibernate.course;
> import org.hibernate.annotations.GenericGenerator;
> import org.hibernate.annotations.Type;
> import org.joda.time.DateTime;
> import javax.persistence.Column;
> import javax.persistence.Entity;
> import javax.persistence.GeneratedValue;
> import javax.persistence.GenerationType;
> import javax.persistence.Id;
> import javax.persistence.IdClass;
> import javax.persistence.NamedQueries;
> import javax.persistence.NamedQuery;
> import javax.persistence.Table;
> @Entity(name = "course")
> @Table(name = "rmbTest_course2", schema = "info")
> @NamedQueries(@NamedQuery(name = "Course.findByTest", query = "from course"))
> public class Course {
>     @Id
>     @GeneratedValue(strategy = GenerationType.IDENTITY)
>     @Column(name = "ID")
>     private final Long id;
>     @Column(name = "TITLE", length = 100, nullable = false)
>     private final String title;
>     @Column(name = "FEE")
>     private final int fee;
>     @Column(name = "startTime")
>     @Type(type="org.jadira.usertype.dateandtime.joda.PersistentDateTime")
>     private final DateTime startTime;
>     public Course(final String title, final int fee, final DateTime startTime) {
>         this.id = null;
>         this.title = title;
>         this.fee = fee;
>         this.startTime = startTime;
>     }
>     /**
>      * Required by JPA
>      */
>     public Course() {
>         id = null;
>         title = null;
>         fee = 0;
>         startTime = null;
>     }
>     public CourseKey getKey() {
>         return new CourseKey(id);
>     }
>     public String getTitle() {
>         return title;
>     }
>     public int getFee() {
>         return fee;
>     }
>     public DateTime getStartTime() {
>         return startTime;
>     }
>     @Override
>     public String toString()
>     {
>         return "Course{" +
>                 "id=" + id +
>                 ", title='" + title + '\'' +
>                 ", fee=" + fee +
>                 ", startTime=" + startTime +
>                 '}';
>     }
> }
> Run this code:
>         Course course = new Course("Core Spring", 1000, new DateTime());
>         course = myRepository.save(course);
>         System.out.println("key = " + course.getKey());
> In this case the returned course.getKey() should've been the auto allocated serial id, but it is 1000, i.e. the first column in the table.
> The problem is that IdentifierGeneratorHelper.get(ResultSet rs, Type type) assumes that the id column is always the first column.
> As a workaround I have set my entities @Id annotations to:
> ...
> public class Course {
>     @Id
>     @GeneratedValue(generator = "myGenerator")
>     @GenericGenerator(name = "myGenerator", strategy = "testhibernate.MyGenerator")
>     @Column(name = "ID")
>     private final Long id;
> ...
> and had the following Generator code:
> package testhibernate;
> import org.hibernate.HibernateException;
> import org.hibernate.dialect.Dialect;
> import org.hibernate.id.IdentifierGenerationException;
> import org.hibernate.id.IdentifierGeneratorHelper;
> import org.hibernate.id.IdentityGenerator;
> import org.hibernate.id.PostInsertIdentityPersister;
> import org.hibernate.id.ResultSetIdentifierConsumer;
> import org.hibernate.id.insert.InsertGeneratedIdentifierDelegate;
> import org.hibernate.persister.entity.SingleTableEntityPersister;
> import org.hibernate.type.CustomType;
> import org.hibernate.type.Type;
> import java.io.Serializable;
> import java.math.BigDecimal;
> import java.math.BigInteger;
> import java.sql.PreparedStatement;
> import java.sql.ResultSet;
> import java.sql.SQLException;
> public class MyGenerator extends IdentityGenerator
> {
>     @Override
>     public InsertGeneratedIdentifierDelegate getInsertGeneratedIdentifierDelegate(final PostInsertIdentityPersister persister,
>                                                                                   final Dialect dialect,
>                                                                                   final boolean isGetGeneratedKeysEnabled) throws HibernateException
>     {
>         final InsertGeneratedIdentifierDelegate result;
>         if(isGetGeneratedKeysEnabled)
>         {
>             result = new MyGetGeneratedKeysDelegate(persister, dialect);
>         }
>         else
>         {
>             result = super.getInsertGeneratedIdentifierDelegate(persister, dialect, isGetGeneratedKeysEnabled);
>         }
>         return result;
>     }
>     private static class MyGetGeneratedKeysDelegate extends GetGeneratedKeysDelegate
>     {
>         private final PostInsertIdentityPersister persister;
>         private MyGetGeneratedKeysDelegate(final PostInsertIdentityPersister persister, final Dialect dialect)
>         {
>             super(persister, dialect);
>             assert persister != null;
>             this.persister = persister;
>         }
>         private Serializable getGeneratedIdentityByColumnName(ResultSet rs,
>                                                               Type type,
>                                                               String columnName) throws SQLException, HibernateException {
>             if ( !rs.next() ) {
>                 throw new HibernateException( "The database returned no natively generated identity value" );
>             }
>             final Serializable id = get(rs, type, columnName);
>             // todo log.debug( "Natively generated identity: " + id );
>             return id;
>         }
>         private Serializable get(ResultSet rs, Type type, String columnName) throws SQLException, IdentifierGenerationException
>         {
>             if ( ResultSetIdentifierConsumer.class.isInstance( type ) )
>             {
>                 return ( ( ResultSetIdentifierConsumer ) type ).consumeIdentifier( rs );
>             }
>             if ( CustomType.class.isInstance( type ) )
>             {
>                 final CustomType customType = (CustomType) type;
>                 if ( ResultSetIdentifierConsumer.class.isInstance( customType.getUserType() ) ) {
>                     return ( (ResultSetIdentifierConsumer) customType.getUserType() ).consumeIdentifier( rs );
>                 }
>             }
>             Class<?> clazz = type.getReturnedClass();
>             if ( clazz == Long.class )
>             {
>                 return rs.getLong(columnName);
>             }
>             else if ( clazz == Integer.class )
>             {
>                 return rs.getInt(columnName);
>             }
>             else if ( clazz == Short.class )
>             {
>                 return rs.getShort(columnName);
>             }
>             else if ( clazz == String.class )
>             {
>                 return rs.getString( columnName );
>             }
>             else if ( clazz == BigInteger.class )
>             {
>                 return rs.getBigDecimal( columnName ).setScale( 0, BigDecimal.ROUND_UNNECESSARY ).toBigInteger();
>             }
>             else if ( clazz == BigDecimal.class )
>             {
>                 return rs.getBigDecimal( columnName ).setScale( 0, BigDecimal.ROUND_UNNECESSARY );
>             }
>             else
>             {
>                 throw new IdentifierGenerationException("unrecognised id type : " + type.getName() + " -> " + clazz.getName());
>             }
>         }
>         @Override
>         public Serializable executeAndExtract(final PreparedStatement insert) throws SQLException
>         {
>             insert.executeUpdate();
>             ResultSet rs = insert.getGeneratedKeys();
>             try
>             {
>                 final Type identifierType = persister.getIdentifierType();
>                 Serializable result = null;
>                 boolean useDefaultTechnique = true;
>                 if(persister instanceof SingleTableEntityPersister)
>                 {
>                     final String[] idColumnNames = ((SingleTableEntityPersister)persister).getIdentifierColumnNames();
>                     if(idColumnNames.length == 1)
>                     {
>                         // do it by column name
>                         result = getGeneratedIdentityByColumnName(rs, identifierType, idColumnNames[0] );
>                         useDefaultTechnique = false;
>                     }
>                     else
>                     {
>                         // todo - log
>                     }
>                 }
>                 else
>                 {
>                     // todo - log
>                 }
>                 if(useDefaultTechnique)
>                 {
>                     result = IdentifierGeneratorHelper.getGeneratedIdentity(rs, identifierType);
>                 }
>                 return result;
>             }
>             finally
>             {
>                 rs.close();
>             }
>         }
>     }
> }
> This seems to do the trick but is obviously an ugly hack.

--
This message is automatically generated by JIRA.
For more information on JIRA, see: http://www.atlassian.com/software/jira

        


More information about the hibernate-issues mailing list