-->
These old forums are deprecated now and set to read-only. We are waiting for you on our new forums!
More modern, Discourse-based and with GitHub/Google/Twitter authentication built-in.

All times are UTC - 5 hours [ DST ]



Forum locked This topic is locked, you cannot edit posts or make further replies.  [ 9 posts ] 
Author Message
 Post subject: Problems rolling back session.save() with transient object.
PostPosted: Thu Jan 29, 2004 4:52 pm 
Newbie

Joined: Thu Jan 29, 2004 2:51 pm
Posts: 9
I am attempting to save a transient object and when the operation fails the transient object's id is set (this is bad) but the record is not inserted into the database, as expected.

I have scoured the forums and FAQ's and I can't find a definitive answer to this issue. I'm using Hibernate version 2.0.1 17 June 2003.

This is what I'm doing (and I'll follow it up with a code snippet):

1) Create a new instance of my object to save and set attributes. At this point the object.getId() == null, as it should.
2) Get Hibernate Session, get/start Hibernate Transaction.
3) session.save() the object (this does NOT throw an exception).
4) Tx.commit() (this DOES throw an exception)
5) Cleanup session/transaction objects.

At this point the database shows that the object's attributes were not persisted as I expected. However the Object's identifier is now not null, it contains a valid value, like 22. When I modify the object's attributes and attempt to save it again, it fails and Hibernate something like "No row found where id: 22".

I'm trying to understand the semantics of session.save(), session.flush() and tx.commit(). There are numerous postings on this, and from this I understand that session.save() sets up the SQL query (AND get's the ID for the object if it's null!), session.flush() sends the command to the database, and tx.commit()'s the statement.

Here is the code, I'll try to provide only useful snippets and not 6 pages of debug statements.

Any help would be greatly appreciated!!!

Block of code:
Code:
      System.err.println("id is: " + hibernateObject.getId());

      Session session = null;
      Transaction tx = null;
      boolean didCommit = false;
      try {
         session = SessionFactory.openSession();
         tx = session.beginTransaction();

         session.save(hibernateObject);
         System.err.println("logic succeeded.");

         tx.commit(); // this flushes the session.

         System.err.println("committed transaction.");
         didCommit = true;
      }
      catch (HibernateException he) {
         System.err.println("save exception: " + he.getMessage() );
         if (tx != null) {
            System.err.println("rolling back transaction...");
            tx.rollback();
         }
         throw he;
      }
      finally {
         System.err.println("did commit? " + didCommit);
         if (session != null) {
            session.close();
         }
      }

      System.err.println("id is: " + hibernateObject.getId());

What gets printed out from above:
Code:
id is: null
logic succeeded.
save exception:  Could not synchronize database state with session: ORA-02291: integrity constraint (blah blah blah)
rolling back transaction...
did commit?  false
id is:  22


I designed the test to fail by the way. Actually this was a unittest I wrote. When I try to save the object again, I get the "No row found for id 22" error message.

My hbm.xml file...
Code:
   <class
      name="com.qrs.itm.dao.admin.organization.Organization"
      table="QI_ORGANIZATION"
      dynamic-update="false"
      dynamic-insert="false"
      >
      <id
         name="id"
         column="ORGANIZATION_ID"
         type="com.qrs.itm.dao.admin.organization.OrganizationIDType"
         unsaved-value="null"
         >
         <generator class="com.qrs.itm.dao.admin.organization.OrganizationHiLoGenerator">
            <param name="table">QI_SEQUENCES</param>
            <param name="column">ORGANIZATION</param>
            <param name="max_lo">20</param>
         </generator>
      </id>
      <property
         name="name"
         type="java.lang.String"
         update="true"
         insert="true"
         column="ORGANIZATION_NAME"
         length="64"
         />


I'm using custom types and my custom HiLoGenerator just simply uses Hibernate's TableHiLoGenerator.

