I have a question about how to implement something using EJB. I am sure
that this problem is very common and that it has come up in discussions
in various forms on this list, but I don't think the right approach has
been clarified. This is surprising since the spec should be able to
handle such a situation rather easily. It appears to me that many
people have a misunderstanding about EJB that leads to an incorrect way
to solve this problem. Including in that misunderstanding is the Sun
BluePrints guide. In the following I will assume we are talking EJB 1.1
spec and I am interested in a solution that conforms to that spec.
Problem: Suppose you have an Account class that is the remote interface
to an Account bean. AccountBean is the entity bean implementation
class. You want to write a method increment(int x) that adds x to the
balance on the account represented by the Account instance. There are 2
approaches that come to mind about how to do this. The first is the
most obvious but I believe there are problems with it.
1) public void increment(int x) {balance += x;};
2) public void increment(int x) { //use jdbc to increment the balance in
the db using sql like "updateaccount_table set balance = balance + ?
where account_key = ?". The first question mark is set to x and the
second question mark is set to the primary key of the bean.}
Why isn't 1) good? Containers are free to use multiple bean instances
to handle concurrent calls to an entity object as long as those calls
are occurring under a different transaction. This means that you can
have bean instance A and bean instance B both handling calls to
increment x, where both A and B represent the same entity object (same
primary key). Some containers my choose not to do this, however there
is no guarantee that the container provider won't change their approach
in the future. Furthermore if you rely on the container not doing that
your code will not work on any EJB server. This would make it difficult
for EJB bean providers (like the Theory Center) for providing server or
container independent beans. So if you agree now that your code should
work if the Container provider uses multiple bean instances then I think
there is a problem. Suppose that in two separate threads calls are
made to an entity object. One thread calls increment(1) and the other
increment(2). These calls occur concurrently. Assume also that the
initial balance before the calls is 3. After the call the new balance
should be 6. Here is what can occur:
T1:Container calls ejbLoad on Bean A: Balance gets set to 3.
T2:Container calls ejbLoad on Bean B: Balance still is 3. (This
happens in a separate thread and separate transaction context).
T3:Container calls increment(1) on Bean A: Balance gets set to 4 in Bean
A.
T4:Container calls increment(2) on Bean B: Balance gets set to 5 in Bean
B.
T5:Container calls ejbStore on Bean A: balance in database is updates to
4.
T6:Container calls ejbStore on Bean B: what happens??
What happens may depend on what the Transaction isolation level is and
perhaps on the underlying database. If you use Oracle and the default
transaction isolation level (read committed) then the balance gets set
to 5 in the database. (The wrong answer!). If you use serializable
transaction isolation level then an exception is thrown and the client
will have to react appropriately to the exception. So it would appear
that the only way 1) could possibly work (in some sense) is to use
transaction level serializable. So 1) with transaction serializable is
an approach but it is incompletely specified since we haven't said how
the client should handle this. I imagine there are ways to handle it.
(Catch the exception and try again for example). Furthermore I have
been told by WLS not to use transaction serializable due to a bug in
Oracle so 1) does not currently work for Oracle. Even if this would
work it seems like it would be better to just force the container or to
specify in the deployment descriptor that calls to increment should be
serialized. Currently there is no way to do that in the spec.
The second solution would work however there are also problems with it.
You need to be careful to make sure that when ejbStore is called the
correct value is stored in the database. For example, you cannot just
increment the value in the db. You would also need to set balance in the
bean appropriately.
So the question is, how should one implement this? Surely we should be
able to reach a consensus on this rather simple question?
dan
===========================================================================
To unsubscribe, send email to [EMAIL PROTECTED] and include in the body
of the message "signoff EJB-INTEREST". For general help, send email to
[EMAIL PROTECTED] and include in the body of the message "help".