More Books
Hibernate: A J2EE Developer's Guide
Hibernate: A J2EE™ Developer's Guide
Table of Contents
Copyright
Acknowledgments
About the Author
Preface
Required Skills
Roadmap
Chapter 1. Overview
Why Object/Relational Mapping?
What Is Hibernate?
Comparing JDBC to Hibernate
Hibernate's Mapping System
Other Java/Database Integration Solutions
How to Obtain and Install
Supported Databases
Chapter 2. Getting Oriented
Application Architecture
Mapping Files
Generating Java Source
Application Configuration
Web Application
JSP Interface
Chapter 3. Starting from Java
Java Object Model
Generated Mapping Files
Generated Schema
Working with Artifacts and Owners
Chapter 4. Starting from an Existing Schema
Initial Schema
Using Middlegen
Generated Mapping Files
Generated Java
Working with the Database
Chapter 5. Mapping Files
Basic Structure
Mapping File Reference
Chapter 6. Persistent Objects
Sessions
Objects and Identity
Life-Cycle Methods
Chapter 7. Relationships
Database Relationships
Java Collection Relationships
Java Class Relationships
Any-Based Relationships
Bi-directional Relationships
Chapter 8. Queries
HQL
HQL Reference
Select
From
Where
Group By
Having
Order By
Criteria Queries
Native SQL Queries
Chapter 9. Transactions
Introduction to Transactions
Optimistic and Pessimistic Locking
Chapter 10. Performance
Finding and Solving Problems
Queries
Inserts
Connection Pooling
Caching
Chapter 11. Schema Management
Updating an Existing Schema
Generating Update and Drop Scripts
Chapter 12. Best Practices, Style Guide, Tips and Tricks
Reducing Code with Inversion of Control
Reducing Session Creation Impact with ThreadLocal
Using Hibernate as an EJB BMP Solution
Integrating with Other Technologies
Applications That Use Hibernate
Strategies for Getting Started
Chapter 13. Future Directions
Hibernate 3.0
EJB 3.0
Here and Now
Index
SYMBOL
A
B
C
D
E
F
G
H
I
J
K
L
M
N
O
P
Q
R
S
T
U
V
W
X

Optimistic and Pessimistic Locking

Locking refers to the notion that changes to a record made by one person should not overwrite changes made by someone else. Strategies for dealing with this fall into two categories. Pessimistic locking assumes that every record will be subject to contention; optimistic locking assumes that conflicts are the exception, not the rule.

Pessimistic Locks

Pessimistic locking is used to denote a system in which an individual user can check out a record. It is called pessimistic because it assumes the worst-case scenario in every possible transactionnamely, that every transaction will conflict unless a strict monitor is used.

With a pessimistic lock, users may or may not be able to view or edit a record until the lock is released. To support this style of lock, the database must support pessimistic locking. Obtaining and releasing of locks on specific rows of a table can be managed with the net.sf.hibernate.LockMode class in conjunction with certain methods of Session. For example, you can use the method session.load(object, type, LockMode.UPGRADE) for explicit pessimistic locking. While pessimistic locking can be useful in some circumstances the application must take great care to avoid locking objects for an unnecessarily long time. In addition, care must be taken to release every lock at the right time, or else the record will become unavailable for a lengthy period.

Optimistic Locks

Optimistic locking refers to the notion that "checking out" an object and applying changes are two independent steps. For example, consider the post system described in Chapter 2. A single record is used to store a post. A user at work downloads the original version of the post into his or her Swing client and makes some changes, thereby creating version two of the post. The user leaves for home without saving the changes. At home, the user opens the same post, makes a different set of changes, and then saves the post data. This becomes version three. The next morning, the user returns to the Swing client and clicks "save post" on version two of the document.

VERSION CONTROL LOCKING

If you have ever used a version control system, you've seen one of these two strategies in action. For example, Microsoft Visual SourceSafe uses a pessimistic system in which locks are explicitly managed by the developer (but conflicts are impossible), and CVS uses an optimistic system with no locks (but occasionally requiring merges to deal with conflicts).


