[hibernate-issues] [Hibernate-JIRA] Commented: (HHH-6572) IdentifierGeneratorHelper.getGeneratedIdentity() assumes that id column will always be at index 1
Max Cheban (JIRA)
noreply at atlassian.com
Tue Dec 27 08:04:19 EST 2011
[ http://opensource.atlassian.com/projects/hibernate/browse/HHH-6572?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=44705#comment-44705 ]
Max Cheban commented on HHH-6572:
---------------------------------
It's not neccesary to retrieve the value from ResultSet by column name, you just need to provide proper RETURNING column order.
Method GetGeneratedKeysDelegate.prepare(String, SessionImplementor) should be rewritten as following:
protected PreparedStatement prepare(String insertSQL, SessionImplementor session) throws SQLException {
return session.getBatcher().prepareStatement( insertSQL, persister.getRootTableKeyColumnNames() );
}
> IdentifierGeneratorHelper.getGeneratedIdentity() assumes that id column will always be at index 1
> -------------------------------------------------------------------------------------------------
>
> Key: HHH-6572
> URL: http://opensource.atlassian.com/projects/hibernate/browse/HHH-6572
> Project: Hibernate Core
> 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