Both Hibernate and JPA support named queries and I would typically recommend keeping them inside your DAOs.
However, using a typesafe Generic DAO implementation, you can have one class support all of your CRUD operations, and use named queries to support finder methods. But this approach requires that your service/manager layer know your named queries to submit to your generic finder method. To me this smells bad, I don’t want my service layer knowing about my DAO named queries, even if its only by name.
Here is my Generic DAO Interface:
1: public interface GenericDao<T, PK extends Serializable> {
2: // ... state methods (persist, save, remove)
3:
4: public T findByPk(PK pk);
5: public List<T> findAll();
6:
7: public T findInstanceByNamedQueryPositionalParameter(
8: String queryName, QueryParameter... args);
9:
10: // .. Other named query methods supporting multiple
11: // and named parameters
12: }
The implementation of my GenericDao uses simple JPA calls so I won’t include it here.
Here is my User model object with JPA annotations that the Generic DAO can work on:
1: @Entity
2: @Table(name = "USER")
3: @NamedQueries( { @NamedQuery(name = "user.findByName",
4: query = "select u from User u where u.username like ?1") })
5: public class User extends BaseObject implements Serializable {
6: @Column(name = "USERNAME", nullable = false, unique = true)
7: private String username;
8:
9: // … other properties
10: }
The primary key Long ID is defined on the BaseObject. The number of named queries in a real model object will likely be larger.
I can then create a Spring User DAO bean using my Generic DAO implementation, telling it which model class it will be typed as.
1: <bean id="userDao"
2: class="dao.jpa.GenericDaoJpa">
3: <constructor-arg>
4: <value>model.User</value>
5: </constructor-arg>
6: </bean>
I can use this bean in my service layer like this:
1: User user = userDao.findInstanceByNamedQueryPositionalParameter(
2: "user.findByName", new QueryParameter("user"));
Of course I want to have my cake and eat it too, because I want to say userDao.findByUserName(“user”)
Which means I either need a User DAO class definition (which sadly means a DAO for every model that needs finders) or an elegant AOP solution. Per Mellqvist has a solution to provide this using Spring AOP introductions which is slick as snot, but I fear that this approach will confuse the heck out of my junior developers, and they will likely spend an entire day searching for the definition of userDao.findByUserName which doesn’t exists. Since my developers don’t have the luxury of walking down to my desk to ask for clarification, I think I’m stuck having an explosions of DAO classes. At least the implementation of the finder methods will be simple
Here is my User DAO interface with the finder method:
1: public interface UserDao extends GenericDao<User, Long> {
2: public User findByUserName(String userName);
3: }
User DAO implementation
1: public class UserDaoJpa extends GenericDaoJpa<User, Long> implements UserDao {
2: public User findByUserName(String userName) {
3: return findInstanceByNamedQueryPositionalParameter("user.findByName",
4: new QueryParameter(userName));
5: }
6: }
And the Spring bean definition would change to:
1: <bean id="userDao"
2: class="dao.jpa.UserDaoJpa"/>
Now my code can all the friendly findByUserName method on the User DAO:
User user= userDao.findByUserName(userName);
I know that I’m working against the “Don’t repeat the DAO” movement, but my feeling is that my team will be more productive even though they end up creating more boilerplate code. As AOP gains adoption, I think the introductions approach will be a better choice, maybe I can submit curriculum guidance to Indian engineering colleges. I do have hope though, now that C# 3.0 has introduced extension methods, developers from Java and C# will slowly become comfortable not seeing the definition of the methods they call in the class definition.
References
- “Don’t repeat the DAO!” http://www.ibm.com/developerworks/java/library/j-genericdao.html (Per Mellqvist , developerWorks, May 2006): Presents a Spring AOP generic DAO implementation using Hibernate
- “Defining Your Object Model with JPA” http://www.theserverside.com/tt/articles/article.tss?l=JPAObjectModel (Chris Maki, TheServerSide.com, September 2007) Generic DAO implementation using JPA
I love generic DAOs. We use them heavily and they save us a bunch of time.
However I’m not sure about the named queries. They are certainly clever and flexible. However to make a sweeping generalization strings are evil. They lead to runtime bugs (but of course with unit testing you catch these prior to release).
I also question whether the available queries or required parameters are obvious. Intellisense is a crutch but it’s also a time saver. Would some abstracted specification based query language not be a better solution?
I really like it also,
and to remove the boilerplate you can use a dynamic proxy like I did it here
http://freddy33.blogspot.com/2007/07/jpa-namedqueries-and-jdbc-40.html
Small amount of code with some annotations.