views:

919

answers:

4

Is using the 'synchronized' keyword on methods in a Java DAO going to cause issues when used by a web application?

I ask because I have a multi-threaded stand alone application that needs the methods to by synchronized to avoid resource conflict, as seen here.

java.util.concurrent.ExecutionException: javax.persistence.PersistenceException: org.hibernate.HibernateException: Found shared references to a collection: com.replaced.orm.jpa.Entity.stuffCollection

What I am concerned about is that when a significant number of people try and use the application that the synchronized methods will block and slow the entire application down.

I am using a Spring injected JPA entity manager factory, which provides an entity manager to the DAO. I could technically remove the DAO layer and have the classes call the entity manager factory directly, but I enjoy the separation the DAO provides.

I should also note that I am being very careful not to pass around connected entity ORM objects between threads. I speculate that the resource conflict error comes about when accessing the DAO. I think multiple threads are going at the same time and try to persist or read from the database in non-atomic ways.

In this case is using a DAO going to do more harm then help?


A big piece of information I left out of the question is that the DAO is not a singleton. If I had been thinking lucidly enough to include that detail I probably wouldn't have asked the question in the first place.

If I understand correctly, Spring creates a new instance of the DAO class for each class that uses it. So the backing entity manager should be unique to each thread. Not sharing the entity manager is, as Rob H answered, the key thing here.

However, now I don't understand why I get errors when I remove synchronized.


According to this thread, the @PersistenceContext annotation creates a thread-safe SharedEntityManager. So you should be able to create a singleton DAO.

+1  A: 

If it needs to be synchronized for thread safety, then leave them there. The blocking is required anyway in that case. If the blocking is not required for the web application case, you can either:

  • leave it as is, since the performance hit when there is no contention on the lock is negligible, and insignificant when taken into account the expense of hitting the database.
  • Redesign it so that you add a synchronization layer for the standalone application case which protects the underlying unsynchronized DAO.

Personally, I would leave it as is and profile it to see if you need to refactor. Until then you are simply doing premature optimization.

Robin
That's a good point Robin. I might be falling into a trap trying to fix problems that I haven't had yet. I just wanted to make sure my approach wasn't egregiously wrong to other folk.
James McMahon
+1  A: 

You say you are not sharing entity objects across threads. That's good. But you should also make sure you're not sharing EntityManager objects (or Session objects in Hibernate) across threads either. Frameworks like Spring manage this for you automatically by storing the session in a thread-local variable. If you're coding your own DAOs without the help of a framework, you need to take precautions yourself to avoid sharing them.

Once you do this, there should be no reason to synchronize DAO methods because none of the conversational state will be shared across threads. This is critical for a highly concurrent web application. The alternative is that only one thread will be able to access the DAO at one time, assuming they all share the same DAO instance. Not good at all for throughput.

Rob H
Do you think it is better to just go back to direct access to the entity manager, which are injected into classes that need them by Spring, and let the framework do the work?
James McMahon
Not sure what you mean by "direct access to the entity manager". Normally, you access the entity manager in the DAOs only. You can easily get access to a thread-local entity manager by having your DAOs subclass JpaDaoSupport. (See the Spring doc for details.) Then you use Spring to inject your DAOs into your business service objects. So business services never work with the EM directly.
Rob H
I'll look into JpaDaoSupport. What I meant by direct access is having a spring injected entity manager in each class that needs database access. This is fairly simple to do with annotations.
James McMahon
One other point of clarification: Spring doesn't inject the EntityManager into DAOs or anything else. It injects the EntityManagerFactory, which IS thread-safe, and allows you (and Spring) to get an EntityManager on the fly for the current thread.
Rob H
@Rob H, You are absolutely right there, sorry for the ambiguous language.
James McMahon
Actually, looking at the Spring doc again, I think you are right! ;-) Apparently, Spring has support for "shared" entity managers that are thread-safe proxies for the actual transactional EntityManager and can be injected. I wasn't aware of this because I use native Hibernate--not JPA. Anyway, the important thing is just to make sure that the persistence context state is not shared across threads, which is what may be causing the exception you saw.
Rob H
I think I see where you going here, so I need to inject a EntityManagerFactory into my DAO as opposed to an EntityManager (from the Factory). Is there a way to pull this off simply using annotations or do I have to specify the injection in the Spring configuration?
James McMahon
Yes. Create a setter for the factory and annotate the setter with @Autowired. I've only tested this with native Hibernate--not JPA--but it should work. In the implementation of the setter, you can delegate to JpaDaoSupport's set method for the entity manager factory so you don't need to create a separate member variable for it.
Rob H
I'm so confused by JpaDaoSupport that I opened up another question, http://stackoverflow.com/questions/1112173/spring-daosupport-and-persistancecontext-entitymanager.
James McMahon
A: 

Rob/Nemo,

A long question, please bear with me.

I am using Spring+JPA for a web application. My team is debating over injecting EntityManagerFactory in the GenericDAO(a DAO based on Generics something on the lines provided by APPFUSE, we do not use JpaDaosupport for some reason) over injecting an EntityManager. We are using "application managed persistence".

The arguments against injecting a EntityManagerFactory is that its too heavy and so is not required, the EntityManager does what we need. Also, as Spring would create a new instance of a DAO for every web request(I doubt this) there are not going to be any concurrency issues as in the same EntityManager instance is shared by two threads.

The argument for injecting EFM is that its a good practice over all its always good to have a handle to a factory.

Overall, I am not sure which is the best approach, can you please enlighten me?

SB


Nemo, as you said : I have posted the question here...

http://stackoverflow.com/questions/1310087/injecting-entitymanager-vs-entitymanagerfactory

SB, You should post this up as a question because 1) that is how the site is design 2) it is impossible to post in-depth answers as a comment. If you delete this answer, feel free to post a link to your question as a comment to my question up above.
James McMahon
A: 

Nemo, as you said : I have posted the question here...

http://stackoverflow.com/questions/1310087/injecting-entitymanager-vs-entitymanagerfactory

SB
Thanks for posting up a separate question. I forgot you couldn't post comments with your low rep. Could you edit this answer in with your previous answer so we don't have two none answers here? Thanks.
James McMahon
I've gone ahead and edited it for you, could you delete this answer?
James McMahon