<< jqGrid JSON Java Model | Home | Groovy Java File Prepender >>

DRY CRUD DAOs with JPA

...and Spring

I wanted to shove a few more acronyms into the title of this article but I couldn't find anymore that were relevant. So I am currently working on a very small project using JPA and Hibernate. It has a very simple DAO layer and there are a handful of Model objects, POJO's, JavaBeans; whatever you young kids are calling them these days. Most everything in this application revolves around CRUD operations. So I created the following interface:

public interface BaseDAO<T> { public void insert(T t); public List<T> findAll(Class<T> objectClass); public void delete(T t); public void update(T t); }

Now I can define any domain specific DAO interface I need without repeating CRUD methods (DRY) like so:

public interface ProjectDAO extends BaseDAO<Project> { }

If there are any custom methods I need I can just define them in the ProjectDAO. If I find myself repeating the custom methods for every interface I can simply move them to the BaseDAO class. But it gets even easier. What about the impementations? Well, there is a base class for those as well.

public class JpaBaseDAO<T> extends JpaDaoSupport { public void insert(T t) { getJpaTemplate().persist(t); } public void delete(T t) { getJpaTemplate().remove(t); } public void update(T t) { getJpaTemplate().merge(t); } public List<T> findAll(Class<T> objectClass) { List<T> entities = new ArrayList<T>(); try { String s = "select c from " + objectClass.getSimpleName() + " c"; entities = getJpaTemplate().find(s); } catch (Exception e) { e.printStackTrace(); } return entities; } }

Probably the most questionable method here is findAll (thanks to Will Hartung for suggesting this method). JPA doesn't provide a way to get all the rows from a table without specifiying a bit of HQL. To make it generic we just get the class name from the objectClass and use it. This does assume your tables are named the same as your domain objects. But like I said, this is a very small project and this isn't a problem for me. And now the implmentation:

public class JpaProjectDAO extends JpaBaseDAO<Project> { }

I'm not sure how it could get much simpler. The nice thing is the base class and interface can be carried from project to project because they don't infer anything about the domain object. I'm sure folks are already doing this but I haven't seen a lot on line about it. Hope this helps someone. If anyone has any suggestions for improvements, especially concerning the findAll method, I'd love to hear them.



Re: DRY CRUD DAOs with JPA

We've been using this exact construct on the project I've been working on for several years. It started as Bean-managed persistence, then to straight DAOs, now to Hibernate (no JPA, yet). It works pretty well, for the most part.

In a previous version, prior to using Java 5, we had a code generator generate the repeated code, since it was markedly more difficult to implement without generics. That would also generate skeleton EJB session beans and all the crap required in EJB2. Now, with EJB3 and Java 5, we have so much less code.

Re: DRY CRUD DAOs with JPA

Very nice. Would it make more sense to make the base class abstract and provide a way to specify the class, either with a constructor or abstract method? That way you wouldn't have to pass in the class every time you call findAll().

Re: DRY CRUD DAOs with JPA

Well, I'm sure that could be done. I might have code up an example to see if it really makes sense. I'm afraid that it might cause a bit more code per implementation than what I was going for. I'll see what I come up with.

Re: DRY CRUD DAOs with JPA

You can do it this way in the base class:
public class JpaBaseDAO<T> extends JpaDaoSupport { 
    private Class<T> persistentClass;

    public JpaBaseDAO() {
      this.persistentClass = (Class<T>)((ParameterizedType)getClass().getGenericSuperclass()).getActualTypeArguments()[0];   
    }

    // etc
}
Then reference persistentClass whenever you need the class of the domain object. A little bit ugly, but it works :)

Re: DRY CRUD DAOs with JPA

We use the same technique as mogrify to save passing the class - works well. Also our table names differ from the entity names and this works fine: createQuery("SELECT o FROM " + persistanceClass.getName() + " AS o").getResultList();

Re: DRY CRUD DAOs with JPA

I'm using Spring. If the DAOs are instantiated via Spring XML, there's no way to pass in the type parameter (I assume). Maybe Spring's autowiring would handle it better - but there are other reasons why I can't use that. So - I'm stuck having to pass the class into a constructor.

Re: DRY CRUD DAOs with JPA

That's pretty cool. Thanks for the tip.

Re: DRY CRUD DAOs with JPA

That's almost identical to my BaseDAO. Mine also has a findByPrimaryKey method. To add such a method, include an extra type parameter for the PK:
BaseDAO<T, PK extends Serializable>
... and then the method looks like this:
public T findByPK(Class<T> objectClass, PK primaryKey) {
   return entityManager.find(objectClass, primaryKey);
}
(Adapted to your example. In my version, I don't pass the objectClass into the findByPK method; instead I pass it in via the constructor). I don't bother to subclass the BaseDAO unless I need to add methods. Chris.

Re: DRY CRUD DAOs with JPA

Are you using Spring? I tend to leave my constructors empty but I'm not opposed to it if it makes it more robust. Just trying to stay as simple as possible.

Re: DRY CRUD DAOs with JPA

Yes, I'm using Spring. I don't mind having to add the constructor argument in the Spring config. I agree with Karl that its nicer to not have to pass-in the class as a method param - especially when you have multiple methods on the BaseDAO that would require the class. By the way, since you're using Hibernate as your JPA provider, you could probably use the criteria API instead of freeform HQL. Its a wee bit cleaner:
   Session hibernateSession = (Session) getEntityManager().getDelegate();
   Criteria criteria = hibernateSession.createCriteria(objectClass);
   //Don't actually add any criteria
   return criteria.list()

Re: DRY CRUD DAOs with JPA

HI wonder if is correct/good practice to extend from EntityManagerFactory or EntityManager instead of JpaDaoSuport is there another way to do the same but witout JpaDaoSuport (I would like to dont use a library of spring )

Add a comment Send a TrackBack