views:

106

answers:

3

My question is similar to

http://stackoverflow.com/questions/3476613/updating-one-field-in-jpa-entity

Basically I have an entity Foo which has some basic fields (enum, String, DateTime) and it has two other fields. One of them is a OneToOne and the other is a OneToMany implemented using a Collection.

I have two threads which are running and at various time will look up the same entity using a findById type method on a Singleton EntityManager wrapper class (I did not write this ;)

What I would like to avoid is the following situation:

Thread 1 looks up a Foo which has an ID of 1 and it gets a reference to Foo with the state of Foo at that point in time.

Thread 2 looks up a Foo which has an ID of 1 and gets a ref to Foo with the same state as thread 1

Thread 1 changes the String field on Foo and persists it with a merge Thread 2 changes the value of the Enum field on foo and persists it with a merge

The last operation results in change thread 1 made being replaced with the old state of the String field that Thread 2 got because the merge is updating everything (except the OneToOne and the OneToMany since their CascadeType is Persist and Remove).

What I am looking for is suggestion as to what the best practice approach would be to preventing this clobbering of state when one only needs to update specific fields in this fashion.

One though I had, which I think is what is alluded to in the link at the top of my post, was to change things from using the generic save(Object o) that it uses now which does a merge to something which is specific to this case that executes an UPDATE on the specific field keyed from the entity ID. Is there a better way? My current persistence provider is EclipseLink.

TIA

-NBW

A: 

First, you can utilize transactions that handle these kinds of situations. If performance is not an issue, use pessimistic locking and your problem is solved - the 2nd thread will wait until the first one updates the value.

But I agree for this use-case there should be something simpler. You can use plain JDBC and compose an update query.

Bozho
A: 

You should use optimistic locking (@Version) in JPA. This will cause the second client's transaction to fail.

Also EclipseLink will only update the fields that changed, so if you read both objects into the transaction/persistence context, they would only update the fields that they changed. The only way to have it revert the changes would be if you serialized or detached the object after it was read, then merged in back into a new persistence context. (Note that merge() is only required for detached objects and should not be used for managed objects as they are already managed).

James
A: 

Thread1 refers to detached instance of Foo and so does the thread2 referring a diff instance of Foo, optimistic locking (@Version) will not work for you since when you try to save detached instance second time it will throw exception, Best way to solve your issue is to do this everytime before you merge your detached entity

Sol1 : // loading foo everytime you make changes foo = em.find(foodId, Foo.class); //foo.set.. make change em.merge(foo)

Sol2 : // foo is referring to detached entity em.refresh(foo); //foo.set .. make change em.merge(foo)

Intesar Mohammed