Pages

Tuesday, 8 January 2013

Hibernate as Persistence Provider and ORM Solution - IV


Versioned Objects in Hibernate/JPA

Suppose two users access same record of a table simultaneously. After some time one of the user updates the record but still the other user uses the old data that was loaded when it was first accessed. Now if the second user in this case tries to update the record without refreshing it (to get its updated version) then he might overwrite the changes made by the first user.
In order to handle the above scenario the versioned entities are used. This versioning strategy is also known as optimistic locking. Following is the code that shows how to enable optimistic locking in Hibernate/JPA. 

@Entity
public class MyEntity implements Serializable {    
   //...
   @Version
   private Long version;
   //...
}



In optimistic locking whenever a record is accessed its version is noticed and this version is compared before the same record is updated. If the version before updating the record is found to be same then it means no change has been made after the record was first accessed. However the difference in the version found before updating the record shows that it has already been updated by some other transaction after it was first accessed.
In the background the version field is added to the filter criteria while fetching the data along with other necessary filters in the query. In the above scenario the actual record version was updated but the WHERE clause still used the old version to look up the same record that was first accessed which in return matches nothing and no row is returned by the SELECT query. In such cases Hibernate throws org.hibernate.StaleObjectStateException (wrapped in a javax.persistence.OptimisticLockException). The possible exception handling could be to prompt user to refresh the data from UI or internally refresh the stale object whenever StaleObjectStateException is thrown.
Note: The version field is not necessarily a number. It might be a timestamp or any other unique value. 


Different types of queries available in Hibernate and their Usage

Hibernate supports three types of query languages which are as follows;

1. HQL (Hibernate Query Language)
2. JPQL (JPA Query Language)
3. Native SQL

All of above types of queries can optionally be used as Named Queries. The named query is added in the entity class and therefore we do not have to scatter the HQL, JPQL or Native SQL code over different places within the Java code. I personally believe that as long as the architecture of the application is well designed and all the queries are placed only within the designated layer (lets say DAO layer) the Named Queries should not be used.
However on the other hand a badly designed application may have queries scattered over multiple layers in that case it seems to be preferable to use Named Queries to avoid maintainability issues.
Note: With the exception of names of Java classes and properties, HQL/JPQL queries are case-insensitive.

HQL Queries

HQL queries are compatible with the hibernate native ORM implementation. Following is the code to create HQL queries.

 List<Customer>
 customers = (List<Customer>)session.createQuery("FROM Customer").list(); 


Following is the code to create HQL Named queries.

 @NamedQueries(
    {
         @NamedQuery(name = "getAllCustomers", query = "FROM Customer")
    }
 )
 @Entity
 @Table(name = "customer")
 public class Customer implements java.io.Serializable {
 ...



List<Customer> customers =
(List<Customer>)session.getNamedQuery("getAllCustomers").list();



JPQL Queries

JPQL queries are compatible with the hibernate JPA implementation. Following is the code to create JPQL queries.

 List<Customer> 
 customers =(List<Customer>)entityManager.createQuery("SELECT c FROM Customer c").getResultList(); 



Following is the code to create JPQL Named queries.

 @NamedQueries(
    {
      @NamedQuery(name = "getAllCustomers", query = "SELECT c FROM Customer c")
    }
 )
 @Entity
 @Table(name = "customer")
 public class Customer implements java.io.Serializable {
 ...



 List<Customer> customers =
  (List<Customer>)entityManager.createNamedQuery("getAllCustomers").getResultList();


Native Queries

Native queries are compatible with both hibernate native ORM implementation as well as its JPA implementation. Native queries provide great flexibility in that they let us write simple SQL queries that can easily be optimized. However while using native queries we should be very sure that using HQL/JQPL queries is less convenient in that particular situation. 

a. Hibernate native ORM implementation
We can bind native SQL to an entity by adding invoking the addEntity method on the SQL Query object. There doesn't exist any table for the entity class bound to a native SQL query, it is just a Java class mapped to an SQL query (isn't it powerful?). Following is the code to create native queries with hibernate native ORM implementation.

query = session.createSQLQuery("SELECT ename, job, dept.deptno, dname "
                             + "FROM emp, dept "
                             + "WHERE emp.deptno (+) = dept.deptno AND job (+) = 'MANAGER'").addEntity(Employee.class);

List<Employee> list = query.list();

Following is the code to create named native queries with hibernate native ORM implementation. 

 @NamedNativeQueries(
    {
      @NamedNativeQuery(name = "findEmployees",query = "SELECT ename, job, dept.deptno, dname "
      + "FROM emp, dept "
      + "WHERE emp.deptno (+) = dept.deptno AND job (+) = 'MANAGER'",
      resultClass =  Employee.class)
    }
 )
 @Entity
 @Table(name = "employee")
 public class Employee implements java.io.Serializable {
 ...



 List<Employee> employees =
(List<Employee>)session.getNamedQuery("findEmployees").list();



b. Hibernate JPA implementation
Following is the code to create native queries with hibernate JPA implementation.

Query query =
entityManager.createNativeQuery("SELECT ename, job, dept.deptno,
dname FROM emp, dept WHERE emp.deptno (+) = dept.deptno AND job (+) =
'MANAGER'",Employee.class);

List<Employee> list = query.getResultList();



Following is the code to create named native queries with hibernate JPA implementation.

 @NamedNativeQueries(
    {
     @NamedNativeQuery(name = "findEmployees",query = "SELECT ename, job, dept.deptno, dname "
     + "FROM emp, dept "
     + "WHERE emp.deptno (+) = dept.deptno AND job (+) = 'MANAGER'",
     resultClass =  Employee.class)
    }
 )
 @Entity
 @Table(name = "employee")
 public class Employee implements java.io.Serializable {
 ...



List<Employee> employees =
(List<Employee>)entityManager.createNamedQuery("findEmployees").getResultList();


Preventing SQL injection in Hibernate

Always use indexed parameters or named parameters in all (HQL,JPQL and native SQL) queries which ensures that the parameter values will always be validated or escaped appropriately by the JDBC driver. Thus with the use of these parameters SQL injections can easily be prevented. Using named parameters keeps the code more readable as compared to the indexed parameters. Following are some simple examples.

/* Positional parameter in HQL */
Query query =
session.createQuery("from Orders as orders where orders.id = ?");
List results = query.setString(0, "12").list();

/* Named parameter in HQL */
Query query =
session.createQuery("from Employees as emp where emp.bonus >:bonus");
List results = query.setLong("bonus", new Long(12000)).list();



Flushing and Refreshing

Flushing and Refreshing are the processes that synchronize the data between application and database layers. The entityManager.flush() synchronizes all the changes that are made to the persistent entities back to the underlying database, whereas the entityManager.refresh() method does the reverse. It updates the entity object with values taken from the database. Any new values that are set to the entity objects are lost after calling refresh() method.