Enterprise Java

JPA Named Query Example

1. Introduction

In this article you will learn how to use the JPA @NamedQuery and @NamedNativeQuery annotations. We will start by introducing some general aspects about named queries, then creating simple examples, using both JPQL and native SQL queries, passing parameters to named queries and using vendor-specific query hints. You will then learn how to execute named queries and use optional elements to make use of vendor-specific query hints.

For developing the application presented in this article we used NetBeans IDE 8.1, Payara 4.1.1.154 (Full Java EE) application server and Apache Derby Server 10.11.1.2 (that comes bundled with Payara), but you can also change to use GlassFish application server instead of Payara. You will also need a JDK environment, 1.7 or 1.8.

The complete application can be downloaded from here.

2. What is a named query and why using them?

The first thing to keep in mind is that the Java Persistence API defines two types of named queries, JPQL named queries and native SQL named queries.

A JPQL named query is any JPQL query defined using the @NamedQuery annotation or using the named-query element in a mapping file. A native SQL named query is any SQL query defined using the @NamedNativeQuery annotation or using the named-native-query element in a mapping file.

The @NamedQuery and @NamedNativeQuery annotations can be used individually or can coexist on the class definition for an entity. The annotations defines the name of the query, as well as the query text. In addition, when using native SQL named queries, if the result type is an entity, the resultClass element or the result-class attribute of a named-native-query element may be used to indicate the entity class.

In a real application, you will probably need multiple named queries defined on an entity class. For this, you will need to place multiple @NamedQuery annotations inside a @NamedQueries annotation. Similarly, multiple @NamedNativeQuery annotations are placed inside a @NamedNativeQueries annotation to define multiple native SQL named queries on an entity class.

Why should you consider using named queries? There are a few advantages, listed below:

  • one consist in the fact that named query annotations are compiled and validated at startup time
  • the annotations will be processed only once, then executed at runtime in query form
  • the application avoids continuously parsing JPQL and generating SQL
  • named queries are easier to maintain than string literals embedded in your code
  • you can override existing named queries defined as an annotation using XML mapping files

3. How can I write named queries?

As mention before, there are two types of named queries, JPQL named queries and native SQL named queries. Moreover, each of them can be defined via annotations or in a mapping file. In this section, we will explore these four use-cases.

3.1 Writing JPQL named queries

A JPA @NamedQuery annotation requires two elements, name and query, as shown below:

package eclipselink.apachederby.entity;

// imports

@Entity
@Table(name = "atp_players")
@NamedQuery(
    name = "Player.findAllUnordered",
    query = "SELECT p FROM Player p")
public class Player implements Serializable {
    ...
}

The name element is used to refer to the query when using the EntityManager methods to create the query. The query element is used to specify a query string in the Java Persistence query language. The name of the query is scoped to the entire persistence unit and it must be unique. In order to respect this rule, a common practice is to prefix the name of the query with the name of the entity that is being returned, separated by a dot.

Multiple JPQL named queries can be specified using the @NamedQueries annotation, as follows:

package eclipselink.apachederby.entity;

// imports

@Entity
@Table(name = "atp_players")
@NamedQueries({
    @NamedQuery(
        name = "Player.findAllUnordered",
        query = "SELECT p FROM Player p"),
    @NamedQuery(
        name = "Player.findAllOrderedByName",
        query = "SELECT p FROM Player p ORDER BY p.name")
})
public class Player implements Serializable {
    ...
    @Column(name = "player_name")
    private String name;
    ...
}

A JPQL named query can also be defined in a mapping file using the named-query element, as follows:

<?xml version="1.0" encoding="UTF-8"?>
<entity-mappings version="1.0" xmlns="http://java.sun.com/xml/ns/persistence/orm"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://java.sun.com/xml/ns/persistence/orm http://java.sun.com/xml/ns/persistence/orm_1_0.xsd">

    <named-query name="Player.findAllUnorderedFromXML">
        <query>SELECT p FROM Player p</query>
    </named-query>
</entity-mappings>

The name of the query is specified as an attribute of the named-query element. The query string is specified within the query subelement of named-query. The name of the query must be unique, unless we want to override the query that was defined using an annotation.

When overriding a query, you have to be careful to override it only with a query that has the same result type, or the code that executes the query and processes the result might break.

To define multiple JPQL named queries in a mapping file, you can add multiple named-query elements, as follows:

<?xml version="1.0" encoding="UTF-8"?>
<entity-mappings version="1.0" xmlns="http://java.sun.com/xml/ns/persistence/orm"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://java.sun.com/xml/ns/persistence/orm http://java.sun.com/xml/ns/persistence/orm_1_0.xsd">

    <named-query name="Player.findAllUnorderedFromXML">
        <query>SELECT p FROM Player p</query>
    </named-query>

    <named-query name="Player.findAllOrderedByNameFromXML">
        <query>SELECT p FROM Player p ORDER BY p.name</query>
    </named-query>
