Monday, June 6, 2011

Spring, JPA, Java EE, JBoss: deployment strategies

Last week I made a comment on Michał's post - which I strongly recommend, by the way - where I alluded to the possibility of using an entity manager factory created by the application server instead of using the local JPA entity manager factory beans. Since I have been asked to provide a more detailed description, I will do this in a separate post, as doing that within the comment space is rather difficult, especially when providing code samples. The context of this discussion is the JBoss Application Server, but most of the considerations apply for other Java EE-compliant application servers as well.

Automatic creation of entity manager factories in JBoss


When using Spring and JPA in a servlet container, developers can create an EntityManagerFactory by using a LocalEntityManagerFactoryBean or a LocalContainerEntityManagerFactoryBean. In such an environment, it is pretty much the only option available. In JBoss, they can still do that, or they can choose to rely on the application server to create such an EntityManagerFactory for them.

Typical JPA applications will contain a META-INF/persistence.xml file which contains the persistence unit definition. JBoss, as a full-fledged Java EE application server, will deploy the persistence unit (and therefore create a EntityManagerFactory), whenever such a file is present in the deployment. So, unless special steps are taken to prevent that from happening (more about that below), the EntityManagerFactory will come into existence automatically. As a side note, you are required to use JTA in this case, as Java EE does not support the deployment of persistence units with the RESOURCE_LOCAL setting. If you are worried about how JTA will affect your performance however, please note that JBoss will optimize transaction management in single-resource scenarios, so any overhead is hardly significant.

So, once you provide a META-INF/persistence.xml file with your application, there is no actual need to create a new EntityManagerFactory in Spring, but you still need to create a reference to the container-managed entity manager factory which you can further inject that into your DAOs, and you can do that through a JNDI lookup. In order to do so, you need to register your EntityManagerFactory in JNDI. Web applications can do this by adding this to their web.xml file.


Adding a persistence unit reference to web.xml

  persistence/dogs-emf
  Dogs

Once this is done, the entity manager bean definition can be:


And that is it. JBoss will deploy the persistence unit automatically and you can reference it directly, without the need for creating a new one.


What if I don't want to use the container-managed entity manager factory?

Since the persistence unit deployment process takes place automatically, if you don't want to use the container managed entity manager factory and instead opt for creating a Spring-based one, you need to prevent JBoss from doing the deployment. This can be done in a number of ways - either by renaming the persistence unit file and using the 'persistenceXmlLocation' atrribute as shown below.

Using an alternative persistence definition location

  


You can also use the META-INF/jboss-ignore.txt file as an alternative to renaming. Simply create such a file and include the line below.
WEB-INF/classes/META-INF/persistence.xml

The wrap-up


When using Spring and JPA on JBoss, you can opt between the following strategies:

  1. Container-managed entity manager factory + JTA transactions - requires the least amount of configuration to set up, and it is based on functionality supplied automatically by the application server. It is worth keeping in mind that in a clustering scenario, this option ensures that the configuration for distributing the secondary-level cache will be automatically provided by the application server. 
  2. Spring-managed entity-manager factory + JTA transactions - still using JTA, requires additional configuration for setting up the application server. This is an option to consider if your application uses @Transactional(readOnly=true), as container-managed EntityManagerFactories will not honour that option.
  3. Spring-managed entity-manager factory + local transactions - works in an identical way is an a servlet container.

7 comments:

  1. Hi Marius, thanks for this post. Nice read, interesting indeed. I'm glad we got your blog going again. :)

    I wish there was a book that explains all these possible scenarios and their pros and cons, it's proper JEE-complicated.

    Next thing I'm going to look at is JTA without Spring at all, vanilla JEE.

    ReplyDelete
  2. Michal,

    Thanks! Yes, it's been quite a while :)

    If you are interested in the plain Java EE, I strongly suggest you to take a look at Seam 3, especially the Persistence module.

    ReplyDelete
  3. Marius, that's exactly what I want to look into, but I'm not sure where to start as there seem to be no books on Seam 3 out there yet. The tutorials they offer didn't strike me as particularly comprehensive either.
    So if you're looking for ideas for new posts... ;)

    ReplyDelete
  4. Hi Marius,

    I've a problem with Spring 3.0.5, Spring Webflow 2.3.0 and JBoss AS7 that maybe is related to to your post (or not).

    I've a war application that has a service called from the flow xml:


    evaluate expression="proveedoresService.listProveedores(telefonoCriteria.telefono)" result="flowScope.proveedores"

    and that service is an annotated service.

    The war deploys fine, but when the action that uses the expression calling the service is called, an error ocurrs:
    org.springframework.binding.expression.PropertyNotFoundException: Property not found

    Caused by: org.springframework.expression.spel.SpelEvaluationException: EL1008E:(pos 0): Field or property 'proveedoresService' cannot be found on object of type 'org.springframework.webflow.engine.impl.RequestControlContextImpl'

    Could you please provide me some help. I've been looking for a long time in the forums etc with no success....
    Note: the app flows are identified ok (they work) but the @Service are not located....

    Thanks a lot (and sorry for posting so long question)

    ReplyDelete
  5. This comment has been removed by the author.

    ReplyDelete
  6. Marius, this is one of the clearest explanations I have found.

    I wanted to add something that is new in AS7. According to AS7 JPA Reference Guide you can tell JBoss to not look at the persistence.xml for the database info.

    Just add the following to the <properties> section of the persistence.xml.
    <property name="jboss.as.jpa.managed" value="false"/>

    ReplyDelete
  7. Marius, you explanation is so clear, simple, and easy to understand! It has been very helpful to me, thanks for the blog. I look forward to read more from you.

    Sneha.

    ReplyDelete