Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

JPA atomic query/save for multithreaded app

Tags:

java

jpa

I am in the midst of changing my JPA code around to make use of threads. I have a separate entity manager and transaction for each thread.

What I used to have (for the single threaded environment) was code like:

// get object from the entity manager
X x = getObjectX(jpaQuery);

if(x == null)
{
    x = new X();
    x.setVariable(foo);
    entityManager.persist(x);
}

With that code in the multi threaded environment I am getting duplicate keys since, I assume, getObjectX returns null for a thread, then that thread is swapped out, the next thread calls getObjextX, also getting null, and then both threads will create and persist a new X().

Short of adding in synchronization, is there an atomic way to get/save-if-doesn't-exist a value with JPA or should I rethink my approach

EDIT:

I am using the latest Eclipselink and MySql 5.1

EDIT 2:

I added synchronization... MASSIVE performance hit (to the point that it cannot be used). Going to gather all of the data up back on the main thread and then do the creations on that thread.

like image 732
TofuBeer Avatar asked Jun 07 '10 19:06

TofuBeer


3 Answers

Short sad answer is no the JPA API cannot do that for you. The whole API is more or less built around the optimistic principle of assuming things are going to work and throwing exceptions in the event of concurrent modification.

If this is happening often, there's likely some other component (whatever generates foo?) that could benefit from being made threadsafe, as perhaps an alternative to synchronizing around the query+create.

like image 130
Affe Avatar answered Sep 30 '22 13:09

Affe


Some "hack" to consider:

  • implement hashCode() and equals() based on the business key of the objects (not the generated id)
  • synchronize on:

    (obj.getClass().getName() + String.valueOf(hashCode())).intern()
    

Thus you will get locks only in the relevant cases.

like image 42
Bozho Avatar answered Sep 30 '22 13:09

Bozho


I think you will need to add a unique constraint on the fields that are used in "jpaQuery" so that the database can not create duplicate rows with the same criteria used in the contraints for that query. The calling code will need to trap the resulting exception that occurs as a result of the constraint violation (ideally it will be an EntityExistsException, but the spec is not clear on this case).

like image 42
Michael Barker Avatar answered Sep 30 '22 11:09

Michael Barker