How to define named queries at runtime with JPA 2.1
Take your skills to the next level!
The Persistence Hub is the place to be for every Java developer. It gives you access to all my premium video courses, 2 monthly Q&A calls, monthly coding challenges, a community of like-minded developers, and regular expert sessions.
Defining a static named query via the @NamedQuery annotation is the most common way in JPA. It has the main advantage that the persistence provider can compile and validate the query at start-up time. But you also have to define the query statically at compile time.
OK, you can still define a dynamic query at runtime but how do you handle all the queries that can’t be defined at compile time but are static as soon as the application gets started (or any other point in time)?
In these cases, you can use the programmatic definition of a named query which was introduced in JPA 2.1. By doing this, you can define or change a named query and most of its settings at runtime.
If you want to learn more about the other features introduced in JPA 2.1, have a look at JPA 2.1 – 12 features every developer should know and make sure to download the New Features in JPA 2.1 cheat sheet from the Free Member Library.
3 steps to define a named query at runtime
There are three things you need to do to create a named query at runtime:
- Create a Query. This can be done as a JPQL, native or criteria query. You can also define additional hints and settings for the query.
- Find a name for your query that is unique within your persistence unit. If there is already a named query defined for the name, the query will be updated.
- Use the Query and name to call the addNamedQuery(String name, Query query) method on the EntityManagerFactory.
Define a simple named query
The following code snippet shows how to create a simple named query which selects all authors who have written a book with a title that contains a specific String.
// define the named query Query q = this.em.createQuery("SELECT a FROM Book b JOIN b.authors a WHERE b.title LIKE :title GROUP BY a"); this.em.getEntityManagerFactory().addNamedQuery("selectAuthorOfBook", q);
As you can see, the query looks similar to a query that you provide in a @NamedQuery annotation. It defines a JPQL query and uses a named query parameter. Setting the parameter value is not part of the definition.
The named query is called in the same way as any other query defined by a @NamedQuery annotation. The createNamedQuery method is called with the name of the query, the required parameters are set by calling the setParameter method and the result of the query is requested by calling the getResultList method.
// call the named query TypedQuery<Author> nq = this.em.createNamedQuery("selectAuthorOfBook", Author.class); nq.setParameter("title", "%Java%"); List<Author> authors = nq.getResultList();
Define additional settings for a query
A named query can define more than just the query String. Any configuration of the query except the parameter binding will be part of the named query. You can for example define a SqlResultSetMapping or an entity graph that shall be used when executing the named query.
In the following code snippet, I create a named query based on a native SQL statement, specify a SqlResultSetMapping that maps the query result into a POJO and limit the result to the first 5 rows.
// define the named query Query q = this.em.createNativeQuery("SELECT a.id, a.firstName, a.lastName FROM Author a", "AuthorValue"); q.setFirstResult(0); q.setMaxResults(5); this.em.getEntityManagerFactory().addNamedQuery("selectAuthorOfBook2", q);
You can call this named query in the same way as the previous examples. You call the createNamedQuery method of the EntityManager to create the query and call the getResultList method to send it to the database and get the result.
// call the named query Query nq = this.em.createNamedQuery("selectAuthorOfBook2"); List<AuthorValue> authors = nq.getResultList();
The programmatic definition of a named query can be useful, if it can’t be statically defined at compile time but will not change after application start-up or any other given point in time. This can be done via the addNamedQuery method of the EntityManagerFactory, which creates a named query based on a Query object with all its settings except the actual parameter bindings. The named query is used in the same way as any other named query defined by a @NamedQuery annotation.
Before you leave, join the free Thoughts on Java Library and download your “New Features in JPA 2.1” cheat sheet, which provides all you need to remember about this and other features introduced in JPA 2.1.