EclipseLink has many advanced query features that provide far more flexibility than what is currently in the Java Persistence specification. These features are easily accessible through Query Hints.
Caching Query Results
If your application repeatedly executes the same queries and these queries are expected to return the same results, EclipseLink can cache the query results. Cached query results eliminate the need to query the database, which greatly improves throughput. As with the Entity cache, the Query cache can be configured for size and to auto-expire.
The Query Cache can be configured through Query Hints within the Java Persistence API.
eclipselink.query-results-cache |
Activates the query cache for this query. |
eclipselink.query-results-cache.type |
Has same cache types as the Entity cache but the default for the Query cache is CacheType.cache. |
eclipselink.query-results-cache.size |
Sets a cache size if appropriate. |
eclipselink.query-results-cache.expiry |
Expires cache entries after a fixed number of milliseconds. |
eclipselink.query-results-cache.expiry-time-of-day |
Expires cache entries at specific time of day. |
Bulk Reading
If your application needs to read in multiple related Entities it can be far more efficient to read these Entities in a few queries rather than issue multiple separate queries for each relationship and query result. Java Persistence API allows for limited bulk reading using the JOIN FETCH construct in JPQL. However, EclipseLink offers both nested joins and Batch reading.
Join Fetch
Using the Query Hint eclipselink.join-fetch and a simple dot notation, an application can request multiple levels of Entities to be join fetched.
Query query = entityManger.createQuery(
“SELECT e FROM Employee e WHERE …”);
query.setHint(“eclipselink.join-fetch”, “e.department.address”);
This example will select the Employee, the employee’s Department, and the department’s Address in a single query.
Batch Reading
EclipseLink has an additional bulk reading feature called Batch Reading. Using Join Fetch can often require significant resources as a great deal of duplicate data is returned (for instance, when joining in a OneToMany relationship). Querying the related data using Batch Reading can be more efficient. Batch Reading will query for the root Entity and then use a subsequent query to load the related data. Although there is an additional query, it reduces the number of joins on the database and the amount of data returned.
Query query = entityManger.createQuery(
“SELECT e FROM Employee e WHERE …”);
query.setHint(“eclipselink.batch”, “e.phoneNumbers”);
Batch Reading can be used in combination with Join Fetch for optimized bulk reading.
Stored Procedures
Executing a Stored Procedure through EclipseLink’s Java Persistence API extensions is as easy as defining a Named Stored Procedure query.
@NamedStoredProcedureQuery(
name=”SProcAddress”,
resultClass=models.jpa.advanced.Address.class,
procedureName=”SProc_Read_Address”,
parameters={
@StoredProcedureParameter(
direction=IN_OUT,
name=”address_id_v”,
queryParameter=”ADDRESS_ID”,
type=Integer.class),
@StoredProcedureParameter(
direction=OUT,
name=”street_v”,
queryParameter=”STREET”,
type=String.class)
}
)
This query is then called using the Java Persistence APIs.
Query aQuery = em.createNamedQuery(“SProcAddress”)
A call to getResultList() will return a collection of Address entities through the Stored Procedure’s result set. If no result set is returned, EclipseLink can build Entity results from the output parameters if the resultClass or resultSetMapping is set.
The Direction.OUT_CURSOR is used for stored procedure parameters that are returning result sets through “ref cursors”.
If both result sets and output parameters are returned by the stored procedure, then a SessionEventListener that responds to outputParametersDetected should be registered with the EntityManagerFactory and the output parameters will be returned through this event.
Fetch Groups
Lazy attributes can make queries more efficient by delaying the loading of data. EclipseLink offers a feature called Fetch Groups that allows you to define multiple lazy attribute configurations and apply those on a per-query basis. Attributes are fully lazy accessible, and accessing an unfetched attribute will cause the Entity attributes to be loaded as per the static lazy configuration from the Entity metadata.
Configuration
Fetch Groups can be created statically using @FetchGroup.
@FetchGroups({
@FetchGroup(
name=”FirstLast”,
attributes={
@FetchAttribute(name=”first”),
@FetchAttribute(name=”last”)
}
),
@FetchGroup(
name=”LastName”,
attributes={@FetchAttribute(name=”lastName”)}
)
})
They are then applied to the query through the use of Query Hint.
query.setHint(QueryHints.FETCH_GROUP_NAME, “FirstLast”);
Fetch Groups can also be created and used at runtime…
FetchGroup fetchGroup = new FetchGroup(); fetchGroup.addAttribute(“lastName”);
query.setHint(QueryHints.FETCH_GROUP, fetchGroup);
Other Fetch Group query hints are available as well to provide fine-grained configuration. Details can be found in the QueryHints javaDocs.
Have multiple Persistence Contexts in the same transaction? If any are performing transactional writes, ensure you disable an EclipseLink optimization with the Entity Manager property eclipse link. transaction.join-existing so all queries will access transactional data.
Cursors
When reading a large dataset, the application may wish to stream the results from a query. This is useful if the dataset is quite large. Using the query hint “eclipselink.cursor” set to “true”, EclipseLink will return a org.eclipse.persistence.queries.CursoredStream, which will allow the results to be retrieved iteratively from the database.
Need to load a large dataset and want to bypass the Persistence Context to save heap space? Use the Query Hint “eclipselink.readonly” and the results of the Query will not be managed.
Query Redirectors
Should an application have the need to dynamically alter a query or execute a query against another resource, Query Redirectors can be used. A redirector implements a simple interface org.eclipse. persistence.queries.QueryRedirector, and the developer of the Redirector is free to do most anything. Any cache maintenance will have to occur within the Redirector if the query is executed against another resource.
Configuration
Query Redirectors can be set through the Query Hint eclipse link. query.redirector or set as default Redirectors on an Entity.
@QueryRedirectors(
allQueries=org.queryredirectors.AllQueriesForEntity.class)
@Entity
public class …
Default Redirectors will be called every time a query is executed against this Entity type. Default Redirectors can be configured per Entity by Query type as well.
{{ parent.title || parent.header.title}}
{{ parent.tldr }}
{{ parent.linkDescription }}
{{ parent.urlSource.name }}