I'll provide relevant snippets of the hibernate.log debug file:
Code:
INFO  net.sf.hibernate.cfg.Environment  - Hibernate 2.0.1
INFO  net.sf.hibernate.cfg.Environment  - loaded properties from resource hibernate.properties: {hibernate.connection.driver_class=oracle.jdbc.driver.OracleDriver, hibernate.cglib.use_reflection_optimizer=true, hibernate.dialect=net.sf.hibernate.dialect.Oracle9Dialect, hibernate.jdbc.use_streams_for_binary=true, hibernate.jdbc.batch_size=0, hibernate.connection.username=SGarcia_itm1, hibernate.connection.url=jdbc:oracle:thin:@RCAITMD01:1521:ITMD01, hibernate.show_sql=false, hibernate.connection.password=SGarcia_itm1, hibernate.connection.pool_size=10, hibernate.statement_cache.size=0}
INFO  net.sf.hibernate.cfg.Environment  - using java.io streams to persist binary types
INFO  net.sf.hibernate.cfg.Environment  - using CGLIB reflection optimizer
INFO  net.sf.hibernate.cfg.Environment  - JVM proxy support: true
INFO  net.sf.hibernate.cfg.Configuration  - Mapping resource: com/qrs/itm/dao/admin/organization/Organization.hbm.xml
DEBUG net.sf.hibernate.util.DTDEntityResolver  - trying to locate http://hibernate.sourceforge.net/hibernate-mapping-2.0.dtd in classpath under net/sf/hibernate/
DEBUG net.sf.hibernate.util.DTDEntityResolver  - found http://hibernate.sourceforge.net/hibernate-mapping-2.0.dtd in classpath
INFO  net.sf.hibernate.cfg.Binder  - Mapping class: com.qrs.itm.dao.admin.organization.Organization -> QI_ORGANIZATION
DEBUG net.sf.hibernate.cfg.Binder  - Mapped property: id -> ORGANIZATION_ID, type: com.qrs.itm.dao.admin.organization.OrganizationIDType
DEBUG net.sf.hibernate.cfg.Binder  - Mapped property: name -> ORGANIZATION_NAME, type: string
DEBUG net.sf.hibernate.cfg.Binder  - Mapped property: shortName -> SHORT_NAME, type: string
[snip]
INFO  net.sf.hibernate.impl.SessionFactoryImpl  - building session factory
DEBUG net.sf.hibernate.impl.SessionFactoryImpl  - instantiating session factory with properties: {hibernate.connection.driver_class=oracle.jdbc.driver.OracleDriver, hibernate.dialect=net.sf.hibernate.dialect.Oracle9Dialect, hibernate.jdbc.use_streams_for_binary=true, hibernate.jdbc.batch_size=0, hibernate.connection.username=SGarcia_itm1, hibernate.connection.url=jdbc:oracle:thin:@RCAITMD01:1521:ITMD01, hibernate.show_sql=false, hibernate.connection.password=SGarcia_itm1, hibernate.statement_cache.size=0, hibernate.connection.pool_size=10}
INFO  net.sf.hibernate.dialect.Dialect  - Using dialect: net.sf.hibernate.dialect.Oracle9Dialect
INFO  net.sf.hibernate.connection.DriverManagerConnectionProvider  - Hibernate connection pool size: 10
INFO  net.sf.hibernate.connection.DriverManagerConnectionProvider  - using driver: oracle.jdbc.driver.OracleDriver at URL: jdbc:oracle:thin:@RCAITMD01:1521:ITMD01
INFO  net.sf.hibernate.connection.DriverManagerConnectionProvider  - connection properties: {user=SGarcia_itm1, password=SGarcia_itm1}
INFO  net.sf.hibernate.impl.SessionFactoryImpl  - Use outer join fetching: true
DEBUG net.sf.hibernate.connection.DriverManagerConnectionProvider  - total checked-out connections: 0
DEBUG net.sf.hibernate.connection.DriverManagerConnectionProvider  - opening new JDBC connection
DEBUG net.sf.hibernate.connection.DriverManagerConnectionProvider  - created connection to: jdbc:oracle:thin:@RCAITMD01:1521:ITMD01, Isolation Level: 2
DEBUG net.sf.hibernate.connection.DriverManagerConnectionProvider  - returning connection to pool, pool size: 1
INFO  net.sf.hibernate.impl.SessionFactoryImpl  - Use scrollable result sets: true
DEBUG net.sf.hibernate.impl.SessionFactoryObjectFactory  - initializing class SessionFactoryObjectFactory
DEBUG net.sf.hibernate.impl.SessionFactoryObjectFactory  - registered: 2c90829anullfa62db51null00fanull62db5972null0000 (unnamed)
INFO  net.sf.hibernate.impl.SessionFactoryObjectFactory  - no JDNI name configured
INFO  net.sf.hibernate.impl.SessionFactoryImpl  - Query language substitutions: {}
DEBUG net.sf.hibernate.impl.SessionFactoryImpl  - instantiated session factory
DEBUG net.sf.hibernate.impl.SessionImpl  - opened session
DEBUG net.sf.hibernate.transaction.JDBCTransaction  - begin
DEBUG net.sf.hibernate.connection.DriverManagerConnectionProvider  - total checked-out connections: 0
DEBUG net.sf.hibernate.connection.DriverManagerConnectionProvider  - using pooled JDBC connection, pool size: 0
DEBUG net.sf.hibernate.connection.DriverManagerConnectionProvider  - total checked-out connections: 1
DEBUG net.sf.hibernate.connection.DriverManagerConnectionProvider  - opening new JDBC connection
DEBUG net.sf.hibernate.connection.DriverManagerConnectionProvider  - created connection to: jdbc:oracle:thin:@RCAITMD01:1521:ITMD01, Isolation Level: 2
DEBUG net.sf.hibernate.connection.DriverManagerConnectionProvider  - returning connection to pool, pool size: 1
DEBUG net.sf.hibernate.id.TableHiLoGenerator  - new hi value: 1
DEBUG net.sf.hibernate.impl.SessionImpl  - saving [com.qrs.itm.dao.admin.organization.Organization#22]
DEBUG net.sf.hibernate.transaction.JDBCTransaction  - commit
DEBUG net.sf.hibernate.impl.SessionImpl  - flushing session
DEBUG net.sf.hibernate.impl.SessionImpl  - Flushing entities and processing referenced collections
DEBUG net.sf.hibernate.impl.SessionImpl  - Processing unreferenced collections
DEBUG net.sf.hibernate.impl.SessionImpl  - Scheduling collection removes/(re)creates/updates
DEBUG net.sf.hibernate.impl.SessionImpl  - Flushed: 1 insertions, 0 updates, 0 deletions to 1 objects
DEBUG net.sf.hibernate.impl.SessionImpl  - Flushed: 0 (re)creations, 0 updates, 0 removals to 0 collections
DEBUG net.sf.hibernate.impl.SessionImpl  - executing flush
DEBUG net.sf.hibernate.persister.EntityPersister  - Inserting entity: com.qrs.itm.dao.admin.organization.Organization#22
DEBUG net.sf.hibernate.impl.BatcherImpl  - about to open: 0 open PreparedStatements, 0 open ResultSets
DEBUG net.sf.hibernate.impl.SessionFactoryImpl  - prepared statement get: insert into QI_ORGANIZATION (ORGANIZATION_NAME, SHORT_NAME, GLN, IS_QRS_CUSTOMER, PARENT_ORGANIZATION_ID, QRS_ACCT_ID, URL, PRIMARY_CONTACT_ID, ORGANIZATION_ID) values (?, ?, ?, ?, ?, ?, ?, ?, ?)
DEBUG net.sf.hibernate.impl.SessionFactoryImpl  - preparing statement
DEBUG net.sf.hibernate.persister.EntityPersister  - Dehydrating entity: com.qrs.itm.dao.admin.organization.Organization#22
DEBUG net.sf.hibernate.type.StringType  - binding 'name' to parameter: 1
DEBUG net.sf.hibernate.type.StringType  - binding null to parameter: 2
DEBUG net.sf.hibernate.type.StringType  - binding null to parameter: 3
DEBUG net.sf.hibernate.type.IntegerType  - binding '1' to parameter: 4
DEBUG net.sf.hibernate.type.StringType  - binding 'ext acct' to parameter: 6
DEBUG net.sf.hibernate.type.StringType  - binding null to parameter: 7
DEBUG net.sf.hibernate.util.JDBCExceptionReporter  - SQL Exception
java.sql.SQLException: ORA-02291: integrity constraint (SGARCIA_ITM1.QI_ORGANIZATION_FK_1) violated - parent key not found

   at oracle.jdbc.dbaccess.DBError.throwSqlException(DBError.java:168)
   at oracle.jdbc.ttc7.TTIoer.processError(TTIoer.java:208)
   [snip]
DEBUG net.sf.hibernate.impl.SessionImpl  - transaction completion
DEBUG net.sf.hibernate.transaction.JDBCTransaction  - rollback
DEBUG net.sf.hibernate.impl.SessionImpl  - transaction completion
DEBUG net.sf.hibernate.impl.SessionImpl  - closing session
DEBUG net.sf.hibernate.impl.SessionImpl  - disconnecting session
DEBUG net.sf.hibernate.connection.DriverManagerConnectionProvider  - returning connection to pool, pool size: 2
DEBUG net.sf.hibernate.impl.SessionImpl  - transaction completion


Top
 Profile  
 
 Post subject:
PostPosted: Thu Jan 29, 2004 5:05 pm 
Expert
Expert

Joined: Thu Jan 08, 2004 6:17 pm
Posts: 278
Looks like Hibernate's setting of the object's ID is not itself linked to the transaction context.

It's a gray area in general. If you set some other field of your object (calling any arbitrary setter), and then the transaction failed, would you expect that other field to be reverted?

Hibernate doesn't try to take all Java object creation/update and make it transactional.

Is there any way to use an interceptor or other Hibernate construct to catch rollbacks and undo selected Java object settings?

Cheers!
Rob


Top
 Profile  
 
 Post subject:
PostPosted: Thu Jan 29, 2004 5:16 pm 
Hibernate Team
Hibernate Team

Joined: Tue Aug 26, 2003 12:50 pm
Posts: 5130
Location: Melbourne, Australia
Hibernate does not implement object-level rollback. It is actually a performance killer and people do not usually need it.

I intend for it to be an option in a future version of Hibernate.


Top
 Profile  
 
 Post subject:
PostPosted: Thu Jan 29, 2004 5:56 pm 
Newbie

Joined: Thu Jan 29, 2004 2:51 pm
Posts: 9
It certainly makes sense that it is a performance killer. In the meantime do you recommend a paradigm that users can implement? The simpliest I can think of is

- if upon insert an exception is thrown, set the <id> of the hibernateObject back to "unsaved-value" (which for our DAOs are always null.)

The better way to do this would be before session.save(), clone the object, and if the insert fails upon session.save() return the cloned object. Cloning can be a really expensive operation and I guess that is what you mean by a "performance killer."

Thanks, Steve


Top
 Profile  
 
 Post subject:
PostPosted: Thu Jan 29, 2004 7:02 pm 
Hibernate Team
Hibernate Team

Joined: Tue Aug 26, 2003 12:50 pm
Posts: 5130
Location: Melbourne, Australia
Quote:
Cloning can be a really expensive operation and I guess that is what you mean by a "performance killer."


Exactly! ;)

