-->
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.  [ 8 posts ] 
Author Message
 Post subject: VERSIONING PROBLEM WITH OBJECT IN THE SAME SESSION
PostPosted: Wed Feb 22, 2006 3:44 am 
Newbie

Joined: Mon Feb 20, 2006 4:51 am
Posts: 3
Hello all,
I am using version field to ensure optimistic locking. It works just fine when the object is detached.
Suppose record A is opened by user X & Y at the same time. X changes A and updates the data. version field of A is incremented by hibernate. now when Y wants to change and update A with the older version, it fails. Thats my logic and it works just fine when A is not associated with the hibernate session. but when Y is loading A, changing it's version(to an older one) and trying to save it within the same hibernate session, it doesn't throw any exception. somehow version set by Y is overridden. But that should not be the expected behavior. it should throw exception whether A is related to session or not (detached/persistent) when it finds a version mismatch with the database. I have gone through some hibernate debugging. the version is taken from EntityEntry for entity associated with hibernate session. Thus any version property set by Y is also overridden and no exception is thrown. is that the way it should work?
I have seen people suggesting evict(A) before save/update. is there any other way? is this a bug / a feature? i have thousends of save/update call, i can't just add evect before each of them.
regards,
Sajid Moinuddin.


Top
 Profile  
 
 Post subject:
PostPosted: Wed Feb 22, 2006 4:03 am 
Newbie

Joined: Mon Feb 20, 2006 4:51 am
Posts: 3
and here is my db class. db objects with hibernate-doclate and annotation extends this class.
And i am using spring's getHibernateTemplate().saveOrUpdate(A);
<pre>
<code>
class A extends Persistent{
//..............
}

@EmbeddableSuperclass
public abstract class Persistent implements Serializable {

Date created;
Date updated;
long version;

/**
* @hibernate.property
* update="false"
* insert="true"
* type="timestamp"
*/
@Basic(temporalType = TemporalType.TIMESTAMP)
public Date getCreated() {
return created;
}
public void setCreated(Date d) {
created = d;
}

/**
* @hibernate.property
* update="true"
* insert="true"
* type="timestamp"
*/
@Basic(temporalType = TemporalType.TIMESTAMP)
public Date getUpdated() {
return updated;
}
public void setUpdated(Date d) {
updated = d;
}

/**
* @hibernate.version
* type="long"
*/
@Version
public long getVersion() {
return version;
}
public void setVersion(long v) {
version = v;
}
}

</code>
</pre>


Top
 Profile  
 
 Post subject:
PostPosted: Wed Feb 22, 2006 4:34 am 
Beginner
Beginner

Joined: Wed Sep 07, 2005 9:57 am
Posts: 20
Hi,

I am not sure if I understood you correctly. Two users are loading the same object in the same hibernate session?

If this is the case than I guess the behaviour is as it can be expected.

Hibernate loads the object for user A. Then user B loads the object within the same session. As the object is already in the session it is not really loaded again but user B will get a reference to the very same object.
Now user A and user B make changes on the same object (Eg. user A changes attribute X - user B should see those changes immediatly as he is holding a reference to the same object). When you commit the session Hibernate will store the state of the object.

I guess you simply can not use the same hibernate session for two users and achieve optimictic locking.


Top
 Profile  
 
 Post subject:
PostPosted: Wed Feb 22, 2006 5:17 am 
Newbie

Joined: Mon Feb 20, 2006 4:51 am
Posts: 3
no, that is not the case...
suppose a user loads an object from database, changes the version and tries to save it. all withing the same hibernate session. this should result in an optimistic locking failure if the version doesn't match the version of the database.but it doesn't. the problem occurs when the object is associated with the hibernate session. When the object is stale object(not associated with the session), it does throw the exception.

1.A a =(A) getHibernateTemplate().load(id);
2.a.setVersion(a.getVersion()-1);
3.//getHibernateTemplate().evict(a);
4.getHibernateTemplate().saveOrUpdate(a);

if line 3 is uncommented, it throws the expected exception. Otherwise it doesn't. My point is no matter what , it should throw the exception.

sajid moinuddin


Top
 Profile  
 
 Post subject:
PostPosted: Wed Feb 22, 2006 6:14 am 
Beginner
Beginner

Joined: Wed Sep 07, 2005 9:57 am
Posts: 20
Why do you manually set the version?

Don't you use something like this in your mapping:

Code:
      <version name="version" column="VERSION"  access="org.hibernate.property.DirectPropertyAccessor">
          <meta attribute="scope-field">protected</meta>
          <meta attribute="scope-get">protected</meta>
      </version>


so hibernate increments the version?


Top
 Profile  
 
 Post subject:
PostPosted: Thu Feb 23, 2006 3:24 pm 
Regular
Regular

Joined: Tue Dec 02, 2003 6:25 pm
Posts: 61
Location: Dallas, TX
Funny - I was just about to start a new thread on this exact topic.

I am having the exact same problem. I am using Hibernate's versioning feature in the standard way. (I'm using Hibernate 3.0.5)

Here is a mapping:
Code:
   <class name="mycompany.Opportunity" lazy="false"
      table="cc_opportunity">
      <id name="id" type="long" unsaved-value="null">
         <column name="id" sql-type="int" not-null="true" />
         <generator class="identity" />
      </id>
      <version name="version" type="long"></version>
      <many-to-one name="submittor" />
      <property name="name"  />
   </class>


Pretty simple. Everything works correctly with detached objects.

However, I'm not using detached objects and Hibernate is NOT throwing a StaleObjectException when I expect it to.

The Strategy

My current approach to application transactions: short sessions with no detached objects

