Announcement Announcement Module
Collapse
No announcement yet.
Spring, Hibernate, PostgreSQL - id generator Page Title Module
Move Remove Collapse
X
Conversation Detail Module
Collapse
  • Filter
  • Time
  • Show
Clear All
new posts

  • Spring, Hibernate, PostgreSQL - id generator

    Problem Description
    ****************
    During a transaction that rollbacks, all NEW OBJECTs that had been persisted in it, mantain the Key that has been assigned to then by the GENERATOR. So, the next time i try to saveOrUpdate thats NEW OBJECTs, Hibernate manages its as if they where PERSISTED OBJECTS, generating a new exception.

    Ambient Details
    ************

    Application
    -------------
    Spring (release 1.0.2)
    Hibernate (2.1.0)
    Java (jdk 1.4.1_02-b06)

    Application Configuration
    -----------------------------
    * transactionManager: org.springframework.orm.hibernate.HibernateTransac tionManager
    * transactionInterceptor: org.springframework.transaction.interceptor.Transa ctionInterceptor
    * hibernate.dialect: net.sf.hibernate.dialect.PostgreSQLDialect
    * GENERATOR: "sequence"

    DB
    ----
    postgreSQL 7.4.3
    Linux Red Hat 9

    Analisys Realized and More Details
    ****************************

    Analisys Realized
    ---------------------
    1) PersonService.savePerson(IPerson person) is invoked by a client
    2) PersonDAO.savePerson(IPerson person) is then invoked by PersonService.savePerson(IPerson person)
    3) Before the inserts in the DB, the session uses GENERATOR to generate the unique Key (eg: 1421) that identifies the person.
    4) Suppose that, while inserting in the DB, a constrain is violated and an error is raise. The transactionManager inits the rollback process and finish it successfully.
    5) Unfortunately, after that process, the person mantains a relation to that generated Key (1421), but it does not exists in the DB after the rollback.
    6) So, the next time i try to saveOrUpdate that person, Hibernate manages its as if it where an already PERSISTED person, generating, calling the Session.doUpdate( ... ) method, generating an Hibernate exception.

    database
    -----------
    create table person
    (
    id serial,
    firstname varchar(20),
    lastname varchar(20),
    sex varchar(1) not null
    );

    person.hbm
    --------------
    <class name="Person" table="person">
    <id column="id" name="id" type="java.lang.Long">
    <generator class="sequence">
    <param name="sequence">elector_id_seq</param>
    </generator>
    </id>
    <property column="apellido" name="fistname" not-null="false" type="string" />
    <property column="apellido" name="lastname" not-null="false" type="string" />
    <property column="sex" name="lastname" not-null="true" type="string" />
    </class>

    conf_hibernate.xml
    -----------------------
    <bean id="transactionManager" class="org.springframework.orm.hibernate.Hibernate TransactionManager">
    <property name="sessionFactory">
    <ref bean="mySessionFactory"/>
    </property>
    </bean>

    <bean id="transactionInterceptor" class="org.springframework.transaction.interceptor .TransactionInterceptor">
    <property name="transactionManager">
    <ref bean="transactionManager"/>
    </property>
    <property name="transactionAttributeSource">
    <value>test.Interfaces.IPersonService.save*=PROPAG ATION_REQUIRED,-DataAccessException</value>
    </property>
    </bean>

    <bean id="person" class="test.BO.Person">
    <property name="sessionFactory">
    <ref bean="mySessionFactory"/>
    </property>
    </bean>

    <bean id="personDao" class="test.logica.PersonDAO">
    <property name="sessionFactory">
    <ref local="mySessionFactory"/>
    </property>
    </bean>

    <bean id="personServiceTarget" class="test.BO.PersonService">
    <property name="personDao">
    <ref bean="personDao"/>
    </property>
    </bean>

    <bean id="personService" class="org.springframework.aop.framework.ProxyFact oryBean">
    <property name="proxyInterfaces">
    <value>test.Interfaces.IPersonService</value>
    </property>
    <property name="interceptorNames">
    <value>transactionInterceptor,personServiceTarge t</value>
    </property>
    </bean>

    class
    -------
    PersonService class contains a savePerson(IPerson) methods that is intercepted for transactional reasons:

    public void savePerson(IPerson person) throws DataAccessException {
    this.personDao.savePerson(person);
    }

    PersonDAO class contains the next method:
    public void savePerson(IPerson person) throws DataAccessException {

    Session session = SessionFactoryUtils.getSession(getSessionFactory() , false);
    try{
    session.saveOrUpdate(person);
    }
    catch (...){
    ...
    }
    }

  • #2
    During a transaction that rollbacks, all NEW OBJECTs that had been persisted in it, mantain the Key that has been assigned to then by the GENERATOR.
    This is how Hibernate behaves. Transaction management does not rollback your transient objects to their original state. It is the developer responsibility to do this. You can also provide a "fresh" POJO each time you call save.

    Comment


    • #3
      Originally posted by irbouho
      During a transaction that rollbacks, all NEW OBJECTs that had been persisted in it, mantain the Key that has been assigned to then by the GENERATOR.
      This is how Hibernate behaves. Transaction management does not rollback your transient objects to their original state. It is the developer responsibility to do this. You can also provide a "fresh" POJO each time you call save.
      Omar:

      I'm not sure about that. If you use (for example) a the "identity" generator, all behaves as if you haven't persist that objects. Unfortunatelly, identities generators doesn't work with Postgres DB.

      See below the fragment of SessionImpl.doSave(Object ) method:
      .
      .
      .

      if (useIdentityColumn) {
      // if the id is generated by the database, we assign the key later
      key = null;
      }
      else {
      key = new Key(id, persister);
      .
      .
      .
      }

      In the last case, key isn't set to null during rollbacks, keeping the
      new Key(id, persister) value.

      Comment


      • #4
        luciano, there is a difference how Hibernate (and plain JDBC if you like) process identity and sequences:

        1. identity:
        1. hibernate execute an insert to create the new record
        2. the underlying rdbms get a new value for the identity
        3. the rdbms creates the table row
        4. hibernate execute a "select @@identity" (or equivalent) to get the identity value used
        5. hibernate sets the entity id using the returned value

        so if the insert statement fails (step 3), your entity id is not set yet

        1. sequence:
        1. hibernate execute a select query to get the sequence next value "select nextval(seq)" (or equivalent)
        2. hibernate sets the entity id using the returned value
        3. hibernate executes an insert to create the new record
        4. the rdbms creates the table row

        in this case, if step 4 fails, your entity id is already set.

        I hope this makes things a bit clear.

        Comment


        • #5
          Thank you Omar.

          One more thing, do you know some patch (implemented or in progress) that have similar behaviour to "identity" in PostgreSQL.

          Comment

          Working...
          X