But I don't understand why you want to keep working with same objects after a transaction rollback ... a rollback sould be a pretty "severe" error, shouldn't it?


Top
 Profile  
 
 Post subject:
PostPosted: Thu Jan 29, 2004 7:44 pm 
Newbie

Joined: Thu Jan 29, 2004 2:51 pm
Posts: 9
Quote:
But I don't understand why you want to keep working with same objects after a transaction rollback ... a rollback sould be a pretty "severe" error, shouldn't it?


Great point...here's the deal. I have a unittest for this object that tests all of the possible things that can go right and wrong with saving an object. So the behavior I saw was in a unittest and it turned out that we were trying to reuse the same object after it initially failed. Probably bad unittest design. It's possible that the negative test case is something that should never happen in a production system.

Let's say a user
1) fills in some fields in a web form and clicks submit (to insert the record)
2) it fails, yet you want to preserve what the user filled and display it back in a friendly way, i.e. ("you need to specify a real ID")
3) the user fixes it then clicks submit again and the record gets inserted.

Everything above occurs with the same object (like Java pointer equals).

Now I'm not really a UI guy but in most cases the HTML forms have a hidden field indicating what the object ID is. So in #1 above that would be null, in #2 it would be some set value like 22, and #3 the update would occur and the ID should be null but it's 22. That is what indicates whether a user is "adding" or "editing".

