© Mike Keith, Merrick Schincariol, Massimo Nardone 2018
Mike Keith, Merrick Schincariol and Massimo NardonePro JPA 2 in Java EE 8https://doi.org/10.1007/978-1-4842-3420-4_11

11. Advanced Queries

Mike Keith1 , Merrick Schincariol2 and Massimo Nardone3
(1)
Ottawa, Ontario, Canada
(2)
RR 3, RR 3, Almonte, Ontario, Canada
(3)
Helsinki, Finland
 

At this point, most of you will know enough about queries from what you have learned so far that you will likely not need any of the material from this chapter. In fact, roughly half of this chapter deals with using queries that will couple your application to a target database, so they should be used with some degree of planning and caution in any case.

The JPA version 2.2 did not introduce any new features related to advanced queries.

We start by going through the JPA support for native SQL queries and show how the results can be mapped to entities, non-entities, or simple data projection results. We then move on to stored procedure queries and explain how a stored procedure can be invoked from within a JPA application and how the results may be returned.

The second half of the chapter deals with entity graphs and how they can be used to override the fetch type of a mapping during a query. When passed as fetch graphs or load graphs, they provide great flexibility at runtime for controlling what state should get loaded and when.

SQL Queries

With all the effort that has gone into abstracting away the physical data model, both in terms of object-relational mapping and JP QL, it might be surprising to learn that SQL is alive and well in JPA. Although JP QL is the preferred method of querying over entities, SQL cannot be overlooked as a necessary element in many enterprise applications. The sheer size and scope of the SQL features, supported by the major database vendors, means that a portable language such as JP QL will never be able to fully encompass all their features.

Note

SQL queries are also known as native queries. EntityManager methods and query annotations related to SQL queries also use this terminology. While this allows other query languages to be supported in the future, any query string in a native query operation is assumed to be SQL.

Before discussing the mechanics of SQL queries, let’s first consider some of the reasons why a developer using JP QL might want to integrate SQL queries into their application.

First, JP QL , despite the enhancements made in JPA 2.0, still contains only a subset of the features supported by many database vendors. Inline views (subqueries in the FROM clause), hierarchical queries, and additional function expressions to manipulate date and time values are just some of the features not supported in JP QL.

Second, although vendors may provide hints to assist with optimizing a JP QL expression, there are cases where the only way to achieve the performance required by an application is to replace the JP QL query with a hand-optimized SQL version. This may be a simple restructuring of the query that the persistence provider was generating, or it may be a vendor-specific version that leverages query hints and features specific to a particular database.

Of course, just because you can use SQL doesn’t mean you should. Persistence providers have become very skilled at generating high-performance queries, and many of the limitations of JP QL can often be worked around in application code. We recommend avoiding SQL initially if possible and then introducing it only when necessary. This will enable your queries to be more portable across databases and more maintainable as your mappings change.

The following sections discuss how SQL queries are defined using JPA and how their result sets can be mapped back to entities. One of the major benefits of SQL query support is that it uses the same Query interface used for JP QL queries. With some small exceptions that are described later, all the Query interface operations discussed in previous chapters apply equally to both JP QL and SQL queries.

Native Queries vs. JDBC

A perfectly valid question for anyone investigating SQL support in JPA is whether it is needed at all. JDBC has been in use for years, provides a broad feature set, and works well. It’s one thing to introduce a persistence API that works on entities, but another thing entirely to introduce a new API for issuing SQL queries.

The main reason to consider using SQL queries in JPA, instead of just issuing JDBC queries, is when the result of the query will be converted back into entities. As an example, let’s consider a typical use case for SQL in an application that uses JPA. Given the employee id for a manager, the application needs to determine all the employees who report to that manager either directly or indirectly. For example, if the query were for a senior manager, the results would include all the managers who report to that senior manager as well as the employees who report to those managers. This type of query cannot be implemented by using JP QL, but a database such as Oracle natively supports hierarchical queries for just this purpose. Listing 11-1 demonstrates the typical sequence of JDBC calls to execute this query and transform the results into entities for use by the application.

@Stateless
public class OrgStructureBean implements OrgStructure {
    private static final String ORG_QUERY =
        "SELECT emp_id, name, salary " +
        "FROM emp " +
        "START WITH manager_id = ? " +
        "CONNECT BY PRIOR emp_id = manager_id";
    @Resource
    DataSource hrDs;
    public List findEmployeesReportingTo(int managerId) {
        Connection conn = null;
        PreparedStatement sth = null;
        try {
            conn = hrDs.getConnection();
            sth = conn.prepareStatement(ORG_QUERY);
            sth.setLong(1, managerId);
            ResultSet rs = sth.executeQuery();
            ArrayList<Employee> result = new ArrayList<Employee>();
            while (rs.next()) {
                Employee emp = new Employee();
                emp.setId(rs.getInt(1));
                emp.setName(rs.getString(2));
                emp.setSalary(rs.getLong(3));
                result.add(emp);
            }
            return result;
        } catch (SQLException e) {
            throw new EJBException(e);
        }
    }
}
Listing 11-1

Querying Entities Using SQL and JDBC

Now consider the alternative syntax supported by JPA, as shown in Listing 11-2. By simply indicating that the result of the query is the Employee entity, the query engine uses the object-relational mapping of the entity to figure out which result columns map to the entity properties and builds the result set accordingly.

@Stateless
public class OrgStructureBean implements OrgStructure {
    private static final String ORG_QUERY =
        "SELECT emp_id, name, salary, manager_id, dept_id, address_id " +
        "FROM emp " +
        "START WITH manager_id = ? " +
        "CONNECT BY PRIOR emp_id = manager_id";
    @PersistenceContext(unitName="EmployeeService")
    EntityManager em;
    public List findEmployeesReportingTo(int managerId) {
        return em.createNativeQuery(ORG_QUERY, Employee.class)
                 .setParameter(1, managerId)
                 .getResultList();
    }
}
Listing 11-2

Querying Entities Using SQL and the Query Interface

Not only is the code much easier to read but it also uses the same Query interface that can be used for JP QL queries. This helps to keep application code consistent because it needs to concern itself only with the EntityManager and Query interfaces.

An unfortunate result of adding the TypedQuery interface in JPA 2.0 is that the createNativeQuery() method was already defined in JPA 1.0 to accept a SQL string and a result class and return an untyped Query interface. Now there is no backward compatible way to return a TypedQuery instead of a Query. The regrettable consequence is that when the createNativeQuery() method is called with a result class argument one might mistakenly think it will produce a TypedQuery, like createQuery() and createNamedQuery() do when a result class is passed in.

Defining and Executing SQL Queries

SQL queries may be defined dynamically at runtime or named in persistence unit metadata, similar to the JP QL query definitions discussed in Chapter 7. The key difference between defining JP QL and SQL queries lies in the understanding that the query engine should not parse and interpret vendor-specific SQL. In order to execute a SQL query and get entity instances in return, additional mapping information about the query result is required.

The first and simplest form of dynamically defining a SQL query that returns an entity result is to use the createNativeQuery() method of the EntityManager interface, passing in the query string and the entity type that will be returned. Listing 11-2 in the previous section demonstrated this approach to map the results of an Oracle hierarchical query to the Employee entity. The query engine uses the object-relational mapping of the entity to figure out which result column aliases map to which entity properties. As each row is processed, the query engine instantiates a new entity instance and sets the available data into it.

If the column aliases of the query do not match up exactly with the object-relational mappings for the entity, or if the results contain both entity and non-entity results, SQL result set mapping metadata is required. SQL result set mappings are defined as persistence unit metadata and are referenced by name. When the createNativeQuery() method is invoked with a SQL query string and a result set mapping name, the query engine uses this mapping to build the result set. SQL result set mappings are discussed in the next section.

Named SQL queries are defined using the @NamedNativeQuery annotation. This annotation may be placed on any entity and defines the name of the query as well as the query text. Like JP QL named queries, the name of the query must be unique within the persistence unit. If the result type is an entity, the resultClass element may be used to indicate the entity class. If the result requires a SQL mapping, the resultSetMapping element may be used to specify the mapping name. Listing 11-3 shows how the hierarchical query demonstrated earlier would be defined as a named query.