</entity-mappings>

The XML mapping file is usually located in the /META-INF application folder and must be specified in the persistence.xml file, as follows:

<?xml version="1.0" encoding="UTF-8"?>
<persistence version="2.1" xmlns="http://xmlns.jcp.org/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/persistence http://xmlns.jcp.org/xml/ns/persistence/persistence_2_1.xsd">

    <persistence-unit name="EclipseLink_NamedQuery-ejbPU" transaction-type="JTA">
        <provider>org.eclipse.persistence.jpa.PersistenceProvider</provider>
        <mapping-file>META-INF/queries.xml</mapping-file>
        <class>eclipselink.apachederby.entity.Player</class>
        ...
    </persistence-unit>
</persistence>

3.2 Writing native SQL named queries

A JPA @NamedNativeQuery annotation requires two elements, name and query, as shown below:

package eclipselink.apachederby.entity;

// imports

@Entity
@Table(name = "atp_players")
@NamedNativeQuery(
    name = "Player.countAllwithSQL",
    query = "SELECT COUNT(*) AS total FROM atp_players")
public class Player implements Serializable {
    ...
}

The name element is used to refer to the query when using the EntityManager methods to create the query. The query element is used to specify the native SQL query. The name of the query must be unique. If the result type is an entity, the resultClass element can be used to indicate the entity class:

package eclipselink.apachederby.entity;

// imports

@Entity
@Table(name = "atp_players")
@NamedNativeQuery(
    name = "Player.findAllUnorderedWihSQL",
    query = "SELECT * FROM atp_players",
    resultClass = Player.class)
public class Player implements Serializable {
    ...
}

Multiple native SQL named queries can be specified using the @NamedNativeQueries annotation, as follows:

package eclipselink.apachederby.entity;

// imports

@Entity
@Table(name = "atp_players")
@NamedNativeQueries({
    @NamedNativeQuery(
        name = "Player.countAllwithSQL",
        query = "SELECT COUNT(*) AS total FROM atp_players"),
    @NamedNativeQuery(
        name = "Player.findAllUnorderedWihSQL",
        query = "SELECT * FROM atp_players",
        resultClass = Player.class)
})
public class Player implements Serializable {
    ...
}

A native SQL named query can also be defined in a mapping file using the named-native-query element, as follows:

<?xml version="1.0" encoding="UTF-8"?>
<entity-mappings version="1.0" xmlns="http://java.sun.com/xml/ns/persistence/orm"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://java.sun.com/xml/ns/persistence/orm http://java.sun.com/xml/ns/persistence/orm_1_0.xsd">

    <named-native-query name="Player.countAllwithSQLfromXML">
        <query>SELECT COUNT(*) AS total FROM atp_players</query>
    </named-native-query>
</entity-mappings>

The name of the query is specified as an attribute of the named-native-query element. The query string is specified within the query subelement of named-native-query. The name of the query must be unique, unless we want to override the query that was defined using an annotation. If the result type is an entity, the result-class attribute of a named-native-query element can be used to indicate the entity class.

To define multiple native SQL named queries in a mapping file, you can add multiple named-native-query elements, as follows:

<?xml version="1.0" encoding="UTF-8"?>
<entity-mappings version="1.0" xmlns="http://java.sun.com/xml/ns/persistence/orm"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://java.sun.com/xml/ns/persistence/orm http://java.sun.com/xml/ns/persistence/orm_1_0.xsd">

    <named-native-query name="Player.countAllwithSQLfromXML">
        <query>SELECT COUNT(*) AS total FROM atp_players</query>
    </named-native-query>

    <named-native-query name="Player.findAllUnorderedWihSQLfromXML"
            result-class="eclipselink.apachederby.entity.Player">
        <query>SELECT * FROM atp_players</query>
    </named-native-query>
</entity-mappings>

You are free to create one or multiple mapping files for your named queries, as long as you specify each of them in the persistence.xml file. For example, you can choose to create a mapping file for the JPQL named queries and another mapping file for the native SQL named queries, or you can define all named queries in the same mapping file.

4. How can I execute a named query?

Once defined with an annotation or in a mapping file, named queries are created by passing the name of the query to the EntityManager createNamedQuery() method. Depending on the result of the query, we can then call the getSingleResult() method or the getResultList() method defined in the Query interface. The following examples creates and executes the queries defined in the previous section:

package eclipselink.apachederby.ejb;

// imports

@Named("bean")
@Stateless
public class SampleBean {

    @PersistenceContext(unitName = "EclipseLink_NamedQuery-ejbPU")
    private EntityManager em;

    private List players;

    ...

    @SuppressWarnings("unchecked")
    public void findAllUnordered() {
        players = em.createNamedQuery("Player.findAllUnordered")
                .getResultList();
    }

