> > Simply put if there's $5 left in the bank, you wouldn't want to have
> > a user withdraw $5 with two different web browsers if they hit
> > submit at the same time... so we kinda want it to be like there's
> > only one teller window, and the servlets have to wait their
> > turn.
A poster already mentioned that simplistic solutions might not work
in a clustered environment. I agree that designs that work due
to timing or happen stance that may not remain constant are weak
designs bound to have problems in deployment.
This problem is the the classic stale update scenario. Multiple
clients show $5 available to with draw. They all hit "submit"
at once. How to guard against an over draft in a robust and
repeatable in all scenarios way??
The most offered solution that I've heard and I've implemented uses
optimistic concurrancy guarded with a row version number;
- add a version number or modification date to your data base tables.
I use a sequence number that trigger auto-increaments on update.
- Your Model class (represents a data base table row) returned by
DAO's and Entities include the sequence number.
- Your presentation tier has a reference of the model (including the
row version number) and all display $5 and internally held in
the session is an instance of the Model used to generate the
dynamic data by your View (MVC) component.
- All users hit submit.
- The Controller (MVC) processing this event fetches the Model from
the session and calls the setter to modify withdraw ammount, passes
this Model to the Business Deligate to access your transactional layer
(mediated by a Session bean calling Entities).
- The Model instance is serialized and passed as an argument to the
layers of business components down to the Entity responsible for
updating the AccountBalance table row for this account.
- I used BMP and DAO objects to package the SQL logic. My DAO fetches
the sequence number from the Model and account new balance from the
model and builds the SQL prepared statement:
UPDATE account set balance = ? where column_version_num = ? ;
I call stment.setString (1, model.getNewBalance());
stment.setString (2, model.getVersionNumber());
If I get a SQLException on this update, then I know that someone else
beat me to the $5.
The key concept is the WHERE clause will only update the row IFF
the sequence number has not been changed by someone else (meaning
I had stale data).
This will work in clustered environments at all times and does not
depend on synchronized business delegate methods or any other design
that is likely to be defeated by appserver servlet or object pooling
or use of multiple VMs to host your application.
Good luck,
curt