@NamedNativeQuery(
    name="orgStructureReportingTo",
    query="SELECT emp_id, name, salary, manager_id, dept_id, address_id " +
          "FROM emp " +
          "START WITH manager_id = ? " +
          "CONNECT BY PRIOR emp_id = manager_id",
    resultClass=Employee.class
)
Listing 11-3

Using an Annotation to Define a Named Native Query

One advantage of using named SQL queries is that the application can use the createNamedQuery() method on the EntityManager interface to create and execute the query. The fact that the named query was defined using SQL instead of JP QL is not important to the caller. A further benefit is that createNamedQuery() can return a TypedQuery whereas the createNativeQuery() method returns an untyped Query.

Listing 11-4 demonstrates the reporting structure bean again, this time using a named query. The other advantage of using named queries instead of dynamic queries is that they can be overridden using XML mapping files. A query originally specified in JP QL can be overridden with a SQL version, and vice versa. This technique is described in Chapter 13.

@Stateless
public class OrgStructureBean implements OrgStructure {
    @PersistenceContext(unitName="EmployeeService")
    EntityManager em;
    public List<Employee> findEmployeesReportingTo(int managerId) {
        return em.createNamedQuery("orgStructureReportingTo",
                                   Employee.class)
                 .setParameter(1, managerId)
                 .getResultList();
    }
}
Listing 11-4

Executing a Named SQL Query

One thing to be careful of with SQL queries that return entities is that the resulting entity instances become managed by the persistence context, just like the results of a JP QL query. If you modify one of the returned entities, it will be written to the database when the persistence context becomes associated with a transaction. This is normally what you want, but it requires that any time you select data that corresponds to existing entity instances, it is important to ensure that all the necessary data required to fully construct the entity is part of the query. If you leave out a field from the query, or default it to some value and then modify the resulting entity, there is a possibility that you will overwrite the correct version already stored in the database. This is because the missing state will be null (or some default value according to the type) in the entity. When the transaction commits, the persistence context does not know that the state was not properly read in from the query and might just attempt to write out null or the default value.

There are two benefits to getting managed entities back from a SQL query. The first is that a SQL query can replace an existing JP QL query and that application code should still work without changes. The second benefit is that it allows the developer to use SQL queries as a method of constructing new entity instances from tables that may not have any object-relational mapping. For example, in many database architectures, there is a staging area to hold data that has not yet been verified or requires some kind of transformation before it can be moved to its final location. Using JPA , a developer could start a transaction, query the staged data to construct entities, perform any required changes, and then commit. The newly created entities will get written to the tables mapped by the entity, not the staging tables used in the SQL query. This is more appealing than the alternative of having a second set of mappings that maps the same entities (or even worse, a second parallel set of entities) to the staging tables and then writing some code that reads, copies, and rewrites the entities.

SQL data-manipulation statements (INSERT, UPDATE, and DELETE) are also supported as a convenience so that JDBC calls do not have to be introduced in an application otherwise limited to JPA. To define such a query, use the createNativeQuery() method, but without any mapping information. Listing 11-5 demonstrates these types of queries in the form of a session bean that logs messages to a table. Note that the bean methods run in a REQUIRES_NEW transaction context to ensure that the message is logged even if an active transaction rolls back.

@Stateless
@TransactionAttribute(TransactionAttributeType.REQUIRES_NEW)
public class LoggerBean implements Logger {
    private static final String INSERT_SQL =
        "INSERT INTO message_log (id, message, log_dttm) " +
        "VALUES(id_seq.nextval, ?, SYSDATE)";
    private static final String DELETE_SQL =
        "DELETE FROM message_log";
    @PersistenceContext(unitName="Logger")
    EntityManager em;
    public void logMessage(String message) {
        em.createNativeQuery(INSERT_SQL)
          .setParameter(1, message)
          .executeUpdate();
    }
    public void clearMessageLog() {
        em.createNativeQuery(DELETE_SQL)
          .executeUpdate();
    }
}
Listing 11-5

Using SQL INSERT and DELETE Statements

Executing SQL statements that make changes to data in tables mapped by entities is generally discouraged. Doing so may cause cached entities to be inconsistent with the database because the provider cannot track changes made to entity state that has been modified by data-manipulation statements.

SQL Result Set Mapping

In the SQL query examples shown so far, the result mapping was straightforward. The column aliases in the SQL string matched up directly with the object-relational column mapping for a single entity. It is not always the case that the names match up, nor is it always the case that only a single entity type is returned. JPA provides SQL result set mappings to handle these scenarios.

A SQL result set mapping is defined using the @SqlResultSetMapping annotation . It may be placed on an entity class and consists of a name (unique within the persistence unit) and one or more entity and column mappings. The entity result class argument on the createNativeQuery() method is really a shortcut to specifying a simple SQL result set mapping. The following mapping is equivalent to specifying Employee.class in a call to createNativeQuery():

@SqlResultSetMapping(
    name="EmployeeResult",
    entities=@EntityResult(entityClass=Employee.class)
)

Here we have defined a SQL result set mapping called EmployeeResult that may be referenced by any query returning Employee entity instances. The mapping consists of a single entity result, specified by the @EntityResult annotation, which references the Employee entity class. The query must supply values for all columns mapped by the entity, including foreign keys. It is vendor-specific whether the entity is partially constructed or whether an error occurs if any required entity state is missing.

Mapping Foreign Keys

Foreign keys do not need to be explicitly mapped as part of the SQL result set mapping. When the query engine attempts to map the query results to an entity, it considers foreign key columns for single-valued associations as well. Let’s look at the reporting structure SQL query again.

SELECT emp_id, name, salary, manager_id, dept_id, address_id
FROM emp
START WITH manager_id IS NULL
CONNECT BY PRIOR emp_id = manager_id

The MANAGER_ID, DEPT_ID, and ADDRESS_ID columns all map to the join columns of associations on the Employee entity. An Employee instance returned from this query can use the methods getManager(), getDepartment(), and getAddress(), and the results will be as expected. The persistence provider will retrieve the associated entity based on the foreign key value read in from the query. There is no way to populate collection associations from a SQL query. Entity instances constructed from this example are effectively the same as they would have been had they been returned from a JP QL query.

Multiple Result Mappings

A query may return more than one entity at a time. This is most often useful if there is a one-to-one relationship between two entities; otherwise, the query will result in duplicate entity instances. Consider the following query:

SELECT emp_id, name, salary, manager_id, dept_id, address_id,
       id, street, city, state, zip
FROM emp, address
WHERE address_id = id

The SQL result set mapping to return both the Employee and Address entities out of this query is defined in Listing 11-6. Each entity is listed in an @EntityResult annotation, an array of which is assigned to the entities element. The order in which the entities are listed is not important. The query engine uses the column names of the query to match against entity mapping data, not column position.

@SqlResultSetMapping(
    name="EmployeeWithAddress",
    entities={@EntityResult(entityClass=Employee.class),
              @EntityResult(entityClass=Address.class)}
)
Listing 11-6

Mapping a SQL Query That Returns Two Entity Types

Mapping Column Aliases

If the column aliases in the SQL statement do not directly match up with the names specified in the column mappings for the entity, field result mappings are required for the query engine to make the correct association. Suppose, for example, that both the EMP and ADDRESS tables listed in the previous example used the column ID for their primary key. The query would have to be altered to alias the ID columns so that they are unique:

SELECT emp.id AS emp_id, name, salary, manager_id, dept_id, address_id,
       address.id, street, city, state, zip
FROM emp, address
WHERE address_id = address.id

The @FieldResult annotation is used to map column aliases to the entity attributes in situations where the name in the query is not the same as the one used in the column mapping. Listing 11-7 shows the mapping required to convert the EMP_ID alias to the id attribute of the entity. More than one @FieldResult may be specified, but only the mappings that are different need to be specified. This can be a partial list of entity attributes.

@SqlResultSetMapping(
    name="EmployeeWithAddress",
    entities={@EntityResult(entityClass=Employee.class,
                            fields=@FieldResult(
                                       name="id",
                                       column="EMP_ID")),
              @EntityResult(entityClass=Address.class)}
)
Listing 11-7

Mapping a SQL Query with Unknown Column Aliases

Mapping Scalar Result Columns

SQL queries are not limited to returning only entity results, although it is expected that this will be the primary use case. Consider the following query:

SELECT e.name AS emp_name, m.name AS manager_name
FROM emp e,
     emp m
WHERE e.manager_id = m.emp_id (+)
START WITH e.manager_id IS NULL
CONNECT BY PRIOR e.emp_id = e.manager_id

Non-entity result types, called scalar result types, are mapped using the @ColumnResult annotation. One or more column mappings may be assigned to the columns attribute of the mapping annotation. The only attribute available for a column mapping is the column name. Listing 11-8 shows the SQL mapping for the employee and manager hierarchical query.

@SqlResultSetMapping(
    name="EmployeeAndManager",
    columns={@ColumnResult(name="EMP_NAME"),
             @ColumnResult(name="MANAGER_NAME")}
)
Listing 11-8

Scalar Column Mappings

Scalar results may also be mixed with entities. In this case, the scalar results are typically providing additional information about the entity.

Let’s look at a more complex example in which this would be the case. A report for an application needs to see information about each department, showing the manager, the number of employees, and the average salary. The following JP QL query produces the correct report:

SELECT d, m, COUNT(e), AVG(e.salary)
FROM Department d LEFT JOIN d.employees e
                  LEFT JOIN d.employees m
WHERE m IS NULL OR m IN (SELECT de.manager
                         FROM Employee de
                         WHERE de.department = d)
GROUP BY d, m

This query is particularly challenging because there is no direct relationship from Department to the Employee who is the manager of the department. Therefore, the employees relationship must be joined twice: once for the employees assigned to the department and once for the employee in that group who is also the manager. This is possible because the subquery reduces the second join of the employees relationship to a single result. We also need to accommodate the fact that there might not be any employees currently assigned to the department, and further that a department might not have a manager assigned. This means that each of the joins must be an outer join and that we further have to use an OR condition to allow for the missing manager in the WHERE clause.

Once in production, it is determined that the SQL query generated by the provider is not performing well, so the DBA proposes an alternate query that takes advantage of the inline views possible with the Oracle database. The query to accomplish this result is shown in Listing 11-9.

SELECT d.id, d.name AS dept_name,
       e.emp_id, e.name, e.salary, e.manager_id, e.dept_id,
       e.address_id,
       s.tot_emp, s.avg_sal
FROM dept d,
     (SELECT *
      FROM emp e
      WHERE EXISTS(SELECT 1 FROM emp WHERE manager_id = e.emp_id)) e,
     (SELECT d.id, COUNT(*) AS tot_emp, AVG(e.salary) AS avg_sal
      FROM dept d, emp e
      WHERE d.id = e.dept_id (+)
      GROUP BY d.id) s
WHERE d.id = e.dept_id (+) AND
      d.id = s.id
Listing 11-9

Department Summary Query

Fortunately, mapping this query is a lot easier than reading it. The query results consist of a Department entity; an Employee entity; and two scalar results, the number of the employees and the average salary. Listing 11-10 shows the mapping for this query.

@SqlResultSetMapping(
    name="DepartmentSummary",
    entities={
        @EntityResult(entityClass=Department.class,
                      fields=@FieldResult(name="name", column="DEPT_NAME")),
        @EntityResult(entityClass=Employee.class)
    },
    columns={@ColumnResult(name="TOT_EMP"),
             @ColumnResult(name="AVG_SAL")}
)
Listing 11-10

Mapping for the Department Query

Mapping Compound Keys

When a primary or foreign key is composed of multiple columns that have been aliased to unmapped names, a special notation must be used in the @ FieldResult annotations to identify each part of the key. Consider the query shown in Listing 11-11 that returns both the employee and the manager of the employee. The table in this example is the same one we demonstrated in Figure 10-4 of Chapter 10. Because each column is repeated twice, the columns for the manager state have been aliased to new names.

SELECT e.country, e.emp_id, e.name, e.salary,
       e.manager_country, e.manager_id, m.country AS mgr_country,
       m.emp_id AS mgr_id, m.name AS mgr_name, m.salary AS mgr_salary,
       m.manager_country AS mgr_mgr_country, m.manager_id AS mgr_mgr_id
FROM   emp e,
       emp m
WHERE  e.manager_country = m.country AND
       e.manager_id = m.emp_id
Listing 11-11

SQL Query Returning Employee and Manager

The result set mapping for this query depends on the type of primary key class used by the target entity. Listing 11-12 shows the mapping in the case where an id class has been used. For the primary key, each attribute is listed as a separate field result. For the foreign key, each primary key attribute of the target entity (the Employee entity again in this example) is suffixed to the name of the relationship attribute.

@SqlResultSetMapping(
    name="EmployeeAndManager",
    entities={
        @EntityResult(entityClass=Employee.class),
        @EntityResult(
            entityClass=Employee.class,
            fields={
                @FieldResult(name="country", column="MGR_COUNTRY"),
                @FieldResult(name="id", column="MGR_ID"),
                @FieldResult(name="name", column="MGR_NAME"),
                @FieldResult(name="salary", column="MGR_SALARY"),
                @FieldResult(name="manager.country",
                             column="MGR_MGR_COUNTRY"),
                @FieldResult(name="manager.id", column="MGR_MGR_ID")
            }
        )
    }
)
Listing 11-12

Mapping for Employee Query Using id Class

If Employee uses an embedded id class instead of an id class, the notation is slightly different. We have to include the primary key attribute name as well as the individual attributes within the embedded type. Listing 11-13 shows the result set mapping using this notation.

@SqlResultSetMapping(
    name="EmployeeAndManager",
    entities={
        @EntityResult(entityClass=Employee.class),
        @EntityResult(
            entityClass=Employee.class,
            fields={
                @FieldResult(name="id.country", column="MGR_COUNTRY"),
                @FieldResult(name="id.id", column="MGR_ID"),
                @FieldResult(name="name", column="MGR_NAME"),
                @FieldResult(name="salary", column="MGR_SALARY"),
                @FieldResult(name="manager.id.country",
                             column="MGR_MGR_COUNTRY"),
                @FieldResult(name="manager.id.id", column="MGR_MGR_ID")
            }
        )
    }
)
Listing 11-13

Mapping for Employee Query Using Embedded id Class

Mapping Inheritance

In many respects, polymorphic queries in SQL are no different from regular queries returning a single entity type. All columns must be accounted for, including foreign keys and the discriminator column for single-table and joined inheritance strategies. The key thing to remember is that if the results include more than one entity type, each of the columns for all the possible entity types must be represented in the query. The field result mapping techniques demonstrated earlier may be used to customize columns that use unknown aliases. These columns may be at any level in the inheritance tree. The only special element in the @ EntityResult annotation for use with inheritance is the discriminatorColumn element. This element allows the name of the discriminator column to be specified in the unlikely event that it is different from the mapped version.

Assume that the Employee entity had been mapped to the table shown in Figure 10-11 from Chapter 10. To understand aliasing a discriminator column, consider the following query that returns data from another EMPLOYEE_STAGE table structured to use single-table inheritance:

SELECT id, name, start_date, daily_rate, term, vacation,
       hourly_rate, salary, pension, type
FROM employee_stage

To convert the data returned from this query to Employee entities, the following result set mapping would be used:

@SqlResultSetMapping(
    name="EmployeeStageMapping",
    entities=
        @EntityResult(
            entityClass=Employee.class,
            discriminatorColumn="TYPE",
            fields={
                @FieldResult(name="startDate", column="START_DATE"),
                @FieldResult(name="dailyRate", column="DAILY_RATE"),
                @FieldResult(name="hourlyRate", column="HOURLY_RATE")
            }
        )
)

Mapping to Non-Entity Types

Since JPA 2.1 the ability to construct non-entity types from native queries via constructor expressions was added. Constructor expressions in native queries, much like the constructor expressions from JP QL, result in the instantiation of user-specified types by using the row data of the underlying result set to invoke the constructor. All of the data required to construct the object must be represented as arguments to the constructor.

Consider the constructor expression example that we introduced in Chapter 8 to create instances of the EmployeeDetails data type by selecting specific fields from the Employee entity:

SELECT NEW example.EmployeeDetails(e.name, e.salary, e.department.name)
FROM Employee e