I would call my transaction and concurrency strategy Short Session and Automatic Versioning (I think it is a legitimate approach, especially for web applications, and is different from all the strategies discussed in the Hibernate documentation.)

Here's how it works...

To be clear: This is a web application and I don't put anything in the HttpSession. In other words, there is nowhere to put a detached object or a long Hibernate session.

The scenario: User submits a form post to update Opportunity:1

My approach (without detached objects)
  • Get the Opportunity id from the servlet request (it was sent via a hidden field)
  • load Opportunity:1 from Hibernate
  • Populate Opportunity:1 with values from the servlet request
  • call session.update(opportunity)
This differs from the common approach in that the common approach seems to be to handle the request like this:
  • Construct a new opportunity
  • Set its id to 1
  • call session.update(opportunity)

The first approach (mine) seems better, because
  • you are modifying the actual object, rather than a newly constructed mock version of the object.
  • as such you have the ability to lazy-load and modify entities that are related to it.
  • and Hibernate will perform dirty-checking, only executing an update if something has actually changed.
You couldn't do that with a newly constructed object, and this is the only way that I can see a web application being able to take advantage of Hibernate's dirty checking.

This approach has worked spectacularly well in our web applications.

The problem...

The only kink is that the versioning check doesn't happen.

Sajid - I think I can tell you why this is happening, but I can't tell you how to address it. I have some ideas and I'll post if I come up with anything. I'm hoping that if we can establish this as a legitimate concern, someone from the Hibernate team might help us think through it and suggest a workaround.

In DefaultFlushEntityEventListener.onFlushEntity, it creates a new EntityUpdateAction, and for the version argument it passes
Code:
entry.getVersion()
where entry is the 1st level cached EntityEntry, and NOT the actual instance that is being updated.

The net effect is that it doesn't matter what is stored in the version field of your object if Hibernate has already loaded it and stored it in the session cache.

I wrote a junit test case that illustrates the problem

Code:

    public void testVersionChecking() throws Exception {

        try {
            Session session = openSession();

            Opportunity opp = (Opportunity) session.load(Opportunity.class,
                    new Long(1));

            Long origVersion = opp.getVersion();

            // modify the name so it is dirty
            opp.setName(opp.getName() + opp.getVersion() + 1);

            session.saveOrUpdate(opp);
            session.flush();

            // Make sure versioning increment worked
            assertTrue(opp.getVersion().intValue() == origVersion.intValue() + 1);

            // Try to set version back and update
            opp.setVersion(origVersion);
            opp.setName("Incorrect Version - should not get saved");

            session.saveOrUpdate(opp);
            session.flush();

            fail("Should have thrown a StaleObjectException exception!");

        }
        catch (StaleObjectStateException e) {
            log.debug("successfully threw exception: " + e,e);
        }
    }


This test fails. A StaleObjectStateException is never thrown and the "Incorrect Version" update succeeds.

There are several ways to make it work but they all involve detaching the object.
  • Before updating, I can call
    Code:
    session.evict(opp);
  • I can create a new session and use it to perform the update

So as Sajid said, there seems to be no real way to enforce version checking on objects within the same session.

One thing that I've thought about trying is extending DefaultFlushEntityEventListener and overriding it so that it uses the version number from the object itself rather than the session cached instance. However, I'm not as smart as the Hibernate guys and I have no idea how that would impact the rest of my application.


Top
 Profile  
 
 Post subject: A solution?
PostPosted: Thu Feb 23, 2006 6:37 pm 
Regular
Regular

Joined: Tue Dec 02, 2003 6:25 pm
Posts: 61
Location: Dallas, TX
UPDATE:

I've got something that is working for me so far, both in my test case and in my web application.

I extended the DefaultFlushEntityEventListener. Before invoking the super, I check to see if the version has changed. This seems like the right place to put this logic, since this listener already checks to make sure the id hasn't changed. It seems reasonable then to add a checkVersion() method along side the checkId() method. Unless I hear objections or criticism here, I'll probably submit this to jira after some more testing.

I'm not sure whether there are other listeners to be modified or if this is the only place where we need to check versions.


Code:
public class FlushEntityEventListener extends DefaultFlushEntityEventListener {

    public void onFlushEntity(FlushEntityEvent event) throws HibernateException {
       
        Object entity = event.getEntity();
        EntityEntry entry = event.getEntityEntry();
        EntityPersister persister = entry.getPersister();
        SessionImplementor session = event.getSession();
       
        if (persister.isVersioned()) {
            Object version = persister.getVersion(entity, session.getEntityMode());
            // Make sure version has not changed
            if (!persister.getVersionType().isEqual(version,entry.getVersion())){
                throw new StaleObjectStateException(persister.getEntityName(),entry.getId());
            }
        }
        super.onFlushEntity(event);
    }
}


Top
 Profile  
 
 Post subject: Re: VERSIONING PROBLEM WITH OBJECT IN THE SAME SESSION
PostPosted: Mon Sep 14, 2009 2:36 pm 
Newbie

Joined: Mon Sep 14, 2009 2:09 pm
Posts: 2
How did you register the custom FlushEntityEventListener with Hibernate?

The problem I'm working on is similar. I want optimistic locking on the back end (which works as expected), but I also want to pass the 'version' along in a hidden field with HTTP forms so that a user who's looking at stale data on an edit screen can get a failure if someone modifies that screen before they click submit.

I tried to argue with The Powers that it is bad form to meddle with the version attribute (since Hibernate owns it), but they suggested that this was the way to go.

Any advice on the best way to work on this if the strategy I've outlined above is *stupid*?

Thanks,
LES2


Top
 Profile  
 
Display posts from previous:  Sort by  
Forum locked This topic is locked, you cannot edit posts or make further replies.  [ 8 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.