Eclipse Community Forums
Forum Search:

Search      Help    Register    Login    Home
Home » Eclipse Projects » EclipseLink » OptimisticLocking selected columns gets last updated values(OptimisticLocking)
OptimisticLocking selected columns gets last updated values [message #821301] Thu, 15 March 2012 03:08 Go to next message
Ralph Roper is currently offline Ralph Roper
Messages: 2
Registered: March 2012
Junior Member
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]

Report message to a moderator

Re: OptimisticLocking selected columns gets last updated values [message #821774 is a reply to message #821301] Thu, 15 March 2012 16:04 Go to previous messageGo to next message
Chris Delahunt is currently offline Chris Delahunt
Messages: 995
Registered: July 2009
Senior Member
Hello,

EntityManagers are treated as separate transactional contexts, so if you are not reusing the exact same EntityManager that initially read in user2's existing record, then the new EntityManager should show the updated values for that record. Chances are likely that you are obtaining the record from an EntityManager, making changes to it, then merging it into a new EntityManager context.

Using OptimisticLockingType.SELECTED_COLUMNS you must be careful using the merge api as this will allow overwrite the new values. This strategy is great if you are modifying the the same object read in, but not if merging in detached entities since there is no way to tell what the values were when you initially read it in.

What you might want to use TS_LAST_MODIFIED as a version field instead of using SELECTED_COLUMNS, and allow EclipseLink to manage the field instead. This will allow the EntityManager merge operation to detect that the entity you are merging has a date earlier than the one you are merging into and throw the appropriate exception.

The alternative might be to have a merge event that checks the values of the field being merged in and throws an application exception if it should not proceed.
http://stackoverflow.com/questions/3410841/jpa-transient-fields-being-cleared-before-preupdate-method-is-called
shows a quick example of a post merge event you would need ot compare your object to the original from the event passed in.

Best Regards,
Chris
Re: OptimisticLocking selected columns gets last updated values [message #823867 is a reply to message #821774] Sun, 18 March 2012 21:21 Go to previous message
Ralph Roper is currently offline Ralph Roper
Messages: 2
Registered: March 2012
Junior Member
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
Previous Topic:moxy osgi
Next Topic:MySQLIntegrityConstraintViolationException when updating @ManyToMany @JoinTable @OrderColumn
Goto Forum:
  


Current Time: Sun Apr 20 17:59:21 EDT 2014

Powered by FUDForum. Page generated in 0.07655 seconds