Now it's certainly possible that this is bad design but the object-level rollback is something that comes with home-baked JDBC data access layers I've written in the past. So this is something new that popped up and I was befuddled for about 6 hours trying to figure this out.

Also, can't there be situations that someone would want to recover from an exception in my case? Maybe you don't want to abandon the object itself for GC but instead want to rectify why the exception occured in the first place.

Not saying that Hibernate's way is good or bad it's just different and it took some time for me to figure out things.

Thanks again - Steve


Top
 Profile  
 
 Post subject:
PostPosted: Thu Jan 29, 2004 7:47 pm 
Newbie

Joined: Thu Jan 29, 2004 2:51 pm
Posts: 9
And...

As it turns out we are not leveraging Hibernate's object/relational mapping capabilities in the sense that we still look at our java objects as direct mappings to the relational data and not as objects themselves. That is, we still do

public class Person
public String getFirstName()
public String getLastName()
public AddressID getAddressID()

and not

public class Person
public String getFirstName()
public String getLastName()
public Address getAddress()

and let Hibernate take care of all the joins.

This is a completely different issue and I'm looking into ways that we can accomodate this in our app. This is Hibernate's core feature and we are not using it - and it's daft considering our application is not designed to be distributed.

(Even if it was I'm sure Hibernate works great as a replacement for (or integrates nicely with) CMP).


Top
 Profile  
 
 Post subject:
PostPosted: Thu Oct 13, 2005 6:46 pm 
Expert
Expert

Joined: Thu Jan 08, 2004 6:17 pm
Posts: 278
Here is a relevant thread (to which I am trying to unify many other threads on this topic):

Canonical "Can't Recover From Version Exception" Thread


Top
 Profile  
 
 Post subject:
PostPosted: Sat Jan 27, 2007 2:28 pm 
Beginner
Beginner

Joined: Sat Dec 16, 2006 1:52 pm
Posts: 40
sgarcia wrote:
And...

As it turns out we are not leveraging Hibernate's object/relational mapping capabilities in the sense that we still look at our java objects as direct mappings to the relational data and not as objects themselves. That is, we still do...



Just wanted to say thanks for that. I keep forgetting to treat my objects more like windows into the database. As such I dont need object rollback because in this case, my objects always hit the db for their info. They are not truly detached. I am only using the detached object for the sake of its ID.

I wonder if I would not be better off, actually using the ID that way I enforce this usage rather than have to keep remember that is the strategy...

Hmm, well actually I do need to roll back 'properties' which are loaded from the db and stored in my object such that when they are requested they are returned directly from my object rather than queried from the db. I can just eliminate that though and have ALL properties returned form db on each call. I have objects a layer up that will manage the caching of the info anyway.


Top
 Profile  
 
Display posts from previous:  Sort by  
Forum locked This topic is locked, you cannot edit posts or make further replies.  [ 9 posts ] 

All times are UTC - 5 hours [ DST ]


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum

Search for:
© Copyright 2014, Red Hat Inc. All rights reserved. JBoss and Hibernate are registered trademarks and servicemarks of Red Hat, Inc.