In order to replace this query with its native equivalent and achieve the same result, we must define both the replacement native query and an SqlResultSetMapping that defines how the query results map to the user-specified type. Let’s begin by defining the SQL native query to replace the JP QL.

SELECT e.name, e.salary, d.name AS deptName
FROM emp e, dept d
WHERE e.dept_id = d.id

The mapping for this query is more verbose than the JP QL equivalent. Unlike JP QL where the columns are implicitly mapped to the constructor based on their position, native queries must fully define the set of data that will be mapped to the constructor in the mapping annotation. The following example demonstrates the mapping for this query:

@SqlResultSetMapping(
    name="EmployeeDetailMapping",
    classes={
        @ConstructorResult(
            targetClass=example.EmployeeDetails.class,
            columns={
                @ColumnResult(name="name"),
                @ColumnResult(name="salary", type=Long.class),
                @ColumnResult(name="deptName")
            })})

As with other examples that use ColumnResult, the name field refers to the column alias as defined in the SQL statement. The column results are applied to the constructor of the user-specified type in the order in which the column result mappings are defined. In cases where there are multiple constructors that may be ambiguous based on position only, the column result type may also be specified in order to ensure the correct match.

It is worth noting that should an entity type be specified as the target class of a ConstructorResult mapping, any resulting entity instances would be considered unmanaged by the current persistence context. Entity mappings on user-specified types are ignored when processed as part of a native query using constructor expressions.

Parameter Binding

SQL queries have traditionally supported only positional parameter binding. The JDBC specification itself supports only named parameters on CallableStatement objects, not PreparedStatement, and not all database vendors even support this syntax. As a result, JPA guarantees only the use of positional parameter binding for SQL queries. Check with your vendor to see whether the named parameter methods of the Query interface are supported, but understand that using them may make your application non-portable between persistence providers.

Another limitation of parameter support for SQL queries is that entity parameters cannot be used. The specification does not define how these parameter types should be treated. Be careful when converting or overriding a named JP QL query with a native SQL query that the parameter values are still interpreted correctly.

Stored Procedures

Since JPA 2.1, the ability to map and invoke stored procedures from a database was added. JPA 2.1 also introduced first class support for stored procedure queries on the EntityManager and defines a new query type, StoredProcedureQuery, which extends Query and better handles the range of options open to developers who leverage stored procedures in their applications. No new features related to stored procedures were added in JPA 2.2.

The following sections describe how to map and invoke stored procedure queries using JPA. Note that the implementation of stored procedures is highly database dependent and is therefore outside the scope of this book. Unlike JP QL and other types of native queries, the body of a stored procedure is never defined in JPA and is instead only ever referenced by name in the application.

Defining and Executing Stored Procedure Queries

Like other types of JPA queries, stored procedure queries may be created programmatically from the EntityManager interface or they may be defined using annotation metadata and later referenced by name. In order to define a stored procedure mapping, the name of the stored procedure must be provided as well as the name and type of all parameters to that stored procedure.

JPA stored procedure definitions support the main parameter types defined for JDBC stored procedures: IN, OUT, INOUT, and REF_CURSOR. As their name suggests, the IN and OUT parameter types pass data to the stored procedure or return it to the caller, respectively. INOUT parameter types combine IN and OUT behavior into a single type that can both accept and return a value to the caller. REF_CURSOR parameter types are used to return result sets to the caller. Each of these types has a corresponding enum value defined on the ParameterMode type.

Stored procedures are assumed to return all values through parameters except in the case where the database supports returning a result set (or multiple result sets) from a stored procedure. Databases that support this method of returning result sets usually do so as an alternative to the use of REF_CURSOR parameter types. Unfortunately, stored procedure behavior is very vendor specific, and this is an example where writing to a particular feature of a database is unavoidable in application code.

Scalar Parameter Types

To begin, let’s consider a simple stored procedure named "hello" that accepts a single string argument named "name" and returns a friendly greeting to the caller via the same parameter. The following example demonstrates how to define and execute such a stored procedure:

StoredProcedureQuery q = em.createStoredProcedureQuery("hello");
q.registerStoredProcedureParameter("name", String.class, ParameterMode.INOUT);
q.setParameter("name", "massimo");
q.execute();
String value = (String) q.getOutputParameterValue("name");

After the query has been executed via the execute() method, any IN or INOUT parameter values returned to the caller may be accessed using one of the getOutputParameterValue() methods , specifying either the name of the parameter or its position. As with native queries, parameter positions are numbered beginning with one. Therefore, if an OUT parameter is registered in the second position for a query, then the number two would be used to access that value, even if the first parameter is of IN type and does not return any value.

Note that calling the getSingleResult() or getResultList() methods of the inherited Query interface will result in an exception if the stored procedure only returns scalar values through parameters.

Result Set Parameter Types

Now let’s move to a more sophisticated example in which we have a stored procedure named fetch_emp that retrieves employee data and returns it to the caller via a REF_CURSOR parameter. In this case, we use the overloaded form of createStoredProcedureQuery() to indicate that the result set consists of Employee entities. The following example demonstrates this approach:

StoredProcedureQuery q = em.createStoredProcedureQuery("fetch_emp");
q.registerStoredProcedureParameter("empList", void.class, ParameterMode.REF_CURSOR);
if (q.execute()) {
    List<Employee> emp = (List<Employee>)q.getOutputParameterValue("empList");
    // ...
}

The first point to note about this example is that we are not specifying a parameter class type for the empList parameter. Whenever REF_CURSOR is specified as the parameter type, the class type argument to registerStoredProcedureParameter() is ignored. The second point to note is that we are checking the return value of execute() to see any result sets were returned during the execution of the query. If no result sets are returned (or the query only returns scalar values), execute() will return false. In the event that no records were returned and we did not check the result of execute(), then the parameter value would be null.

Previously it was noted that some databases allow result sets to be returned from stored procedures without having to specify a REF_CURSOR parameter. In this case, we can simplify the example by using the getResultList() method of the query interface to directly access the results:

StoredProcedureQuery q = em.createStoredProcedureQuery("fetch_emp");
List<Employee> emp = (List<Employee>)q.getResultList();
// ...

As a shortcut for executing queries, both getResultList() and getSingleResult() implicitly invoke execute(). In the case where the stored procedure returns more than one result set, each call to getResultList() will return the next result set in the sequence.

Stored Procedure Mapping

Stored procedure queries may be declared using the @NamedStoredProcedureQuery annotation and then referred to by name using the createNamedStoredProcedureQuery() method on EntityManager. This simplifies the code required to invoke the query and allows for more complex mappings than are possible using the StoredProcedureQuery interface. Like other JPA query types, names for stored procedure queries must be unique within the scope of a persistence unit.

We begin by declaring the simple "hello" example using annotations and then progressively move to more complex examples. The "hello" stored procedure would be mapped as follows:

@NamedStoredProcedureQuery(
    name="hello",
    procedureName="hello",
    parameters={
        @StoredProcedureParameter(name="name", type=String.class,
                                                  mode=ParameterMode.INOUT)
    })

Here we can see that the native procedure name must be specified in addition to the name of the stored procedure query. It is not defaulted as it is when using the createStoredProcedureQuery() method. As with the programmatic example, all parameters must be specified using the same arguments as would be used with registerStoredProcedureParameter().

The "fetch_emp" query is mapped similarly, but in this example we must also specify a mapping for the result set type:

@NamedStoredProcedureQuery(
    name="fetch_emp",
    procedureName="fetch_emp",
    parameters={
        @StoredProcedureParameter(name="empList", type=void.class,
                                            mode=ParameterMode.REF_CURSOR)
    },
    resultClasses=Employee.class)

The list of classes provided to the resultClasses field is a list of entity types. In the version of this example where REF_CURSOR is not used and the results are returned directly from the stored procedure, the empList parameter would simply be omitted. The resultClasses field would be sufficient. It is important to note that the entities referenced in resultClasses must match the order in which result set parameters are declared for the stored procedure. For example, if there are two REF_CURSOR parameters—empList and deptList—then the resultClasses field must contain Employee and Department in that order.

For cases where the result sets returned by the query do not natively map to entity types, SQL result set mappings may also be included in the NamedStoredProcedureQuery annotation using the resultSetMappings field. For example, a stored procedure that performed the employee and manager query defined in Listing 11-11 (and mapped in Listing 11-12) would be mapped as follows:

