Recently, I have suffered a wired problem when using Ebean to update item. When I update it by doing following:
Then when I run this code, the
save() statement give me a exception:
The update code is pretty simple, and so does the definition of SomeEntity.
(If you just want a quick solution, refer to the next section)
According to avaje’s document, OptimisticLock is a mechanism to avoid lost of updates when these updates occur in the same time on the some data.
This mechanism is very simple: check if old data has been changed. If the attributes before updated have been changed by other threads, the update will be abort by throwing OptimisticLockException.
There are two ways for Ebean to decide whether the old data has been changed:
- check all the attributes.(default way)
- check one special attribute defined by user named as Version Attribute.
The first one is how problem came out. When I performed
save(), the Ebean actually mapped it into following sql:
This sql seems innocent, and it works well in the most time. However sometimes it can fail even if on one has changed the data. That is because sometimes database saves data as different value(to optimise the storing I guess). This could happen to double or text type of data. This will cause the where clause above fails, and make this update exceptional.
As an example mentioned by this post:
It can happen that you retrieve in your Java code something like 0.46712538003907 but in fact, in the database, the data stored is something like 0.467125380039068. When updating, the Java value will be included in the WHERE clause and, as it doesn’t match the value stored in the database, the update will fail.
Solution: Version Attribute
By adding Version Attribute to the model, you can specify Ebean only use this column to decide whether old value has been changed.
You don’t need to change anything in the updating code. Now if you perform an update, the sql statement will be:
And problem should be solved.
Note that OptimisticLock only happen in READ_COMMITTED Isolation level. So if you use transaction when update somehow, you might just walk around this problem.