Introduction
One of the disadvantage of locating the entity objects using the EntityManager.find() and EntityManager.getReference() methods is, we cannot specify any powerful search criteria for searching the entity objects. All we can provide is the primary key to request for a particular object. Another fact is the class name of entity must be known. The Query API that comes with JPA is powerful in the sense more additional criteria can be specified in the run-time during executing the query.
EntityManager serves as factory classes for getting a reference to the Query objects. The query string that we specify for locating entity objects is called Java Persistent Query Language (JPQL). The current version of the JPQL is 1.0 and it is more robust flexible and object-oriented than SQL. The persistence engine will parse the query string, transform the JPQL to the native SQL before executing it.
also read:
Types of Queries
Queries can be classified into two different types depending upon how actually the query string is defined. One is the static query (Named Query) and the other one, the Dynamic Query.
Static Query
A static query (or a named query) is one which is defined statically with the help of annotation (or XML) before the entity class. A name is usually given to the query definition so that other components in the same persistent unit can refer the query using the same.
For example,
@NamedQuery(name = "MobileEntity.findAll" query = “SELECT M FROM MOBILEENTITY”) @Entity class MobileEntity{ }
A named query (with a name ‘Mobile.findAll’) has been defined and this query can be referenced later in the code through its name, like this.
Query findAllQuery = entityManager.createNamedQuery ("MobileEntity.findAll"); // Execute the query.
Note that the code is referring the query by its name (‘Mobile.findAll’) by calling the createNamedQuery() method. Also, since these named queries are statically defined, during deployment time itself, the persistent engine may parse and translate the JPQL into native SQL, cache them, thereby yielding better performance.
Multiple named queries can be logically defined with the help of @NamedQueries, like this,
@NamedQueries( { @NamedQuery(name = "Mobile.selectAllQuery" query = "SELECT M FROM MOBILEENTITY"), @NamedQuery(name = "Mobile.deleteAllQuery" query = "DELETE M FROM MOBILEENTITY") } )[The @NamedQueries is very similar like @NamedQuery, but can accept multiple @NamedQuery objects, The definition of the @NamedQueries looks like this,
@interface NamedQuries{ NamedQuery[] value; }
The way to specify arrayed elements with the definition of an annotation is by using {}. So was defined for the @NamedQueries( {…} ) and since the property called ‘value’ is the default property name for annotation, there is no need to explicitly mention the property name in the annotation definition. Following also works well which explicitly defines the property name ‘value’ for the annotation @NamedQueries.
@NamedQueries(value = { …. } )
Dynamic Queries
Dynamic Queries are nothing but whose query strings are provided at run-time. All calls to EntityManager.createQuery(queryString) are actually creating dynamic query objects. By defining query objects in this way, we lose the efficiency and from the performance point of view, the query execution may be slow as the persistence engine has to do all the parsing and validation stuffs, along with mapping the JPQL to the SQL at the run-time.
Following code creates a dynamic query object on the fly.
String queryString = …… // Obtained during run-time. Query dynaQuery = entityManager.createQuery(queryString);
Static Queries will be a better choice if the scenario for which the query to be executed is well known in advance.
Query Operations
Subsequent topics cover how to work with the query API for locating the entity objects, named and the positional support for Query objects and Paging Query Results for performance.
Single Result
The following code shows how to execute a query that returns a single entity object (assuming that a table called MobileEntity with field’s model, manufacturer and imei are available in the database).
Query singleSelectQuery = entityManager.createQuery( "SELECT M FROM MOBILEENTITY WHERE M.IMEI = 'ABC-123'"); MobileEntity mobileObj = singleSelectQuery.getSingleResult();
The query string that’s defined inside the createQuery() method is the Java Persistence Query Language. The Java Persistent Query Language (JPQL) resembles more like a SQL. The query statement is a simple select statement that is used to select a particular mobile object with imeiNo ‘ABC-123’. M represents a named instance row, which can be used to directly refer the fields in the corresponding table.
A call to getSingleResult() will execute the query and returns a single row that matches the search criteria in the query string. If the match wasn’t successful, then the getSingleResult() will return an EntityNotFoundException. Also, if more than one matches occur during query execution (this may not happen in our case, since imeiNo is a primary key), a run-time NonUniqueResultException will be thrown.
Multiple Results
Query.getResultList() will execute a query and may return a List object containing multiple entity instances. The following code demonstrates the same.
Query multipleSelect = entityManager .createQuery("SELECT M FROM MOBILE"); List mobiles = (List)multipleSelect.getResultList();
The type-cast (List) is necessary as the getResultList() method will return a non-parameterized List object. If only one mobile object is found as a result of executing the query, then this method may simple return a list with size 1. Also note that, getResultList() method can only execute on select statements as opposed to UPDATE or DELETE statements. If any statement other than SELECT statement is passed on to the method, then an IllegalStateException will be thrown at the run-time.
Working with parameters
To reuse and to execute the query more efficiently with different set of values, one can depend on the parameter support (positional and named) offered by the JPA. These techniques resembles the one we had for the java.sql.PreparedStatement where one can prepare the statement object once and can execute the query with different set of parameter values again and again.
The positional parameter is used to bind the parameter value in terms of positional index and it is denoted by ?index within the query string. Similarly the named parameter is used to substitute the parameter value which is specified in terms of :name.
Following examples will clarify the above concepts,
String selectQuery = “SELECT M FROM MOBILE WHERE M.MODEL = ?1 and M.MANUFACTURER = ?2”; Query selectQuery = entityManager.createQuery(selectQuery); selectQuery.setParameter(1, model); selectQuery.setParameter(2, manufacturer);
The above code illustrates the positional parameter support, during query execution,?1 and ?2 will be replaced with the values specified for model and manufacturer.
For named parameter, the parameter can be given with some meaningful name prefixed with :, like the below code,
String selectQuery = "SELECT M FROM MOBILE WHERE M.MODEL = :modelName and M.MANUFACTURER = :manufacturer"; Query selectQuery = entityManager.createQuery(selectQuery); selectQuery.setParameter(“modelName”, model); selectQuery.setParameter(“manufacturer”, manufacturer);
It is not that only dynamic queries can be benefited with positional and named parameters. Even for statically defined queries, the same is applicable. We can embed : or ? even in the static query definitions also,
@NamedQuery( name = "Mobile.findByModel” query = "SELECT M FROM MOBILEENTITY WHERE M.MODEL = :modelName ) …
And later in code,
Query namedQuery = entityManager.createNamedQuery("Mobile.findByModel"); namedQuery.setParameter("modelName", model);
Paging Query Results
Imagine that a select query is returning larger number of record entities, it may not be advisable to show the entire set of results to the end-user as it may eat performance. The following two methods can be used in tandem to solve this kind of performance issue.
Assume that we have 1000 mobile entities in our database and we wish to show only 100 objects in an application that displays them in a nice tabular view. In the following code, before calling the getResultList(), which will return all the mobile objects in the database, the query object is well configured with setMaxResults(maxResults) method (the number of maximum entity objects that will be returned) setFirstPosition(position) method (which tells starting position of the pointer is made to point to).
int maxRecords = 10; int startPosition = 0; String queryString = “SELECT M FROM MOBILEENTITY”; while(true){ Query selectQuery = entityManager.createQuery(queryString); selectQuery.setMaxResults(maxRecords); selectQuery.setFirstResult(startPosition); List mobiles = entityManager.getResultList(queryString); if (mobiles.isEmpty()){ break; } //Process the mobile entities. process(mobiles); entityManager.clear(); startPosition = startPosition + mobiles.size(); }
The above code loops through all the 100 objects in the database in a more efficient way. Also note that the EntityManager.clear() method is called every time, to clear (or to detach) the mobile objects which will make the EntityManager less burdened in terms of managing the mobile objects.
Flushing Query objects
JPA provides two modes of flushing query objects namely AUTO and COMMIT (which are defined in FlushModeType Enumeration). If the AUTO mode is set on the Query object which is the default one, then any changes made to entity objects will be reflected the very next time when a select query is made. That means that the persistence engine must make sure to update all the states of the entity objects which will affect the results of a SELECT query.
The AUTO flush mode can be set explicitly on the Query by calling setFlushMode().
queryObject.setFlushMode(FlushModeType.AUTO);
When the flush mode is set to COMMIT, then the persistence engine, may only update all the state of the entities during the database COMMIT. The dirty changes applied to entity objects and then querying for the results of the entities may be unspecified.
queryObject.setFlushMode(FlushModeType.COMMIT);[The method setFlushMode(FlushModeType) is available in both EntityManager and Query interface. If the EntityManager has been configured with setFlushMode(FlushModeType.AUTO) and the Query object has been called with setFlushMode(FlushModeType.COMMIT), then the preference will always goes to the Query object.]
Persistent Units
Before getting into the theory of what is a persistence unit and how it has relations with EntityManagerFactory and EntityManager, let us look into a sample persistence.xml file.
<?xml version="1.0" encoding="UTF-8"?> <persistence> <persistence-unit name="EmployeePersistentUnit" transaction-type="RESOURCE_LOCAL"> <provider>oracle.toplink.essentials.ejb.cmp3.EntityManagerFactoryProvider</provide> <class>com.javabeat.ejb30.persistence.entities.Employee</class> <properties> vproperty name="toplink.jdbc.url" value="jdbc:derby://localhost:1527/sample"/> <property name="toplink.jdbc.user" value="app"/> <property name="toplink.jdbc.driver" value="org.apache.derby.jdbc.ClientDriver"/> <property name="toplink.jdbc.password" value="app"/> <property name="toplink.ddl-generation" value="drop-and-create-tables"/> </properties> </persistence-unit> </persistence>[A persistence.xml file is used to manage one or more persistence units. Like web.xml for a servlet application and ejb-jar.xml for an EJB application, this persistence.xml is a must for an entity application to run.]
At a very high-level, we can infer multiple persistence units within the same persistence.xml file being uniquely identified by its name tag. Each persistence unit is configured with a provider along with its data-source and with some set of entities. So, we can say that a persistence unit provides a logical grouping for some set of entities that share some configuration.
Imagine an application is having so many entity objects and these entity objects have been nicely categorized in a way that some set of entities are sharing a common provider (Toplink, for example), acting on some data-source, whereas other set of entities are depending on a different provider (Hibernate, for example). In such a case, we can define a persistent unit with some configuration properties for some set of entities and a different persistent unit with different set of configuration properties for the remaining entities. So a persistent unit is a way to categorize some set of entities that aimed to do some common goals and common configurations to share with.
Back to the above sample code, we have two different persistent units, one being configured to point to Toplink provider with the help oftag, and the other one pointing to a hibernate provider. We also have put some vendor specific hints (or properties) through the property tag (found within the properties tag). The meaning of toplink.ddl-generation with value drop-and-create-tables essentially tells the Toplink persistence engine that during deployment time, if already there exists a table for the corresponding class MobileEntity, just drop (delete ) the table and create it before using. Similarly the property-value pair “hibernate.hbm2ddl.auto-update” tells the persistence engine, to use the existing table and not to recreate it. Thetag represents the name of entity class that is to be included as part of this persistent unit.
Now, that we have a basic understanding about persistent units that how they are useful in categorizing entity objects, now its time to see how EntityManager and persistent units are related. In a regular J2SE application, instances of EntityManager class are obtained through EntityManagerFactory which is turn is obtained through the boot-strap class Persistence.
The following shows the sample code to achieve the same,
String persistentUnitName = "MyMobilePersistentUnit"; EntityManagerFactoryfactory factory = Persistence .createEntityManagerFactory(persistentUnitName); // Line 1 EntityManager entityManager = factory.createEntityManager(); // Line 2
In line 1, an instance of EntityManagerFactory is obtained with a call to createEntityManagerFactory() with the name of the persistent unit being passed as an argument. At this stage, we know that the persistent unit by name MyMobilePersistentUnit has been configured with Toplink as its provider along with a data source object with some additional vendor specific properties. Therefore, EntityManager objects being created for this kind of configuration from the EntityManagerFactory have this configuration for their entire life-time.
In the case of a J2EE application , one can instantiate an instance of EntityManager object backed up with @PersistentUnit like this,
@PersistentUnit(unitName = "MyMobilePersistentUnit") private EntityManager entityManager;
Now, the EntityManager object will have the set of configuration parameters being loaded as referenced by the persistent unit name “MyMobilePersistentUnit”.