@NameStoredProcedureQuery(
    name="queryEmployeeAndManager",
    procedureName="fetch_emp_and_mgr",
    resultSetMappings = "EmployeeAndManager"
)

As with resultClasses, multiple result set mappings may also be specified if the stored procedure returns multiple result sets. It should be noted, however, that combining resultClasses and resultSetMappings is undefined. Support for result set mappings in the NamedStoredProcedureQuery annotation ensures that even the most complex stored procedure definitions can likely be mapped by JPA, simplifying access and centralizing metadata in a manageable way.

Entity Graphs

Unlike what its name implies, an entity graph is not really a graph of entities, but rather a template for specifying entity and embeddable attributes. It serves as a pattern that can be passed into a find method or query to specify which entity and embeddable attributes should be fetched in the query result. In more concrete terms, entity graphs are used to override at runtime the fetch settings of attribute mappings. For example, if an attribute is mapped to be eagerly fetched (set to FetchType.EAGER), it can set to be lazily fetched for a single execution of a query. This feature is similar to what has been referred to by some as the ability to set a fetch plan, load plan, or fetch group.

Note

Although entity graphs are currently being used for overriding attribute fetch state in queries, they are just structures that define attribute inclusion. There are no inherent semantics stored in them. They could just as easily be used as an input to any operation that might benefit from an entity attribute template, and in future they might be used with other operations as well. However, we focus on what exists right now and discuss entity graphs in the context of defining fetch plans.

The structure of an entity graph is fairly simple in that there are really only three types of objects that are contained within it.
  • Entity graph nodes: There is exactly one entity graph node for every entity graph. It is the root of the entity graph and represents the root entity type. It contains all of the attribute nodes for that type, plus all of the subgraph nodes for types associated with the root entity type1.

  • Attribute nodes: Each attribute node represents an attribute in an entity or embeddable type. If it is for a basic attribute, then there will be no subgraph associated with it, but if it is for a relationship attribute, embeddable attribute, or element collection of embeddables, then it may refer to a named subgraph.

  • Subgraph nodes: A subgraph node is the equivalent of an entity graph node in that it contains attribute nodes and subgraph nodes, but represents a graph of attributes for an entity or embeddable type that is not the root type2.

To help illustrate the structure, let's assume that we have the domain model described in Figure 8-1 (Chapter 8) and want an entity graph representation that specifies a subset of those attributes and entities, as shown in Figure 11-1.

The state in Figure 11-1 is clearly a subset of the state in Figure 8-1, and the entity graph structure to store a representative fetch plan for this state might look like the depiction in Figure 11-2.
../images/314633_3_En_11_Chapter/314633_3_En_11_Fig1_HTML.gif
Figure 11-1

Domain model subset

Entity graphs can be defined statically in annotation form or dynamically through an API. The composition differs slightly when using the annotation form of creating entity graphs versus creating them with the API; however, Figure 11-2 shows the basic structure.
../images/314633_3_En_11_Chapter/314633_3_En_11_Fig2_HTML.gif
Figure 11-2

Entity graph state

A key point to mention is that identifier and version attributes (see Chapter 12 for a complete description about version attributes) will always be included in both the entity graph node and each of the subgraph nodes. They do not need to be explicitly included when creating an entity graph, although if either of them is included, it is not an error, just a redundancy.

Every entity and embeddable class has a default fetch graph that is composed of the transitive closure of all of the attributes that are either explicitly defined as, or defaulted to be, eagerly fetched. The transitive closure part means that the rule is recursively applied, so the default fetch graph of an entity includes not only all of the eager attributes defined on it, but also all of the eager attributes of the entities that are related to it, and so on until the relationship graph has been exhausted. The default fetch graph will save us a lot of effort when it comes time to define more expansive entity graphs.

Entity Graph Annotations

Defining an entity graph statically in an annotation is useful when you know ahead of time that an attribute access fetch pattern needs to be different than what has been configured in the mappings. You can define any number of entity graphs for the same entity, each depicting a different attribute fetch plan.

The parent annotation to define an entity graph is @NamedEntityGraph . It must be defined on the root entity of the entity graph. All of its components are nested within the one annotation, so this means two things. First, if you are defining a large and involved entity graph, then your annotation is going to also be large and involved (and not likely very pretty). Second, if you are defining multiple entity graphs, you cannot share any of the constituent subgraph parts of one with another3. They are completely encapsulated within the enclosing named entity graph.

You likely noticed that the annotation begins with the Named prefix. This is a clear giveaway that entity graphs are named, and like other types of named objects in JPA, the names must be unique within the scope of the persistence unit. If not specified, the name of an entity graph will default to be the entity name (which, as you will recall from Chapter 2, is the unqualified name of the entity class). We will see later how a name is used to refer to a given entity graph when obtaining it from the entity manager.

Note that the subgraphs within an entity graph are also named, but those names are only valid within the scope of the entity graph and, as you will see in the next section, are used to connect the subgraphs.

Basic Attribute Graphs

Let’s use the domain model from Figure 8-1 as a starting point. In that model there is an Address entity with some basic attributes. We could create a simple named entity graph by annotating the Address entity, as shown in Listing 11-14.

@Entity
@NamedEntityGraph(
    attributeNodes={
        @NamedAttributeNode("street"),
        @NamedAttributeNode("city"),
        @NamedAttributeNode("state"),
        @NamedAttributeNode("zip")}
)
public class Address {
    @Id private long id;
    private String street;
    private String city;
    private String state;
    private String zip;
    // ...
}
Listing 11-14

Named Entity Graph with Basic Attributes

The entity graph is assigned the default name of Address and all of the attributes are explicitly included in the attributeNodes element, meaning that they should be fetched. There is actually a shortcut for this case by means of a special includeAllAttributes element in the @NamedEntityGraph annotation:

@Entity
@NamedEntityGraph(includeAllAttributes=true)
public class Address { ... }

Since the Address entity contains only eager attributes anyway (all basic attributes default to being eager), we can make it even shorter:

@Entity
@NamedEntityGraph
public class Address { ... }

Annotating the class without listing any attributes is a shorthand for defining a named entity graph that is composed of the default fetch graph for that entity. Putting the annotation on the class causes the named entity graph to be created and referenceable by name in a query.

Using Subgraphs

The Address entity was a rather simple entity, though, and didn’t even have any relationships. The Employee entity is more complicated and requires that we add in some subgraphs, as shown in Listing 11-15.

@Entity
@NamedEntityGraph(name="Employee.graph1",
    attributeNodes={
        @NamedAttributeNode("name"),
        @NamedAttributeNode("salary"),
        @NamedAttributeNode(value="address"),
        @NamedAttributeNode(value="phones", subgraph="phone"),
        @NamedAttributeNode(value="department" subgraph="dept")},
    subgraphs={
        @NamedSubgraph(name="phone",
            attributeNodes={
                @NamedAttributeNode("number"),
                @NamedAttributeNode("type")}),
        @NamedSubgraph(name="dept",
            attributeNodes={
                @NamedAttributeNode("name")})
    })
public class Employee {
    @Id
    private int id;
    private String name;
    private long salary;
    @Temporal(TemporalType.DATE)
    private Date startDate;
    @OneToOne
    private Address address;
    @OneToMany(mappedBy="employee")
    private Collection<Phone> phones = new ArrayList<Phone>();
    @ManyToOne
    private Department department;
    @ManyToOne
    private Employee manager;
    @OneToMany(mappedBy="manager")
    private Collection<Employee> directs = new ArrayList<Employee>();
    @ManyToMany(mappedBy="employees")
    private Collection<Project> projects = new ArrayList<Project>();
    // ...
}
Listing 11-15

Named Entity Graph with Subgraphs

For every Employee attribute that we want fetched, there is a @NamedAttributeNode listing the name of the attribute. The address attribute is a relationship, though, so listing it means that the address will be fetched, but what state will be fetched in the Address instance? This is where the default fetch group comes in. When a relationship attribute is listed in the graph but does not have any accompanying subgraph, then the default fetch group of the related class will be assumed. In fact, the general rule is that for any embeddable or entity type that has been specified to be fetched, but for which there is no subgraph, the default fetch graph for that class will be used. This is as you might expect since the default fetch graph specifies the behavior you get when you don’t use an entity graph at all, so the absence of specifying one should be equivalent. For our address example, you saw that the default fetch graph of Address is all of its (basic) attributes.

