With the arrival of the first commercial Java EE 5 application server (Weblogic 10) a production quality EE option to manage JPA is available. As a matter of fact, day by day I'm more comfortable with Glassfish but unfortunately it lacks the sales punch of Bea or IBM. As an architect it's a good question to determine what are the differences between a EJB3 approach versus a pure (lightweight) Spring approach and how and where to use one or the other.
As a side note, if the deployment target will be Weblogic 10+ then reading the Pitchfork Project documentation is advisable as some nice tricks can be performed.
First of all let's describe how a SLSB EJB3 likes to manage persistence. As you can imagine the entity classes stay the same and the bean will serve as a facade. Everything is then packed in an EAR file that usually contains a WAR file (for the screens), a JAR file for the EJBs and another JAR file inside the lib directory (for common libraries) with the entities. A simple code example of the implementation class:
As a side note, if the deployment target will be Weblogic 10+ then reading the Pitchfork Project documentation is advisable as some nice tricks can be performed.
First of all let's describe how a SLSB EJB3 likes to manage persistence. As you can imagine the entity classes stay the same and the bean will serve as a facade. Everything is then packed in an EAR file that usually contains a WAR file (for the screens), a JAR file for the EJBs and another JAR file inside the lib directory (for common libraries) with the entities. A simple code example of the implementation class:
public abstract class AbstractCRUD implements CRUD {
protected abstract EntityManager getEntityManager();
public void create(Object entity) {
getEntityManager.persist(entity);
}
...
}
@Stateless
@Local(GenericCRUD.class)
@TransactionAttribute(TransactionAttributeType.REQUIRES_NEW)
public class GenericCRUDImpl extends AbstractCRUD implements Serializable {
@PersistenceContext private EntityManager em;
public EntityManager getEntityManager() {
return em;
}
}
protected abstract EntityManager getEntityManager();
public void create(Object entity) {
getEntityManager.persist(entity);
}
...
}
@Stateless
@Local(GenericCRUD.class)
@TransactionAttribute(TransactionAttributeType.REQUIRES_NEW)
public class GenericCRUDImpl extends AbstractCRUD implements Serializable {
@PersistenceContext private EntityManager em;
public EntityManager getEntityManager() {
return em;
}
}
The code above is simple. All the implementation logic has been moved to an abstract class (reusable later). The EJB just indicates how it should be deployed, how to obtain the EntityManager from the context or how to mange security and transactions. It's easy to see how much EJB3 has improved the developing experience. As Spring the EJB3 specification uses annotations and dependency injection. Here's a little list with pros/cons of this EJB3 approach:
Spring approach is different. As always they enforce a lightweight, POJO based, solution (EJBs now are as similar to a POJO as possible). They offer three distinct possibilities (and the best of all is that they're easily intechangeable by configuration):
| Pros | Cons |
|---|---|
| Extremely easy to code | Forces an EAR deployment |
| Extensive use of annotations | DI just for container managed classes |
| Declarative transactions and security | Several business layers |
| Optional deployment descriptors | Looses lazy loading when accessed remotely |
| Local or remote deployments | |
| Standards based |
Spring approach is different. As always they enforce a lightweight, POJO based, solution (EJBs now are as similar to a POJO as possible). They offer three distinct possibilities (and the best of all is that they're easily intechangeable by configuration):
- The JPATemplate: Specialy useful when upgrading from other templates (ie Hibernate). Here's an use explanation by Inteface 21 themselves. It has two downsides IMHO, it's Spring dependent (invasive) and it uses inline functions (which I tend to dislike)
- The LocalContainerEntiyManagerFactoryBean: Or converting the Spring context in a JPA container (!). As in the first case it allows declarative transactions and it adds the capability to parse @PersistenceContext for EntityManager injection.Here's another explanation
- Leveraging the JTA (container) EntityManager. Spring will integrate with the AS and use the containers facilities.
<persistence-unit-ref>
<persistence-unit-ref-name>persistenceUnit</persistence-unit-ref-name>
<persistence-unit-name>persistenceEJBPU</persistence-unit-name>
</persistence-unit-ref>
<persistence-unit-ref-name>persistenceUnit</persistence-unit-ref-name>
<persistence-unit-name>persistenceEJBPU</persistence-unit-name>
</persistence-unit-ref>
The EntityManager should be published in the registry by now so Spring can get a hook. Add the following to the XML configuration:
<jee:jndi-lookup id="entityManagerFactory" jndi-name="java:comp/env/persistenceUnit"/>
<bean class="org...jpa.support.PersistenceAnnotationBeanPostProcessor"/>
<bean id="transactionManager" class="org...jta.JtaTransactionManager">
<tx:annotation-driven/>
<bean class="org...jpa.support.PersistenceAnnotationBeanPostProcessor"/>
<bean id="transactionManager" class="org...jta.JtaTransactionManager">
<tx:annotation-driven/>
That's all the configuration that is need. Two steps are still missing, the first one making your controllers (or beans in general) transactional and the second inject the EM into the DAO/s. To solve the first issue the easiest way is to annotate the bean with @Transactional, Spring's magic will do the rest and the bean will join/create a JTA transaction. The second is solved by using the @PersistenceContext annotation.
public class SpringCRUDImpl extends AbstractCRUD {
private EntityManager em;
@PersistenceContext
public void setEntityManager(EntityManager em) {
this.em = em;
}
protected EntityManager getEntityManager() {
return em;
}
}
private EntityManager em;
@PersistenceContext
public void setEntityManager(EntityManager em) {
this.em = em;
}
protected EntityManager getEntityManager() {
return em;
}
}
As you see it's again very easy as the actual code is the same as the EJB3, just the way to get the EntityManager varies.
Posted in: