views:

2312

answers:

5

It seems like most examples of JPA/Hibernate entity bean classes I've seen do no explicit synchronization. Yet, it is possible to call getters/setters on those objects in the context of building up a transaction. And it's possible for those methods to be called across multiple threads (although maybe that's unusual and weird).

It seems like if it is built up across multiple threads then it's possible for changes to object state to be lost, which would be sad.

So, is leaving out synchronization best practice? Is the Hibernate instrumented code taking care of proper synchronization for me?

As an example:

@Entity
public class Ninja {
  @Id @GeneratedValue
  private Long id;

  @Column 
  private String name;

  @Column
  private int throwingStars;

  public Ninja() {}
  public int getThrowingStars() { return throwingStars; } 
  public void addThrowingStar() { throwingStars += 1; }
}

Do the throwing star methods need synchronization? I sure don't want my ninja to lose any throwing stars.

+2  A: 

"And it's possible for those methods to be called across multiple threads (although maybe that's unusual and weird)."

I can speak for hibernate ONLY since I don't have much experience with JPA. The object on which you call set is NOT always the same object on which you call get. When Hibernate is loading the Object for you, it gets to call the set* methods and then you're(and your threads) calling get the rest of the time.

Again, when you're(i.e, your writer threads) modifying an existing Object and then saving it again, you need to protect access to the object(so that other reader/writer threads don't read dirty data).

anjanb
So you are expecting to see synchronization in the entity class in the later case? Or are you expecting to see synchronization at a higher level coordinated by the app?
Alex Miller
+1  A: 

The object probably is not the same for the 2 threads. Supposing your threads use a session factory to access the db, the objects you get from the session should be viewed as 'independent' (I believe hibernate creates 'fresh' objects for each get(), unless they are in the session).

As for your stars question, it's the same when two persons fetch the same row from a DB, the DB 'ACID' properties will make sure that each operation is atomic, so if you remove a star from a ninja in Thread t1 and commit, Thread t2 will read the committed values of t1.

Alternatively, you can ask hibernate to lock the row of the relevant ninja in T1, so that even if T2 asks for the row it will have to wait until T1 commits or aborts.

Miguel Ping
A: 

JPA/Hibernate entities are POJOs. Hibernate and any JPA-Provider does not change runtime sematics.

So if you would have concurrency issues with a simple POJO, you will also have them with your entity!

In all the Systems I have seen the domain model is not threadsave, entity instances are not accessed by several threads.

However you can have several instances of the same entities concurrently. In this case synchronization is done over the DB. The patterns here are Optimistic and Pessimistic Locking. Hibernate and JPA can help you with the realization of these patterns.

jbandi
A: 

The best practice for your problem is Optimistic Locking:

From the Java Persistence API (JPA) specification (Chapter 3.4.1):

Optimistic locking is a technique that is used to insure that updates to the database data corresponding to the state of an entity are made only when no intervening transaction has updated that data for the entity state since the entity state was read. This insures that updates or deletes to that data are consistent with the current state of the database and that intervening updates are not lost.

You need to add an @Version annotation to your class and a column in the DB table.

+1  A: 

In my opinion you should NEVER share domains objects across threads. In fact I typically share very little between threads as such data must be protected. I have built some large/high performance systems and have never had to break this rule. If you need to parallelize work then do so but NOT by sharing instances of domain objects. Each thread should read data from the database, operate on it by mutating/creating objects and then commit/rollback the transaction. Work pass in/out should be value/readonly objects generally.