Wednesday 31 May 2017

Managing Entities in JPA

In part-1 of this series, I just introduced JPA. In this post, I discuss on the life of an entity and briefly explain how to query entities using JPQL.  It helps you understand and develop JPA based applications.

Life Cycle of an Entity

As a developer, it is very crucial for you to understand how the EntityManager manages the entity. The EntityManager performs several operations to manage entity instances. An entity instance moves from one state to another during these operations. The entity instance attains four states such as: New, Managed, Removed and Detached in its life. The diagram in Figure-1 depicts these states and their transition.


Figure-1: Entity Life Cycle 

When an entity instance is initially created in the application, it attains the New state. The new entity instance just created has no persistent identity, is not yet associated with a persistence context and has no representation in the database.

The entity instance attains Managed state when an attempt is made to save it to the database using persist() method of the EntityManager. The managed entity instance has a persistent identity and is associated with a persistence context.    The persist() method must be invoked within an active transaction context. On transaction commit, the new entity instance is saved to the database. When an entity instance is retrieved from the database by an EntityManager using its find() method or through a query execution, the entity instance attains the managed state. If a managed entity instance is modified within an active transaction the update is propagated to the database on transaction commit.  When EntityManager invokes its refresh() method on a managed entity, the state of the entity instance gets refreshed from the database overwriting changes made to the entity, if any.

When the EntityManager invokes its remove() method on a managed entity within an active transaction, the entity instance is scheduled for removal from the database. The entity instance moves to Removed state from managed state. The removed entity instance has a persistent identity and is associated with a persistent context.  It is physically deleted from the database on transaction commit.

A managed entity attains a Detached state when the EntityManager invokes its detach() method on it. In the detached state, the entity instance has a persistence identity and is not associated with a persistent context.  A detached entity instance is not managed by any EntityManager but still represent data in the database. Changes made to a detached entity instance are not stored in the database unless modified detached instance is merged back into the persistent context to become managed again.  All the entity instances associated with an EntityManager become detached when the EntityManager is closed. Also all the entity instances are detached when the clear() method of the EntityManager is invoked which clears the persistence context associated with the EntityManager.

The entity in a persistent context is synchronized to the database when the active transaction with which the entity is associated commits. To force synchronization of the managed entity to the underlying database, the flush() method of the EntityManager instance is invoked. If the entity is in the removed state, flush forces removal of the entity from the database.

Querying Entities
JPA provides a query language, JPQL (Java Persistence Query Language) to query entities efficiently using the EntityManager. It is similar to SQL in its syntax, object-oriented, well expressive and more flexible than traditional SQL. Unlike SQL query which directly deals with table and column names, a query in JPQL operates over persistent entity schema in the application. The JPQL query is translated into native SQL query and is executed over the database schema to which the entities are mapped. Arbitrary identifiers are assigned to entities so that they can be referenced elsewhere in the query.
For example, consider a simple select query for all the Employee entities:
SELECT e FROM Employee e
In the query example above, the identifier e is assigned to the entity Employee.

Creating Queries
Depending upon how the query string is defined, queries can be classified into two different types: Static query and Dynamic query.  

Static Query: A static query (or named query) is one which is defined in metadata by using @NamedQuery annotation before the entity class. The name element of @NamedQuery specifies the name of the query while the query element of @NamedQuery is the query itself.

For example,
@NamedQuery(name = "Employee.findAll"
            query = “SELECT e FROM Employee e”)
@Entity
public class Employee{
...
     }

The createNamedQuery() method of the EntityManager instance is used to create a static query referring by its name as shown below:
Query query = em.createNamedQuery("Employee.findAll");


Since these named queries are statically defined, during deployment time itself, the persistent provider may parse and translate the JPQL into native SQL, cache them, thereby yielding better performance.

Dynamic Query: A Dynamic Query is created directly using the createQuery() method of the EntityManager instance within a method  of some class that deals with application’s business logic. For example,
public List getEmployeeList() {
       
        Query query = em.createQuery("select e from Employee e");
        List empList = query.getResultList();  // query execution
        return empList;
    }

Since query translation from JPQL to SQL occurs at run-time, the query execution may become slow. Thus static queries are better choice if the scenario for which the query to be executed is well known in advance.

Executing Queries

The Query interface provides separate methods to execute updatable statements such as UPDATE and DELETE and non-updatable statement such as SELECT query.

A SELECT query can be of two types based on the result it return on execution: single result query and multiple results query. The Query interface has two separate methods named getSingleResult() and getResultList() to deal with the situations.

Single Result
An invocation of the method getSingleResult()  of a query instance executes the query and returns an untyped object representing a single row matching the criteria in the query string. If the match fails, then the method throws an exception signifying no result. Also, if more than one match found on query execution, the method throws an exception.

The following code shows how to execute a SELECT query that returns a single entity.

Query sqry = 
            em.createQuery("SELECT e FROM Employee e WHERE e.empid = 123");
            Employee e = (Enployee) sqry.getSingleResult();

Multiple Results

The method getResultList() executes a query and returns the query result as an untyped List object containing multiple entity instances. The following code shows the same:

     Query mqry =
                   entityManager.createQuery("SELECT e FROM Employee e");
            List employees = mqry.getResultList();

Further, both the methods only execute SELECT statements and throw an IllegalStateException exception if UPDATE or DELETE statements are used in place.
The Query interface has executeUpdate() method to execute UPDATE and DELETE statements. When the method is invoked on the query, it returns the number of entities updated or deleted. The following code shows how to execute such statements:

Query uqry =
                   em.createQuery("UPDATE Employee e SET e.designation=’Manager’");
            int uCount = uqry.executeUpdate();

Parameters in Query

Parameterized queries may offer efficiency when executed. In JPQL, parameterized query execution is similar to the PreparedStatement interface in JDBC API. In this case the query is created once and can be executed again and again by setting values for the parameters. The parameter values can be set using setParameter() method of the Query interface in JPQL. JPQL supports two types of parameters in queries: Positional and Named.
Positional Parameters
Positional parameters are prefixed with a question mark (?) followed the number denoting the position of the parameter in the query.  The following example shows a parameterized query with positional parameters:

String strQry =
       “SELECT e FROM Employee e 
        WHERE e.empname = ?1 and e.designation = ?2”;
     Query pQry = em.createQuery(strQry);
     pQry.setParameter(1, eName);
     pQry.setParameter(2, eDesignation);

The above example, the positional parameters ?1 and ?2 will be replaced with the values of the variables eName and eDesignation provided using setParameter() methods.

Named Parameters
Similarly named parameters are query parameters that are prefixed with a colon(:). The following example shows how to use named parameters in aquery:

String strQry = 
       "SELECT e FROM Employee e WHERE e.empname =
       :eName and e.designation = :eDesignation";

     Query nQry = em.createQuery(strQry);

     nQry.setParameter(“eName”, aName);
     nQry.setParameter(“eDesignation”, aDesignation);

Conclusion
In this post, I covered entity life cycle and briefly introduced JPQL query. In the next, post of this series, I will show you how to develop a simple JPA based application using a suitable development environment. Keep following me at this site.


Share:

0 comments:

Post a Comment