For the other relationship attributes the @NamedAttributeNode additionally includes a subgraph element, which references the name of a @NamedSubgraph. The named subgraph defines the attribute list for that related entity type, so the subgraph we named dept defines the attributes of the Department entity related via the department attribute of Employee.

All of the named subgraphs are defined in the subgraphs element of @NamedEntityGraph, regardless of where they fit in the type graph. This means that if the subgraph type contained a relationship attribute, then its named attribute node would contain a reference to yet another subgraph, which would also be listed in the subgraphs element of @NamedEntityGraph. We can even define a subgraph that defines an alternative fetch plan for our root entity graph class when it gets loaded through a related entity. As a matter of fact, there are times when we will need to do exactly that. Let’s look at an example in Listing 11-16.

@Entity
@NamedEntityGraph(name="Employee.graph2",
    attributeNodes={
        @NamedAttributeNode("name"),
        @NamedAttributeNode("salary"),
        @NamedAttributeNode(value="address"),
        @NamedAttributeNode(value="phones", subgraph="phone"),
        @NamedAttributeNode(value="manager", subgraph="namedEmp"),
        @NamedAttributeNode(value="department", subgraph="dept")},
    subgraphs={
        @NamedSubgraph(name="phone",
            attributeNodes={
                @NamedAttributeNode("number"),
                @NamedAttributeNode("type"),
                @NamedAttributeNode(value="employee", subgraph="namedEmp")}),
        @NamedSubgraph(name="namedEmp",
            attributeNodes={
                @NamedAttributeNode("name")}),
        @NamedSubgraph(name="dept",
            attributeNodes={
                @NamedAttributeNode("name")})
    })
public class Employee { ... }
Listing 11-16

Named Entity Graph with Multiple Type Definitions

This named entity graph contains two major changes from the previous one. The first change is that we have added the manager attribute to be fetched. Since the manager is an Employee entity you might be surprised that the namedEmp subgraph is specified, thinking that the manager Employee would just get loaded according to the fetch plan described by the named entity graph that we are defining (it is an Employee entity graph, after all). This is not what the rule states, however. The rule is that unless a subgraph is specified for a relationship attribute type, the default fetch graph for that type will be used as the fetch plan. For Employee, that would mean the manager would have all of its eager relationships loaded, and all of the eager relationships of its related entities, and so on. This could cause much more data to be loaded than we expected. The solution is to do as shown in Listing 11-16 and specify a minimal subgraph for the manager Employee.

The second change to this entity graph is that the phone subgraph includes the employee attribute. Once again, we are referencing the namedEmp subgraph to specify that the employee not be loaded according to the default fetch graph. The first thing of note is that we can reuse the same named subgraph in multiple places in the entity graph. After that you should notice that the employee attribute is really a back pointer to an employee in the result set of the named entity graph. We just want to make sure that referencing it from the phone does not cause more to be fetched than what is already defined by the employee named entity graph, itself.

Entity Graphs with Inheritance

We have so far steered clear of the inheritance aspect of our entity model, but we will ignore it no longer. A scaled-back entity graph that includes only the employee name and projects is shown in Listing 11-17.

@Entity
@NamedEntityGraph(name="Employee.graph3",
    attributeNodes={
        @NamedAttributeNode("name"),
        @NamedAttributeNode(value="projects", subgraph="project")},
    subgraphs={
        @NamedSubgraph(name="project", type=Project.class,
            attributeNodes={
                @NamedAttributeNode("name")}),
        @NamedSubgraph(name="project", type=QualityProject.class,
            attributeNodes={
                @NamedAttributeNode("qaRating")})
    })
public class Employee { ... }
Listing 11-17

Named Entity Graph with Inheritance

The attribute fetch state for the projects attribute is defined in a subgraph called project, but as you can see, there are two subgraphs named project. Each of the possible classes/subclasses that could be in that relationship has a subgraph named project and includes its defined state that should be fetched, plus a type element to identify which subclass it is. However, since DesignProject does not introduce any new state, we don’t need to include a subgraph named project for that class.

The inheritance case that this doesn’t cover is when the root entity class is itself a superclass. For this special case there is a subclassSubgraphs element to list the root entity subclasses. For example, if there was a ContractEmployee subclass of Employee that had an additional hourlyRate attribute that we wanted fetched, then we could use the entity graph shown in Listing 11-18.

@Entity
@NamedEntityGraph(name="Employee.graph4",
    attributeNodes={
        @NamedAttributeNode("name"),
        @NamedAttributeNode("address"),
        @NamedAttributeNode(value="department", subgraph="dept")},
    subgraphs={
        @NamedSubgraph(name="dept",
            attributeNodes={
                @NamedAttributeNode("name")})},
    subclassSubgraphs={
        @NamedSubgraph(name="notUsed", type=ContractEmployee.class,
            attributeNodes={
                @NamedAttributeNode("hourlyRate")})
    })
public class Employee { ... }
Listing 11-18

Named Entity Graph with Root Inherita nce

Tip

A minor issue in the annotation definition is that the subclassSubgraphs element is of type NamedSubgraph[], meaning that a name is required to be specified even though in this case it is not used anywhere. We have labeled it notUsed to show that it is extraneous.

Map Key Subgraphs

The last special case is reserved for our old friend, the Map. When a relationship attribute is of type Map, there is the issue of the additional key part of the Map. If the key is an embeddable or an entity type, then an additional subgraph might need to be specified (or else the default fetch graph rule will apply). To handle these cases there is a keySubgraph element in NamedAttributeNode. To illustrate having a Map with an embeddable key subgraph, we use an EmployeeName embeddable class similar to Listing 5-13 (Chapter 5) and modify our Department entity slightly to be similar to Listing 5-14. We list the type definitions in Listing 11-19 and add a named entity graph.

@Embeddable
public class EmployeeName {
    private String firstName;
    private String lastName;
    // ...
}
@Entity
@NamedEntityGraph(name="Department.graph1",
    attributeNodes={
        @NamedAttributeNode("name"),
        @NamedAttributeNode(value="employees",
            subgraph="emp",
            keySubgraph="empName")},
    subgraphs={
        @NamedSubgraph(name="emp",
            attributeNodes={
                @NamedAttributeNode(value="name",
                    subgraph="empName"),
                @NamedAttributeNode("salary")}),
        @NamedSubgraph(name="empName",
            attributeNodes={
                @NamedAttributeNode("firstName"),
                @NamedAttributeNode("lastName")})
    })
public class Department {
    @Id private int id;
    @Embedded
    private EmployeeName name;
    @OneToMany(mappedBy="department")
    @MapKey(name="name")
    private Map<EmployeeName, Employee> employees;
    // ...
}
Listing 11-19

Named Entity Graph with Map Key Subgraph

At this point you have just about as much expertise on named entity graphs as almost any developer out there, so after seeing all of the annotation examples you might have noticed that they were more complicated than they needed to be. In many cases they listed attributes that could have been easily defaulted by using the default fetch graph rule. This was to try to keep the model as simple as possible but still be correct and able to demonstrate the concepts. Now that you have the rules straight in your mind, you should go back through each of the named entity graphs and as an exercise see how they could be shortened using the default fetch graph rule.

Entity Graph API

The API is useful for creating, modifying, and adding entity graphs dynamically in code. The entity graphs can be used to generate fetch plans based on program parameters, user input, or in some cases even static data when programmatic creation is preferred. In this section, we describe the classes and most of the methods of the API. We apply them in examples showing how to create dynamic equivalents of the named entity graphs in the previous annotation section.

While the entity graphs resulting from annotations are the same as those created using the API, there are some minor differences between the models they each employ. This is primarily because of the inherent differences between annotations and a code API, but is also somewhat by style choice.

The way to get started building a new entity graph is to use the createEntityGraph() factory method on EntityManager. It takes the root entity class as a parameter and returns a new EntityGraph instance typed to the entity class:

EntityGraph<Address> graph = em.createEntityGraph(Address.class);

The next step is to add attribute nodes to the entity graph. The adding methods are designed to do most of the work of creating the node structures for you. We can use the variable-arg addAttributeNodes() method to add the attributes that will not have subgraphs associated with them:

graph.addAttributeNodes("street","city", "state", "zip");

This will create an AttributeNode object for each of the named attribute parameters and add it to the entity graph. There is unfortunately no method equivalent to the includeAllAttributes element in the @NamedEntityGraph annotation.

There are also strongly typed method equivalents to the ones that take string-based attribute names. The typed versions use the metamodel, so you would need to ensure that the metamodel has been generated for your domain model (see Chapter 9). A sample invocation of the strongly typed addAttributeNodes() method would be:

graph.addAttributeNodes(Address_.street, Address_.city,
                        Address_.state, Address_.zip);

When adding an attribute for which you also intend to add a subgraph, the addAttributeNodes() methods should not be used. Instead, there are a number of addSubgraph() method variants that should be used instead. Each of the addSubgraph() methods will first create an instance of AttributeNode for the passed-in attribute, then create an instance of Subgraph, then link the subgraph to the attribute node, and finally return the Subgraph instance. The string-based version can be used to replicate our named entity graph from Listing 11-15. The resulting entity graph is shown in Listing 11-20.

EntityGraph<Employee> graph = em.createEntityGraph(Employee.class);
graph.addAttributeNodes("name","salary", "address");
Subgraph<Phone> phone = graph.addSubgraph("phones");
phone.addAttributeNodes("number", "type");
Subgraph<Department> dept = graph.addSubgraph("department");
dept.addAttributeNodes("name");
Listing 11-20

Dynamic Entity Graph with Subgraphs

The API-based entity graph is clearly shorter and neater than the annotation-based one. This is one of those cases when methods are not only more expressive than annotations but also easier to read. Of course, the variable argument methods don't hurt either.

The example in Listing 11-16 illustrated having an entity graph containing a second definition of the root entity class , as well as having one subgraph refer to another. Listing 11-21 shows that the API actually suffers in this case because it doesn’t allow sharing of a subgraph within the same entity graph. Since there is no API to pass in an existing Subgraph instance, we need to construct two identical named employee subgraphs.

EntityGraph<Employee> graph = em.createEntityGraph(Employee.class);
graph.addAttributeNodes("name","salary", "address");
Subgraph<Phone> phone = graph.addSubgraph("phones");
phone.addAttributeNodes("number", "type");
Subgraph<Employee> namedEmp = phone.addSubgraph("employee");
namedEmp.addAttributeNodes("name");
Subgraph<Department> dept = graph.addSubgraph("department");
dept.addAttributeNodes("name");
Subgraph<Employee> mgrNamedEmp = graph.addSubgraph("manager");
mgrNamedEmp.addAttributeNodes("name");
Listing 11-21

Dynamic Entity Graph with Multiple Type Definitions

The inheritance example in Listing 11-17 can be translated into an API-based version. When a related class is actually a class hierarchy, each call to addSubgraph() can take the class as a parameter to distinguish between the different subclasses, as shown in Listing 11-22.

EntityGraph<Employee> graph = em.createEntityGraph(Employee.class);
graph.addAttributeNodes("name","salary", "address");
Subgraph<Project> project = graph.addSubgraph("projects", Project.class);
project.addAttributeNodes("name");
Subgraph<QualityProject> qaProject = graph.addSubgraph("projects", QualityProject.class);
qaProject.addAttributeNodes("qaRating");
Listing 11-22

Dynamic Entity Graph with Inheritance

When inheritance exists at the root entity class, the addSubclassSubgraph() method should be used. The class is the only parameter that is required. The API version of the annotation in Listing 11-18 is shown in Listing 11-23.

EntityGraph<Employee> graph = em.createEntityGraph(Employee.class);
graph.addAttributeNodes("name","address");
graph.addSubgraph("department").addAttributeNodes("name");
graph.addSubclassSubgraph(ContractEmployee.class).addAttributeNodes("hourlyRate");
Listing 11-23

Dynamic Entity Graph with Root Inheritance

Note that in Listing 11-23 we use the fact that no further subgraphs are being added to the subgraphs connected to the entity graph node, so neither of the created subgraphs are saved in stack variables. Rather, the addAttributeNodes() method is invoked directly on each of the Subgraph results of the addSubgraph() and addSubclassSubgraph() methods.

Our final example to convert is the Map example from Listing 11-19. The API equivalent is shown in Listing 11-24. The root entity is the Department entity and the Map is keyed by the EmployeeName embeddable.

EntityGraph<Department> graph = em.createEntityGraph(Department.class);
graph.addAttributeNodes("name");
graph.addSubgraph("employees").addAttributeNodes("salary");
graph.addKeySubgraph("employees").addAttributeNodes("firstName", "lastName");
Listing 11-24

Dynamic Entity Graph with Map Key Subgraph

In this example, the addKeySubgraph() method was invoked on the root entity graph node but the same method also exists on Subgraph, so a key subgraph can be added at any level where a Map occurs.

Managing Entity Graphs

The previous sections taught you how to create named and dynamic entity graphs, so the next logical step is to see how they can be managed. For the purposes of this section, managing entity graphs means accessing them, saving them, changing them, and creating new ones using an existing one as a starting point.

Accessing Named Entity Graphs

It is easy to access a dynamic entity graph since you have it stored in the same variable that you used during the process of entity graph creation. When you have defined a named entity graph, though, you must access it through the entity manager before it can be used. This is achieved by passing in the name of the entity graph to the getEntityGraph() method . It will be returned as an EntityGraph object. We can access the entity graph that we defined in Listing 11-16 with the following statement:

EntityGraph<?> empGraph2 = em.getEntityGraph("Employee.graph2");

Note that the type parameter is wildcarded because the type of the entity graph is not known by the entity manager. Later, when we show ways to use an entity graph, you will see that it is not necessary to strongly type it in order to use it.

If there are many entity graphs defined on a single class and we have a reason to sequence through them, we can do so using the class-based accessor method. The following code looks at the attribute names of the root entity classes for each of the Employee entity graphs:

List<EntityGraph<? super Employee>> egList =
                                 em.getEntityGraphs(Employee.class);
for (EntityGraph<? super Employee> graph : egList) {
    System.out.println("EntityGraph: " + graph.getName());
    List<AttributeNode<?>> attribs = graph.getAttributeNodes();
    for (AttributeNode<?> attr : attribs) {
        System.out.println("  Attribute: " + attr.getAttributeName());
    }
}

In this case, the EntityGraph parameter type is lower bounded to be Employee, but may also be some superclass of Employee as well. If, for example, Person was a superclass entity of Employee, then we would also get the Person entity graphs included in the output.

Adding Named Entity Graphs

The Entity Graph API allows dynamic creation of entity graphs, but you can go even further by taking those entity graphs and saving them as named entity graphs. Once they are named they can be used just like a named entity graph that was statically defined in an annotation.

We can add any the entity graphs that we created in Listings 11-20 to 11-24 as a named entity graph by using the addNamedEntityGraph() method on the entity manager factory:

em.getEntityManagerFactory().addNamedEntityGraph("Employee.graphX", graph);

Note that the name we choose for the entity graph is up to us, just as it was when we defined it in annotation form, except that in this case there is no default name. We must supply a name as a parameter.

If a named entity graph with the same name already existed in the named entity graph namespace, it will be overridden by the one we are supplying in the addNamedEntityGraph() call, since there can only be one entity graph of a given name in a persistence unit.

Creating New Entity Graphs From Named Ones

In some cases you may find that you have an existing entity graph but want to create a new one that is very similar to the existing one, but differs by some small factor. This may be especially true due to the fact that subgraphs cannot be shared across entity graphs. The best way to do this is to use the createEntityGraph() method . By using an existing graph you can modify just the parts that you want changed and then resave the modified one under a different name.

In Listing 11-16, we defined an entity graph for Employee that included their phones and their department, but not their projects. In Listing 11-25, we access that entity graph and change it to also include their projects. Adding an attribute node that is a relationship, but not adding a subgraph for it, will cause the default fetch graph for that entity type to be applied. We can then choose to save the entity graph under the same name, effectively overwriting the previous one, or save it under a different name so that we have both entity graphs to choose from. Listing 11-25 does the latter.

EntityGraph<?> graph = em.createEntityGraph("Employee.graph2");
graph.addAttributeNodes("projects");
em.getEntityManagerFactory().addNamedEntityGraph("Employee.newGraph", graph);
Listing 11-25

Creating an Entity Graph from an Existing Graph

The change that we made to the entity graph in Listing 11-25 was actually fairly trivial on purpose because it turns out that you may be somewhat limited in what you can do when it comes to making changes to an existing entity graph. The reason is that the Javadoc for the Entity Graph API does not specify whether you can mutate the collection accessors. For example, EntityGraph has a getAttributeNodes() method but the method does not specify whether the List<AttributeNode<?>> returned by it is the actual List referred to by the EntityGraph instance. If it is a copy, then if it is modified, it will have no effect on the EntityGraph instance it was obtained from. This would make it impossible to remove an attribute from the graph since there is no alternative API to modify the collections other than to add to them.

Using Entity Graphs

The hardest part of entity graphs is creating them to be correct and to produce the result that you are expecting. Once you have created the right entity graphs, using them is fairly simple. They are passed as the values of one of two standard properties. These properties can either be passed into a find() method or set as a query hint on any named or dynamic query. Depending on which property is used, the entity graph will take on the role of either a fetch graph or a load graph. The following sections explain the semantics of the two types of graphs and show some examples of how and when they can be used.

Fetch Graphs

When an entity graph is passed into a find() method or a query as the value to the javax.persistence.fetchgraph property, the entity graph will be treated as a fetch graph. The semantics of a fetch graph are that all attributes included in the graph are to be treated as having a fetch type of EAGER, as if the mapping had been specified as fetch=FetchType.EAGER, regardless of what the static mapping actually specifies. Attributes that are not included in the graph are to be treated as LAZY. As described earlier, all identifier or version attributes will be treated as EAGER and loaded, regardless of whether they are included in the graph. Also, as we explained, if a relationship or embedded attribute is included in the graph but no subgraph is specified for it, then the default fetch graph for that type will be used.

The usefulness of a fetch graph is primarily to enable attributes to be fetched lazily when they were configured or defaulted to be eagerly fetched in the mapping. The thing to remember is that the same semantics of LAZY apply here as those that were described in Chapter 4. That is, when an attribute is marked as LAZY, there is no guarantee that the attribute will remain unloaded until the first time it is accessed. If an attribute is LAZY, it only means that the provider can optimize by not fetching the state of the attribute until it is accessed. The provider always has a right to load LAZY attributes eagerly if it wants to.

Let’s look at an example of using a fetch graph. In Listing 11-16, we defined an entity graph for the Employee entity. Since it was defined as a named entity graph, we can access it using the getEntityGraph() method and use it as the value for our javax.persistence.fetchgraph property.

Map<String,Object> props = new HashMap<String,Object>();
props.put("javax.persistence.fetchgraph",
          em.getEntityGraph("Employee.graph2"));
Employee emp = em.find(Employee.class, empId, props);

We can just as easily pass in the dynamic version that we created in Listing 11-21. If the dynamic graph was being created in the graph variable, then we could simply pass it in to the find() method, or as a query hint:

EntityGraph<Employee> graph = em.createEntityGraph(Employee.class);
// ... (compose graph as in Listing 11-21)
TypedQuery<Employee> query = em.createQuery(
        "SELECT e FROM Employee e WHERE e.salary > 50000", Employee.class);
query.setHint("javax.persistence.fetchgraph",graph);
List<Employee> results = query.getResultList();

Load Graphs

A load graph is an entity graph that is supplied as the value to the javax.persistence.loadgraph property. The main difference between a fetch graph and a load graph is how the missing attributes are treated. While in a fetch graph an excluded attribute is to be treated as LAZY, in a load graph all missing attributes are to be treated as they are defined in the mappings. Expressing it in terms of the default fetch graph that we discussed in the previous sections, an empty load graph with no attributes included is the same as the default fetch graph for that type. The value of using a load graph is in the ability to cause one or more attributes to be treated as EAGER even though they were statically defined to be LAZY.

In Chapter 6, we showed some examples of ways to ensure the employee department was loaded, even though it was specified to be LAZY. This is a perfect case for using a load graph. In Listing 11-26, we show an alternative version of Listing 6-27 (Chapter 6).

@Stateless
public class EmployeeService {
    @PersistenceContext(unitName="EmployeeService")
    private EntityManager em;
    public List findAll() {
        EntityGraph<Employee> graph = em.createEntityGraph(Employee.class);
        graph.addAttributeNodes("department");
        TypedQuery<Employee> query = em.createQuery(
                "SELECT e FROM Employee e", Employee.class);
        query.setHint("javax.persistence.loadgraph", graph);
        return query.getResultList();
    }
    // ...
}
Listing 11-26

Triggering a Lazy Relationship

As you can see, the graph creation is quite simple since we are only adding one attribute and no subgraphs. The department attribute is a relationship, and we did not include a subgraph for it so the default fetch graph for Department will be used.

The Case of the Missing Property

The difficulty with using a fetch graph is that it is a complete specification for the type. To make a single attribute be lazy, you need to specify all of the eager attributes. In other words, you cannot just override the fetch mode for a single attribute; when you create a fetch graph for an entity or embeddable type, you are effectively overriding all of the attributes of the type, either by including them or excluding them from the fetch graph. What the spec really needs is an additional property, javax.persistence.lazygraph, that would specify that all of the attributes included in the graph are to be lazy and all of the excluded attributes revert to what they are defined to be in their mappings. This would allow selective inclusion of lazy attributes.

Best Practices for Fetch and Load Graphs

When and how to use a fetch graph or a load graph will become obvious as you try to use one or the other; it won't take long to discover which one fits your attribute loading needs. However, we offer a few tips to get you going and perhaps save you a little bit of time at the outset.

Learn what your target provider supports in terms of laziness. If your provider loads every lazy attribute eagerly and you are planning to create a series of fetch graphs for your queries to make attributes be lazy, then it might not be worth going through the effort at all. You can test the loading behavior of your provider by using the PersistenceUnitUtil.isLoaded() method on a lazy attribute before it is accessed. Try it out on the different types of attributes that you are planning to set to LAZY.

If your entity graphs are not acting the way that you think they should be, then look for attributes that do not have subgraphs. Remember that the default fetch graph will be used when no subgraph is specified. This is relevant for all bidirectional relationships (remember to set a subgraph for the relationship back to the original object type), but especially those that navigate back to the root entity type. Your intuition may tell you that the root entity graph specification will be used, but it is the default fetch graph that will be used instead.

If you end up using a fetch or load graph to change the fetch type of an attribute more times than not, you may want to consider changing how the fetch type is defined in the mapping. The mapping should define the most commonly used fetch mode, with the fetch or load graphs overriding it in the exceptional cases.

Using named entity graphs provides reusability of the entity graph and is a convenient way to create a slightly different graph for one-off modifications. While declaring them in code is a neater and preferred way to define entity graphs, you should go on to register them as named entity graphs to achieve the reusability. You will also want to define them in code that you are sure will be executed before any of the queries that use them get executed.

Summary

We began the chapter with a look at SQL queries. We looked at the role of SQL in applications that also use JP QL and the specialized situations where only SQL can be used. To bridge the gap between native SQL and entities, we described the result set mapping process in detail, showing a wide range of queries and how they translate back into the application domain model.

We then showed how stored procedures can be invoked through a JPA query and how the results can be obtained through output parameters, ref cursors, and result sets. Stored procedures are always going to be somewhat database-specific so care must be taken when using them.

We finished off by talking about entity graphs, what they are, and how they are constructed. We showed how named entity graphs can be defined in annotation form and went on to describe the API for creating dynamic entity graphs in code. The entity manager methods for obtaining named or modifiable entity graphs was discussed, including an example that took an existing named entity graph, made a change to it, then added the changed graph as a separate named entity graph. Lastly, we showed how entity graphs are used. You saw how they take on the semantics of fetch graphs or load graphs when they are passed as property values to find() methods or queries.

In the next chapter, we look at more advanced topics, primarily in the areas of lifecycle callbacks, entity listeners, validation, concurrency, locking, and caching.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.129.210.17