OptimisticLocking selected columns gets last updated values [message #821301] |
Thu, 15 March 2012 03:08  |
Eclipse User |
|
|
|
hi,
we are using Eclipselink 2.3 against a legacy Oracle 10g DB. We are following their optimistic locking strategy of using the TS_LAST_MODIFIED timestamp column on the table when doing updates. For insert we use TS_CREATED timestamp column.
This is defined in a Eclipselink @MappedSuperclass like so.
@OptimisticLocking(type = OptimisticLockingType.SELECTED_COLUMNS, selectedColumns = { @Column(name = "TS_CREATED"), @Column(name = "TS_LAST_MODIFIED") })
This is used in a web app so detached entities are being merged. Wanting to check that a concurrency exception would get thrown I did the following test.
User 1 - Opens a edit page for existing record
User 2 - Opens a edit page for same existing record
User 1 - Makes changes and updates record successfully
User 2 - Makes changes and updates record successfully <-- Would have thought this throws a concurrency exception!!
The reason that User 2 saves successfully is because its TS_LAST_MODIFIED column that was modified by User 1 has the new value on the WHERE clause of User 2 update. Hence it does not pick up there is a difference.
Update 1 SQL
UPDATE REVIEW_EVENT_REGISTER SET TX_COMMENT = ?, TS_LAST_MODIFIED = ? WHERE (((KY_REVIEW_APPLICATION = ?) AND (KY_SEQ = ?)) AND ((TS_CREATED = ?) AND (TS_LAST_MODIFIED = ?)))
bind => [test 4155acvv6, **2012-03-15 15:25:51.352**, 20609, 21680, 2012-03-13 10:40:39.0, 2012-03-15 15:19:43.0]
Update 2 SQL
UPDATE REVIEW_EVENT_REGISTER SET TX_COMMENT = ?, TS_LAST_MODIFIED = ? WHERE (((KY_REVIEW_APPLICATION = ?) AND (KY_SEQ = ?)) AND ((TS_CREATED = ?) AND (TS_LAST_MODIFIED = ?)))
bind => [test 4155acvv5333333, 2012-03-15 15:26:33.368, 20609, 21680, 2012-03-13 10:40:39.0, **2012-03-15 15:25:51.0**]
I've updated the pertinent timestamp values with **. I have read of the trap where you do a find again on the entity before the merge and this results in picking up the latest values but I am definitely not doing in this case.
It is almost as if that when EclipseLInk does the merge it syncs itself with the underlying database. I have no cache or other properties defined in the persistence.xml so as bare bones as you can get.
Is this expected behaviour? Or am I missing the obvious here?
thanks for any help,
Ralph
Environment: Spring 3.1 | Tomcat 7 | Eclipselink 2.3
[Updated on: Thu, 15 March 2012 03:09] by Moderator
|
|
|
|
Re: OptimisticLocking selected columns gets last updated values [message #823867 is a reply to message #821774] |
Sun, 18 March 2012 21:21  |
Eclipse User |
|
|
|
hi Chris,
thanks for the great answer.
I tried to use the @Version annotation but the TS_LAST_MODIFIED timestamp column is null when initially created. We have another field, TS_CREATED, that gets populated on insert. Tried to write a Converter to get around this but no go. Too hard to try and update the legacy application to make the TS_LAST_MODIFIED always populated.
Fell back to extending DescriptorEventAdapter and providing a hook for aboutToUpdate. This all worked beautifully. Have attached code below.
public class CustomEntityListener extends DescriptorEventAdapter {
/**
* Provide optimistic locking on update. Last modified timestamp used as the
* versioning field. As the timestamp may be null when an entity is created
* the more common @Version annotation cannot be used as it does not accept
* nulls.
* <br/>
* Id and last modified timestamp fields updated.
*
*/
@SuppressWarnings("unchecked")
@Override
public void aboutToUpdate(final DescriptorEvent pEvent) {
BaseDomain currentObject = (BaseDomain) pEvent.getSource();
BaseDomain originalObject = (BaseDomain) pEvent.getOriginalObject();
if (originalObject.getLastModified() != null
&& (originalObject.getLastModified().compareTo(currentObject.getLastModified()) != 0)) {
throw new OptimisticLockException("Data has changed. Please refresh and try again.");
}
pEvent.getRecord().put("ID_LAST_MODIFIED", SecurityUtils.getCurrentUsername());
pEvent.getRecord().put("TS_LAST_MODIFIED", new Timestamp(Calendar.getInstance().getTimeInMillis()));
}
/**
* Add id and time when entity created.
*/
@Override
public void preInsert(final DescriptorEvent pEvent) {
((BaseDomain) pEvent.getSource()).setCreated(new Timestamp(Calendar.getInstance().getTimeInMillis()));
((BaseDomain) pEvent.getSource()).setCreatedBy(SecurityUtils.getCurrentUsername());
}
cheers,
Ralph
|
|
|
Powered by
FUDForum. Page generated in 0.17190 seconds