Named queries are evil

  • Post category:Java / JEE / JPA

The JPA specification has the concept of named queries which is an attempt to ‘Don’t Repeat Yourself’ (DRY), but in my opinion it is more a ‘Mistake By Specification’.

The fact that a query is written as a string, and therefore is not compiler checkable, is a missed chance (solutions like QueryDSL offer great alternatives here), but not the point I would like to make. My issue is with the ‘named’ part.

The link to named queries above has the following example:

@NamedQuery(
    name="findAllEmployeesByFirstName",
    queryString="SELECT OBJECT(emp) FROM Employee emp WHERE emp.firstName = :firstname"
)

What you see here is that there is a definition of a query (usually located at the top of the Entity class) and somewhere else the query is used to read employees from the database, like so:

Query queryEmployeesByFirstName = em.createNamedQuery("findAllEmployeesByFirstName");
queryEmployeeByFirstName.setParameter("firstName", "John");
Collection employees = queryEmployeesByFirstName.getResultList();

The problem is that the named query in no way enforces or even specifies the parameters it requires. In this case it requires one, and its name of course suggest that, but this is a very thin and unstable relation, and not something I would like to base the stability of my code upon. After all, we all know how easy the name may go out-of-sync to the contents of the query.

What named queries try to prevent is that the query text is present on multiple locations. But the query and the required parameters are tightly related, basically they are a single unit which should not be split.

Because you need Java code anyhow to execute the JPQL query, I believe it is much better to wrap the query text and the parameters into a single method, where the query parameters are identical to the method parameters.

public Collection<Employees> findAllEmployeesByFirstName(String firstName) {
    Query query = em.createQuery("SELECT OBJECT(emp) FROM Employee emp WHERE emp.firstName = :firstname");
    query.setParameter("firstName", firstName);
    Collection<Employees> employees = (Collection<Employees>)query.getResultList();
    return employees;
}

This puts the query text and the parameters closely together, making it easier to keep them in sync, and (because the query text cannot be reused outside this method) ensuring that any change in the parameters is compiler checkable. It does not really matter if such a finder method is placed in a DAO object, or in a static method on the entity (emulating a named query). What ever suits your taste.

Having argued that named queries are a bad idea, but solely because you need to keep the query and parameters together, could lead to the approach of just scattering queries through out the code, as long as the parameters are there as well. And conceptually this is fine; the premises that ‘the EntityManager is the DAO’ would in fact encourage this. However, there is an additional advantage of moving queries into finder methods; domain level security. Domain level security becomes important when users can have different permissions on different instances of the same class. For example an user in a forum may have edit permissions on his own posts, but he is not allowed to edit posts of another user. And the user also may not be allowed to read post that are part of a shielded forum. Implementing such security, for example using Spring Security – Access Control Lists (ACL), requires checking individual objects. Named queries in their intended use do not have a single place where they are executed -otherwise it would not make much sense using named queries-, and it will be very hard to make sure all query results are processed by the domain level security. But scattering queries all over the place is not very practical either, using DAO classes makes this much easier.

So, to summarize; do not use named queries, they’re evil. But because of future requirements maybe you should not use the EM directly at all and stick to DAO’s.

This Post Has 3 Comments

  1. Mario Voehl

    Thanks! Found this site again after a while…QueryDSL seems a good choice. Anyway queries look as they are constructed anew every time they are used. Hence real static queries with fixed structure such as named queries (or prepared statements as known from jdbc) could lead to a better execution performance.

  2. Mario Voehl

    If you hate it, do not write it on your own – let it be generated for you. There’s an Eclipse plug-in (anqu method) which will generate the boilerplate access method you have mentioned (static or into a bean – just as you like).

    anqu method can also generate test methods which ensure that the method and the query are still in sync. This is another evil caused by named queries: Changes in the query will not lead to compiler errors. All users of the query may be broken without any warning. To handle this problem you can use a special test cases.

    1. tbeernot

      Yes, the runtime behavior of named queries is another aspect. Your plugin is a great way to easily convert @NamedQuery to methods, but personally I use QueryDSL to enable compile time checking of the query. That is even better than at test time. Never the less, good work!

Leave a Reply to Mario Voehl Cancel reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.