If the developer had chosen pessimistic locking, the record would have been locked when checked out by the Swing client, and the user would not have been able to save the changes at home (generally speaking, pessimistic locking is easier for the developer, but frustrating for users). Optimistic locking, in contrast, refers to the fact that we "optimistically" assume that no changes are made between when the record is read and when the record is updated. This is only a problem if a "secret" change is made (in the example above, version two).

The user experience for resolving an optimistic conflict will vary depending on the application. In the example given above, when the user clicks "save post" for version three, we probably would like to notify the user that the record has been updated since the last viewing. Ideally, we'd want to let the user compare versions and select the preferred version (as shown in Chapter 2). If the application is an elaborate one, it may even provide a mechanism for merging changes. Listing 9.1 shows an excerpt of the code given in Chapter 2. Note how the net.sf.hibernate.StaleObjectStateException is caught to manage the pessimistic conflict.

Listing 9.1. Handling an Optimistic Conflict
try
{
     hibernateSession = AppSession.getSession();
     myTransaction = hibernateSession.beginTransaction();

     myPost = new Post();
     myPost.setId(request.getParameter("postID"));
     myPost.setRevision(
           new Integer(
                 request.getParameter("revision"))
                      .intValue());
     myPost.setTitle(request.getParameter("title"));
     myPost.setDate(new java.util.Date());
     myPost.setSummary(request.getParameter("summary"));
     myPost.setContent(request.getParameter("content"));

     Author myAuthor = new Author();
     myAuthor.setId(request.getParameter("authorID"));
     myPost.setAuthor(myAuthor);

     hibernateSession.update(myPost);

     myTransaction.commit();
     hibernateSession.close();

     redirect_page =
           redirect_page +
                request.getParameter("postID");
     done = true;
}
catch (net.sf.hibernate.StaleObjectStateException stale)
{
     error =
     "This post was updated by another " +
     "transaction. You may either update " +
     "the existing data, or resubmit ";
     "your changes.";
     conflict=true;
}
catch (Exception e) {
     error = e.getMessage(); e.printStackTrace();
     try{ myTransaction.rollback(); }
     catch (Exception e2) {;}
}
finally
{
     try{hibernateSession.close();}
     catch (Exception e) {;}
}

Hibernate supports several different models for detecting this sort of versioning conflict (or optimistic locks). The versioning strategy uses a version column in the table. You can use either a version property tag or a timestamp property tag to indicate a version column for the class (see the appropriate tag for more information). When a record is updated, the versioning column is automatically updated as well. This versioning strategy is generally considered the best way to check for changesboth for performance and for compatibility with database access that occurs outside of Hibernate. For example, you can simply issue a SELECT to obtain a record by id, and include the version column in the WHERE clause to indicate the specific record you wish to UDPATE; a failure to update is an easy way to detect that the record is not properly synchronized. This is precisely the functionality as provided by the StaleObjectException. An example of the versioning strategy (and a user interface for managing conflicts) is shown in Chapter 2.

The dirty strategy only compares the columns that need to be updated. For example, let's say you load a Cat object, with a name ("Tom"), weight, and color. You change the name to "Bob" and want to save the change. Hibernate will verify that the current Cat name is "Tom" before updating the name to "Bob." This is computationally intensive compared to the versioning strategy, but can be useful in situations in which a version column is not feasible.

The all strategy compares all the columns, verifying that the entire Cat is as it was when loaded before saving. Using the Cat example in the preceding paragraph, Hibernate will verify that the name, weight, and color are as loaded before persisting the change to the name. While more secure than the dirty strategy, this strategy, too, is more computationally intensive.

Finally, the none strategy can be used to ignore optimistic locking. Updated columns are updated, period. This obviously performs better than any other strategy but is likely to lead to problems if you have more than a single user updating related records.

Generally speaking, I recommend using the versioning strategy to manage conflicts if at all possible. The pessimistic model, unless very carefully managed, can lead to more problems than it solves, and the dirty and all mechanisms are not very high-performance. Also, don't go overboard with versioningthe likelihood of contention is quite low if a resource is owned by only a single user.