I can quickly describe both patterns. The Generic DAO pattern uses Java Generics so you don't have to repeat the same CRUD-code over and over again to handle Entities. It's made for Hibernate but as you will see later, it works equally well in JPA.
The EAO pattern basically introduces an extra layer of abstraction. There are lots of discussions all over the internet whether the DAO (or in our case: EAO) pattern has died with the inception of EJB 3.0, where you can simply inject an EntityManager to your EJBs. Personally I have mixed feelings about it but I do accept the main argument given by the book, namely: What if you ever want to change persistence technology? You will probably end up rewriting all your EJBs.
However, the pattern as shown in the book left me wondering if I am not just writing lot's and lot's of code for nothing and thus the idea of merging two patterns was born.
So let's get started right away...
First of all, we need a generic interface for our EAO:
Subsequently we build a generic EAO superclass:
public interface BaseEAO<T, PK extends Serializable> {
void create(T t);
T read(PK pk);
T update(T t);
void delete(T t);
T refresh(T t);
}
In fact, if this class wouldn't be declared abstract, you'd be good to go already (provided the class using it has a type-level @PersistenceContext annotation, I'll explain this later, though...).
public abstract class GenericEAO<T, PK extends Serializable> implements BaseEAO<T, PK> {
private static final String ENTITY_MANAGER_NAME = "java:comp/env/myPU";
private Class<T> type;
protected EntityManager em;
public GenericEAO(Class<T> type) {
this.type = type;
this.em = getEntityManager();
}
public void create(T t) {
em.persist(t);
}
public T read(PK pk) {
return em.find(type, pk);
}
public T update(T t) {
return em.merge(t);
}
public void delete(T t) {
em.remove(em.merge(t));
}
public T refresh(T t) {
if (!em.contains(t)) {
t = em.merge(t);
}
em.refresh(t);
return t;
}
protected EntityManager getEntityManager() {
try {
InitialContext ctx = new InitialContext();
return (EntityManager) ctx.lookup(ENTITY_MANAGER_NAME);
} catch (NamingException e) {
System.out.println("Unable to obtain EntityManager. This call will fail!");
return null;
}
}
}
Create an interface for the EAO which we create in just a second:
And now let's implement an actual EAO which can be used in a Session Bean.:
public interface FieldEAO extends BaseEAO<Field, Integer> {
List<Field> findAllByType(String type);
}
As you can see, we didn't redo all the CRUD-operations, however, we did add a findAllByType-method which is specific to this Entity.
public class FieldEAOImpl extends GenericEAO<Field, Integer> implements FieldEAO {
public FieldEAOImpl() {
super(Field.class);
}
public List<Field> findAllByType(String type) {
Query q = em.createQuery("select f from Field f where f.type = :type");
q.setParameter("type", type);
@SuppressWarnings("unchecked")
List<Field> list = q.getResultList();
return list;
}
}
Now this is starting to look like something useful, however, we aren't quite there yet. Since all the classes so far are simple POJOs, the EntityManager-lookup in the InitialContext would fail. Why? Well, no persistence context has been created. We can do this by the previously mentoined type-level annotation. We localize this to a Stateless Session Bean which coincidentally also works as a factory, an EAO factory, to be specific:
And naturally we need a local interface for this bean:
@Stateless
@PersistenceContext(name = "myPU", unitName = "myPU")
public class EAOFactoryBean implements EAOFactory {
public FieldEAO getFieldEAO() {
return new FieldEAOImpl();
}
}
As you can see, the bean uses the @PersistenceContext annotation, which creates the persistence context for the EAO to use it.
@Local
public interface EAOFactory {
FieldEAO getFieldEAO();
}
Finally, some EJB might want to use it:
And it's local interface:
@Stateless
public class FieldFacadeBean implements FieldFacade {
@EJB
private EAOFactory eaoFactory;
public void createField(Field f) {
FieldEAO eao = eaoFactory.getFieldEAO();
eao.create(f);
}
public List<Field> findAllByType(String type) {
FieldEAO eao = eaoFactory.getFieldEAO();
return eao.findAllByType(type);
}
}
Well, that's about it. Thanks to the excessive use of interfaces and the factory, you will easily be able to replace the JPA-specific implementation with any other data-access implementation
@Local
public interface FieldFacade {
public void createField(Field f);
public List<Field> findAllByType(String type);
}
Note that you may want to expose the EntityManager's flush() method in the GenericEAO-class since there are cases where you may need it and there isn't any other way to get the EntityManager in the current design.
I definitely wouldn't consider it a best practice just yet and this pattern definitely has imperfections. If you have suggestions, questions or criticism, I'd love to know about it so leave a comment.
NOTE: Erik asked in a comment whether all entities will be detached. After getting a little uncertain if there is a previously undiscovered flaw in the pattern I set up a little test case and it does behave as expected. While the entities are in the scope of a transaction they will remain attached until the transaction commits (or aborts).
3 comments:
Hi, I'm currently using your'e pattern in a project, and it works greate! Nice to see some concret examples of using the EAO! Thanks.
That's good to hear. I am actually planning to refine the pattern a little bit before my next project so check back in a couple of weeks for some updates :)
I will! :) Just a quick question. In "EJB 3 in Action" the use of a Session Facade is descibed. Just wondering if you use that with the pattern, and if so, how do you handle detatchment? With your pattern the entites are detatched when they are returned (right?). Would using an EntityManager in the facade, and merging there be a solution? Thinking in particular about retriving lazy loaded references etc.
Post a Comment