-->
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: About locking
PostPosted: Wed May 19, 2004 10:04 am 
Newbie

Joined: Fri May 14, 2004 9:34 am
Posts: 19
Suppose I got 2 users... so 2 different HttpSession objects, each user loads from the db using Hibernate the same persistent object, so they both perform session.load(aClass.class, new Long(10)) then both users have different instances of the same persistent class.
One user modify the firstName attribute of that instance and call session.save(firstInstanceModified) and then the other user modify lastName Attribute of the instance and also call
session.save(secondInstanceModified).

How HIbernate handle this ? How does the second user know that the instance he wants to persist (with lastName modified) has been already modified ?


Top
 Profile  
 
 Post subject:
PostPosted: Wed May 19, 2004 10:40 am 
Hibernate Team
Hibernate Team

Joined: Tue Sep 09, 2003 2:10 pm
Posts: 3246
Location: Passau, Germany
Use optimistic locking with a version/timestamp column is the recommended approach.


Top
 Profile  
 
 Post subject:
PostPosted: Wed May 19, 2004 11:12 am 
Newbie

Joined: Fri May 14, 2004 9:34 am
Posts: 19
Thanks !

Code:
The problem that you're pointing out is one of concurrency.  There are
two basic ways to solve this problem: optimistic locking and pessimistic
locking.

Pessimistic locking involves locking the row in the database.  While
this was a reasonable and often used option in client/server
applications, it is not as attractive when dealing with three tiered
applications.

Optimistic locking is the approach used by most "well designed" three
tiered applications.  To implement optimistic locking, you would add a
"version" column to your database table.  Each row would have an
associated version.  Each time an update to a row is made, its version
number is incremented.  Lets take a look what happens in your scenario,
where you have two different applications reading the same row and then
updating it in turn:

-------------------------------------------------
|   id   |   name   |   version   |
-------------------------------------------------
|   1   |   Maciej   |   0   |
-------------------------------------------------

1. app1 reads row with id=1 and version=0
2. app2 reads row with id=1 and version=0
3. app1 updates row with id=1 and version=0 and sets name=Zawadzki --
version=1
4. app2 tries to update row with id=1 and version=0 to set name=Bogdan
-- this update does not happen

The reason the update does not happen in step 4 is because the SQL for
the update is something like this:

UPDATE ITEM
SET
     NAME = ?,
     ...               // additional attributes
     VERSION = ?       // new version value
WHERE
     ID = ? AND
     VERSION = ?       // old version value


The Java code to do this would be something like:

1.   // handle the concurrency control
2.   int oldVersion = state.getVersion();
3.   int newVersion = oldVersion + 1;
4.   
5.   try {
6.       conn = getConnection();
7.       stmnt = conn.prepareStatement(UPDATE_SQL);
8.   
9.       // update the attributes
10.       ...
11.       stmnt.setInt(6, newVersion);
12.   
13.       // set the concurrency control attribute on the where clause
14.       stmnt.setInt(7, oldVersion);
15.   
16.       // execute the query
17.       int rowCount = stmnt.executeUpdate();
18.   
19.       // check for concurrent modification and rollback version
20.       if (rowCount == 0) {
21.           throw new DFConcurrentModificationException("Could not update "+
22.               "version " + oldVersion);
23.       }
24.   } catch (SQLException e) {
25.       ...
26.   } finally {
27.       // close the statement and connection
28.       ...
29.   }

Notice that we can check how many rows were modified by the update and
throw an exception if no rows were modified -- which would happen if
some one else got in there and modified the row before us.

Now, back to your email though.  EJBs do NOT protect against concurrent
modification if you have multiple applications accessing the same row.
Even Entity beans will not solve the problem of concurrency.  What's
more, EJBs do not solve this problem even when used within the same
application.  That's because the way that we use EJBs is with
DataTransferObjects.  And that means that we have one transaction to
obtain the DTO, which is then displayed to the user.  And when the user
submits edits, we need another transaction to write those edits to the
database.  There is no telling how many other clients have made
modification to the underlying data between the two transactions -- one
to get the DTO and one to write the updated state back to the database.
  This is one of those often misunderstood parts of EJBs.  And no-one is
out there trying to make this better understood because the marketecture
says that EJBs will solve all of your concurrency problems -- well, they
don't.  EJBs do enforce concurrency (by using basically pessimistic
locking) but only within the scope of a Transaction.  But as you pointed
out, that is pretty much useless since most three tiered apps will need
concurrency across two transactions -- and EJBs don't help there.

So, now back to Hibernate.  I think there is a simple solution that
you've missed.  Take a look at 5.1.7
(http://www.hibernate.org/hib_docs/reference/html/or-mapping.html#or-mapping-s1-6).
  You can define a version column as part of your database schema. Now,
based on that, take a look at 17.3.1 one more time.  In a web app, you
would simply associate the Hibernate Session with the user's HTTP
session.  When the user submits a request, you simply get that user's
Hibernate session, reconnect it and do another transaction.  Hibernate
takes care of the optimistic locking for you if you have a version
column in your schema.

Notice that there is nothing in Hibernate that says that a Session can
not be accessed by multiple Threads.  The only things that is said about
Sessions is that they are not Thread safe and should not be accessed by
multiple *concurrent* threads.  So, it is OK to bind a Hibernate Session
to an HTTP session, because even though each successive HTTP request may
be handled by a different thread, such threads will be accessing the
Hibernate session one at a time -- and that is OK, no problem with
thread safety there.

A little disclaimer.  I'm not a Hibernate guru and have not actually
used the version (optimistic locking) feature mentioned above.  I have
used (and developed) similar features in other O/R mapping tools though.
  But it should be very easy to test whether Hibernate does exactly what
we need it to do to implement optimistic locking.




Top
 Profile  
 
 Post subject:
PostPosted: Wed May 19, 2004 11:17 am 
Newbie

Joined: Fri May 14, 2004 9:34 am
Posts: 19
When i call update() it increments the version value automatically ?


Top
 Profile  
 
 Post subject:
PostPosted: Wed May 19, 2004 11:24 am 
Hibernate Team
Hibernate Team

Joined: Tue Sep 09, 2003 2:10 pm
Posts: 3246
Location: Passau, Germany
If an update is done, yes it is.


Top
 Profile  
 
 Post subject:
PostPosted: Wed May 19, 2004 2:39 pm 
Newbie

Joined: Fri May 14, 2004 9:34 am
Posts: 19
I add the VERSION column to my table Attribute in order to simulate the commented scenario, also add the version attribute in the Attribute Class.
but when i Execute...

Code:
      try {
         Configuration cfg = new Configuration().addClass(Task.class).addClass(TaskAttribute.class).addClass(Attribute.class);
         SessionFactory sf = cfg.buildSessionFactory();

         Session firstSession = sf.openSession();
         Transaction firstTransaction = firstSession.beginTransaction();

         Session secondSession = sf.openSession();
         Transaction secondTransaction = secondSession.beginTransaction();

         Attribute firstAttribute = (Attribute) firstSession.load(Attribute.class, new Long(1));
         Attribute secondAttribute = (Attribute) secondSession.load(Attribute.class, new Long(1));
      
         firstAttribute.setName("pepe");
         firstSession.update(firstAttribute);
         firstTransaction.commit();
      
         secondAttribute.setName("cucho");
         secondSession.update(firstAttribute);
         secondTransaction.commit();
               
         firstSession.close();
         secondSession.close();
      
      } catch(StaleObjectStateException e) {
         e.printStackTrace();
      }


I got NonUniqueObjectException. Should't I receive StaleObjectStateException instead wich is thrown when a version number check failed ?


Top
 Profile  
 
 Post subject:
PostPosted: Wed May 19, 2004 2:51 pm 
Newbie

Joined: Fri May 14, 2004 9:34 am
Posts: 19
Sorry my code was wrong ... now it is correct

Code:
   firstAttribute.setName("cucho");
         firstSession.update(firstAttribute);
         firstTransaction.commit();
      
         secondAttribute.setName("pepe");
         secondSession.update(secondAttribute);
         secondTransaction.commit();


But i got no Exceptions now... when StaleObjectStateException should be thrown. Instead value "cucho" is saved then "pepe" is saved when I think secondTransaction.commit() should thrown the exception since the version value it has is the old version value, cause firstTransaction.commit() already incremented it. so what's wrong ?


Top
 Profile  
 
 Post subject:
PostPosted: Wed May 19, 2004 3:45 pm 
Newbie

Joined: Fri May 14, 2004 9:34 am
Posts: 19
ok it worked now... i add unsaved-value="negative" to the <version> attribute in the mapping file.

What does negative mean ? The doc only explain undifined


Top
 Profile  
 
 Post subject:
PostPosted: Thu May 20, 2004 6:46 am 
Hibernate Team
Hibernate Team

Joined: Tue Sep 09, 2003 2:10 pm
Posts: 3246
Location: Passau, Germany
The unsaved-value determines what id-value means your object is "new". Negative means of course Hibernate considers all objects with negative id values to be "unsaved".


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.