    @SuppressWarnings("unchecked")
    public void findAllUnorderedFromXML() {
        players = em.createNamedQuery("Player.findAllUnorderedFromXML")
                .getResultList();
    }

    @SuppressWarnings("unchecked")
    public void findAllOrderedByName() {
        players = em.createNamedQuery("Player.findAllOrderedByName")
                .getResultList();
    }

    @SuppressWarnings("unchecked")
    public void findAllOrderedByNameFromXML() {
        players = em.createNamedQuery("Player.findAllOrderedByNameFromXML")
                .getResultList();
    }

    @SuppressWarnings("unchecked")
    public void findAllUnorderedWihSQL() {
        players = em.createNamedQuery("Player.findAllUnorderedWihSQL")
                .getResultList();
    }

    @SuppressWarnings("unchecked")
    public void findAllUnorderedWihSQLfromXML() {
        players = em.createNamedQuery("Player.findAllUnorderedWihSQLfromXML")
                .getResultList();
    }

    public int countAllwithSQL() {
        int total = (int) em.createNamedQuery("Player.countAllwithSQL")
                .getSingleResult();
        return total;
    }

    public int countAllwithSQLfromXML() {
        int total = (int) em.createNamedQuery("Player.countAllwithSQLfromXML")
                .getSingleResult();
        return total;
    }

    ...

}

The code is self-explanatory, each time a named query is executed, the result is kept in either a list of players, or into an int variable, when counting the number of players.

5. Using query parameters and query hints in named queries

JPA supports both named and positional parameters for JPQL queries. A JPQL named query with a named parameter can be defined as follows:

package eclipselink.apachederby.entity;

// imports

@Entity
@Table(name = "atp_players")
@NamedQuery(
        name = "Player.findByAge",
        query = "SELECT p FROM Player p WHERE p.age = :age")
public class Player implements Serializable {
    ...
    @Column(name = "player_age")
    private int age;
    ...
}

To pass the necessary parameter and execute the query, we can then use:

...
    @SuppressWarnings("unchecked")
    public void findByAge() {
        players = em.createNamedQuery("Player.findByAge")
                .setParameter("age", 27)
                .getResultList();
    }
...

A JPQL named query that uses positional parameters can be defined as follows:

package eclipselink.apachederby.entity;

// imports

@Entity
@Table(name = "atp_players")
@NamedQuery(
        name = "Player.findBetweenAge",
        query = "SELECT p FROM Player p WHERE p.age BETWEEN ?1 AND ?2")
public class Player implements Serializable {
    ...
    @Column(name = "player_age")
    private int age;
    ...
}

To pass the necessary parameters and execute the query, we can then use:

...
    @SuppressWarnings("unchecked")
    public void findBetweenAge() {
        players = em.createNamedQuery("Player.findBetweenAge")
                .setParameter(1, 24)
                .setParameter(2, 28)
                .getResultList();
    }
...

Additional query hints can also be specified to a named query. A hint is simply a string name and object value and each query may be associated with any number of hints. Query hints are specific to the persistence provider, EclipseLink for example, and are usually used to enable different performance optimizations for queries.

To specify a query hint for a JPQL or SQL named query defined using an annotation, we can use:

package eclipselink.apachederby.entity;

// imports

@Entity
@Table(name = "atp_players")
@NamedQuery(
    name = "Player.findBetweenAge",
    query = "SELECT p FROM Player p WHERE p.age BETWEEN ?1 AND ?2",
    hints = {
        @QueryHint(name = QueryHints.QUERY_RESULTS_CACHE, value = HintValues.FALSE)
    })
@NamedNativeQuery(
    name = "Player.findAllUnorderedWihSQL",
    query = "SELECT * FROM atp_players",
    resultClass = Player.class,
    hints = {
        @QueryHint(name = QueryHints.QUERY_RESULTS_CACHE, value = HintValues.FALSE)
    })
public class Player implements Serializable {
    ...
    @Column(name = "player_age")
    private int age;
    ...
}

Query hints can also be specified for named queries defined in a mapping file, as follows:

...
    <named-query name="Player.findAllUnorderedFromXML">
        <query>SELECT p FROM Player p</query>
        <hint name="eclipselink.query-results-cache" value="false"/>
    </named-query>

    <named-native-query name="Player.findAllUnorderedWihSQLfromXML"
                        result-class="eclipselink.apachederby.entity.Player">
        <query>SELECT * FROM atp_players</query>
        <hint name="eclipselink.query-results-cache" value="false"/>
    </named-native-query>
...

6. Testing the application

For this article, we have developed an EAR application, called NamedQuery_EclipseLink_and_ApacheDerby, which aims to illustrate the use-case presented in the previous section. The application contains two modules, an EJB module in which we will develop our EJB beans and entities and a WAR module needed to simply display our data in a web page.

