If you are using multiple system transactions that span a single business transaction, there is chance of isolation problems: a business transaction could see changes made by other transactions and this could lead to erroneous behavior.
Example:
there is a web page that can be accessed by management, that contains a list of all employees. Each row contains the id, the name, the date of the last bonus and a button to give the bonus. When the button is pressed, the id of the employee (eventually) is send to the following service method:
@Transactional
void giveBonus(long id){
//load throws a special exception if the employee is not found
//Spring: ObjectRetrievalFailureException if employee isn't found
Employee e = employeeDao.load(id);
e.giveBonus();
}
The problem with this approach is that 2 managers could access the page at roughly the same moment and both see that some employee didn’t get a bonus for some time and decide to give the bonus. The consequence is that the employee has received the bonus twice. Although this example is a little bit hypothetical, in some cases this behavior could lead to serious problems.
Optimistic locking is one of the best known techniques to deal with these issues: it doesn’t lock (unlike pessimistic locking) but hopes (is optimistic) about the fact that no other transaction is going to conflict. If a different transaction does make a change, the version of the data is changed, and this is something that can be checked. As soon as a difference between versions is found, an optimistic locking failure exception can be thrown.
Just carrying around the version and id, doesn’t feel that good. It makes method signatures less pretty and you have to carry it around. Especially when you need to deal with multiple entities, the solution gets more ugly. The solution is simple: integrate id and version into a single structure: VersionedId. VersionedId’s contain the id of an entity and also contain the version of that entity; so a VersionedId can uniquely identify an entity in time and could look like this:
class VersionedId{
long version, id;
}
the previous example could be rewritten by using a VersionedId:
@Transactional
void giveBonus(VersionedId id){
//load throws a special exception if the employee is not found or version doesn't match
//Spring: ObjectRetrievalFailureException if employee not found
//Spring: OptimisticLockingFailureException if version doesn't match
Employee e = employeeDao.load(id);
e.giveBonus();
}
As you can see there is not much difference. But you have to make sure that the load method of the EmployeeDao is able to handle versioned id’s and starts throwing some kind of optimistic locking failure if the Employee with the given id and version can’t be found. And instead of just placing the id of the employee on the html-page, the version needs to be placed as well.
Using detached objects (and reattaching them) is also a solution. But there are some issues:
- The detached objects need to be stored and the most likely place is the HttpSession. This could lead to replication issues if you are in a clustered environment
- Vague transactional behavior
- I have seen some weird issues with detaching and reattaching entities with Hibernate (especially not detaching completely)