Now let’s have a quick look to the WAR module. We will use the JavaServer Faces technology for the presentation layer of our application. There is nothing fancy here, there are no managed beans, just a simple .xhtml page which looks like below:

<?xml version='1.0' encoding='UTF-8' ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:h="http://java.sun.com/jsf/html">
    <h:head>
        <title>JPA @NamedQuery Examples</title>
    </h:head>
    <h:body>
        <h3>JPA @NamedQuery Examples (EclipseLink and ApacheDerby)</h3>
        <h:form>
            <h:commandButton action="#{bean.persistAction()}"
                             value="Populate database"/><br/>
            <h:commandButton action="#{bean.findAllUnordered()}"
                             value="findAllUnordered()"/>
            <h:commandButton action="#{bean.findAllUnorderedFromXML()}"
                             value="findAllUnorderedFromXML()"/>
            <h:commandButton action="#{bean.findAllUnorderedWihSQL()}"
                             value="findAllUnorderedWihSQL()"/>
            <h:commandButton action="#{bean.findAllUnorderedWihSQLfromXML()}" 
                             value="findAllUnorderedWihSQLfromXML()"/><br/>
            <h:commandButton action="#{bean.findAllOrderedByName()}"
                             value="findAllOrderedByName()"/>
            <h:commandButton action="#{bean.findAllOrderedByNameFromXML()}"
                             value="findAllOrderedByNameFromXML()"/>
            <h:commandButton action="#{bean.findByAge()}"
                             value="findByAge()"/>
            <h:commandButton action="#{bean.findBetweenAge()}"
                             value="findBetweenAge()"/>
        </h:form>
        <p>countAllwithSQL(): <h:outputText value="#{bean.countAllwithSQL()}"/></p>
        <p>countAllwithSQLfromXML(): <h:outputText 
                    value="#{bean.countAllwithSQLfromXML()}"/></p>
        <h:dataTable value="#{bean.loadData()}" var="t" border="1" 
                    rendered="#{facesContext.postback and bean.loadData().size() > 0}">
            <h:column>
                #{t.name}
            </h:column>
            <h:column>
                #{t.surname}
            </h:column>
            <h:column>
                #{t.age}
            </h:column>
            <h:column>
                #{t.birth}
            </h:column>
        </h:dataTable>
    </h:body>
</html>

Notice that CDI is used in order to reference #{bean} to the SampleBean session bean located in the eclipselink.apachederby.ejb package of our EJB module. When you press the “Populate database” button, it will call the persistAction() method and use a helper class (the Helper class inside the eclipselink.apachederby.helper package) in order to randomly generate some data and insert several players into the database.

Then, pressing the “findAllUnordered()” button will search for our list of players and display something like below:

Testing the application
Testing the application

Similarly, you can test the rest of the application. After each request is fired, when the page is rendered, it will also call the SampleBean.countAllwithSQL() and SampleBean.countAllwithSQLfromXML() methods and display the total number of players found in the database.

The persistence.xml file used for this application contains several configurations specific to Apache Derby that are highlighted below:

<?xml version="1.0" encoding="UTF-8"?>
<persistence version="2.0" xmlns="http://java.sun.com/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd">
 <persistence-unit name="EclipseLink_OneToOne-ejbPU" transaction-type="JTA">
  <provider>org.eclipse.persistence.jpa.PersistenceProvider</provider>
  <mapping-file>META-INF/queries.xml</mapping-file>
  <class>eclipselink.apachederby.entity.Player</class>
  <exclude-unlisted-classes>true</exclude-unlisted-classes>
  <properties>
   <property name="javax.persistence.jdbc.driver" value="org.apache.derby.jdbc.EmbeddedDriver"/>
   <property name="javax.persistence.jdbc.url" value="jdbc:derby:memory:sampleDB;create=true"/>
   <property name="javax.persistence.schema-generation.database.action" value="drop-and-create"/>
   <property name="javax.persistence.schema-generation.create-source" value="metadata"/>
   <property name="javax.persistence.schema-generation.drop-source" value="metadata"/>
  </properties>
 </persistence-unit>
</persistence>

These configurations specifies that the necessary tables will be created on the default schema (named sun-appserv-samples) when running our application. You can explore them by navigating to the Services tab in NetBeans IDE and connecting to the sun-appserv-samples database:

The application database
The application database

7. Conclusion

In this article we learned how to use the JPA @NamedQuery and @NamedNativeQuery annotations. Starting with some general aspects about named queries, we also write simple examples, using both JPQL and native SQL queries, passing parameters to named queries and use vendor-specific query hints. As a final step, we learned how to execute named queries and use optional elements to make use of vendor-specific query hints.

Download
You can download the full source code of this example here: JPA Named Query Example.
Subscribe
Notify of
guest

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

0 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments
Back to top button