Hibernate cache
Caching is all about the performance optimization of the application and it is located between the application and the database to avoid multiple database accesses and allow performance-critical applications to perform better.
Caching is important for Hibernate, and it adopts a multi-level caching scheme described below:
Level 1 cache:
The first level cache is the Session cache, which is a mandatory cache, and all requests through it must be passed. Session objects are constantly being powered by objects before submitting them to the database.
If multiple updates are issued, Hibernate attempts to delay the updates for as long as possible to reduce the number of updates issued SQL statements. If you close the session, all cached objects will be lost, either persistent, or updated in the database.
Level 2 cache:
Level 2 cache is optional and Level 1 cache that will always be sought before any attempt to find an object's Level 2 cache. The second level cache can be configured on a per-class and per-category basis, mainly responsible for objects cached in session.
Any third-party cache can use Hibernate. The org.hibernate.cache.CacheProvider interface provides, and it is necessary to implement a handle cache implementation for providing Hibernate.
Query level cache:
Hibernate also implements the tight integration of query result set cache and level 2 cache.
This is an optional feature that requires two additional physical caches to save cached query results and regions when a table is last updated. This is just very useful for queries that often run with the same parameters.
Level 2 cache:
Hibernate uses Level 1 cache, by default, you do nothing with Level 1 cache. Let's go straight to the optional second level cache. Not all classes benefit from caching, so it is important to disable Level 2 cache.
Hibernate Level 2 cache is set to two steps. First, you must decide which concurrency strategy to use. After this, you can configure cache expiration and use cache to provide physical cache attributes.
Concurrency strategy:
A concurrency policy is a mediator responsible for storing data items in the cache and retrieving them from the cache. If you want to enable Level 2 caching, you will have to decide which cache concurrency policy to use for each persistent class and collection.
Transactional: Using this strategy to primarily read data to prevent concurrent transactions of outdated data is critical in rare cases of updates.
Read-write: Again using this strategy the main reading of data is critical to prevent concurrent transactions from stale data in rare cases of updates.
Nonstrict-read-write: This strategy does not guarantee consistency between cache and database. With this strategy, the key is not to pay attention if the data is rarely changed and the possibility of stale data is to be stale.
Read-only: The concurrency policy is suitable for data and will never change. The data used is for reference only.
If we want to use the second level cache as our Employee class, let's add the mapping elements required to tell Hibernate to use a readable and writeable cache policy for Employee instances.
<?xml version="1.0" encoding="utf-8"?><!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD//EN" "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd"> <hibernate-mapping> <class name="Employee" table="EMPLOYEE"> <meta attribute="class-description"> This class contains the employee detail. </meta> <cache usage="read-write"/> <id name="id" type="int" column="id"> <generator/> </id> <property name="firstName" column="first_name" type="string"/> <property name="lastName" column="last_name" type="string"/> <property name="salary" column="salary" type="int"/> </class></hibernate-mapping>
The usage="read-write" property tells Hibernate to use a cache defined by a read-write concurrency policy.
Cache Provider:
After considering the concurrency policy of your cache candidate class, the next step is to select a cache provider. Hibernate forces selecting a cache to serve the entire application.
Cache provided in the specified hibernate.cfg.xml configuration file. Select EHCache as the second level cache provider:
<?xml version="1.0" encoding="utf-8"?><!DOCTYPE hibernate-configuration SYSTEM "http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd"><hibernate-configuration> <session-factory> <property name="hibernate.dialect"> org.hibernate.dialect.MySQLDialect </property> <property name="hibernate.connection.driver_class"> com.mysql.jdbc.Driver </property> <!-- Assume students is the database name --> <property name="hibernate.connection.url"> jdbc:mysql://localhost/test </property> <property name="hibernate.connection.username"> root </property> <property name="hibernate.connection.password"> root123 </property> <property name="hibernate.cache.provider_class"> org.hibernate.cache.EhCacheProvider </property> <!-- List of XML mapping files --> <mapping resource="Employee.hbm.xml"/></session-factory></hibernate-configuration>
Now, you need to specify the properties of the cache area. EHCache has its own configuration file ehcache.xml, in the application in CLASSPATH. In ehcache.xml, the Employee class cache configuration might look like this:
<diskStore path="java.io.tmpdir"/><defaultCachemaxElementsInMemory="1000"eternal="false"timeToIdleSeconds="120"timeToLiveSeconds="120" overflowToDisk="true"/><cache name="Employee"maxElementsInMemory="500"eternal="true"timeToIdleSeconds="0"timeToLiveSeconds="0"overflowToDisk="false"/>
That's it, now enable the Employee class's secondary cache and Hibernate now have the secondary cache, whenever browsing to an employee or when the employee is loaded by an identifier.
You should analyze all your classes and select the appropriate caching strategy for each class. Sometimes, the secondary cache may degrade the performance of the application. So it is recommended to the benchmark application that does not enable caching for the first time, which is very suitable for caching and checking performance. If the cache does not improve system performance, it is meaningless to make any type of cache.
Query level cache:
Using query cache, it must be activated first in the hibernate.cache.use_query_cache="true" property configuration file. If this property is set to true, let Hibernate create the required cache in memory to save the query and identifier set.
Next, using query cache, you can use the setCacheable (Boolean) method of the Query class. For example:
Session session = SessionFactory.openSession();Query query = session.createQuery("FROM EMPLOYEE");query.setCacheable(true);List users = query.list();SessionFactory.closeSession();Hibernate also supports very fine-grained cache support through the concept of a cache area. The cache is part of the cache given a name.
Session session = SessionFactory.openSession();Query query = session.createQuery("FROM EMPLOYEE");query.setCacheable(true);query.setCacheRegion("employee");List users = query.list();SessionFactory.closeSession();This code uses a method to tell Hibernate to store and find queries on employees in the cache.
Hibernate native SQL
You can use native SQL to express database queries. If you want to use database-specific functions, such as query prompts or CONNECT keywords in Oracle. Hibernate3.x allows the use of handwritten SQL statements, including stored procedures, all creation, update, delete and load operations.
The application will create a native SQL query (on the Session interface) from the session createSQLQuery() method:
public SQLQuery createSQLQuery(String sqlString) throws HibernateException
When passing a SQL query to createSQLQuery() method, you can use the addEntity() method associated with any existing Hibernate entity, or a scalar result using the addEntity() method, addJoin(), and addScalar() method.
Scalar query:
The most basic SQL query is to get a list of scalars (numeric values) from one or more tables. Here are the values of the syntax using native SQL scalars:
String sql = "SELECT first_name, salary FROM EMPLOYEE";SQLQuery query = session.createSQLQuery(sql);query.setResultTransformer(Criteria.ALIAS_TO_ENTITY_MAP);List results = query.list();
Entity query:
The above queries all return scalar values, that is, the "naked" data returned from the resultset. The following is the syntax to obtain entity objects as a whole from native SQL query through the addEntity() method.
String sql = "SELECT * FROM EMPLOYEE";SQLQuery query = session.createSQLQuery(sql);query.addEntity(Employee.class);List results = query.list();
Named SQL Query:
The following is the syntax to obtain entity objects from native SQL queries and use named SQL queries through the addEntity() method.
String sql = "SELECT * FROM EMPLOYEE WHERE id = :employee_id";SQLQuery query = session.createSQLQuery(sql);query.addEntity(Employee.class);query.setParameter("employee_id", 10);List results = query.list(); Native SQL example:
Consider the following POJO class:
public class Employee { private int id; private String firstName; private String lastName; private int salary; public Employee() {} public Employee(String fname, String lname, int salary) { this.firstName = fname; this.lastName = lname; this.salary = salary; } public int getId() { return id; } public void setId( int id ) { this.id = id; } public String getFirstName() { return firstName; } public void setFirstName( String first_name ) { this.firstName = first_name; } public String getLastName() { return lastName; } public void setLastName( String last_name ) { this.lastName = last_name; } public int getSalary() { return salary; } public void setSalary( int salary ) { this.salary = salary; }}Let's create the following EMPLOYEE table to store the Employee object:
create table EMPLOYEE ( id INT NOT NULL auto_increment, first_name VARCHAR(20) default NULL, last_name VARCHAR(20) default NULL, salary INT default NULL, PRIMARY KEY (id));
The following will be mapped files.
<?xml version="1.0" encoding="utf-8"?><!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD//EN" "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd"> <hibernate-mapping> <class name="Employee" table="EMPLOYEE"> <meta attribute="class-description"> This class contains the employee detail. </meta> <id name="id" type="int" column="id"> <generator/> </id> <property name="firstName" column="first_name" type="string"/> <property name="lastName" column="last_name" type="string"/> <property name="salary" column="salary" type="int"/> </class></hibernate-mapping>
Finally, we will create the main() method of the application class to run, and we will use native SQL querying applications:
import java.util.*; import org.hibernate.HibernateException; import org.hibernate.Session; import org.hibernate.Transaction;import org.hibernate.SessionFactory;import org.hibernate.SQLQuery;import org.hibernate.Criteria;import org.hibernate.Hibernate;import org.hibernate.cfg.Configuration;public class ManageEmployee { private static SessionFactory factory; public static void main(String[] args) { try{ factory = new Configuration().configure().buildSessionFactory(); }catch (Throwable ex) { System.err.println("Failed to create sessionFactory object." + ex); throw new ExceptionInInitializerError(ex); } ManageEmployee ME = new ManageEmployee(); /* Add few employee records in database */ Integer empID1 = ME.addEmployee("Zara", "Ali", 2000); Integer empID2 = ME.addEmployee("Daisy", "Das", 5000); Integer empID3 = ME.addEmployee("John", "Paul", 5000); Integer empID4 = ME.addEmployee("Mohd", "Yasee", 3000); /* List down employees and their salary using Scalar Query */ ME.listEmployeesScalar(); /* List down complete employees information using Entity Query */ ME.listEmployeesEntity(); } /* Method to CREATE an employee in the database */ public Integer addEmployee(String fname, String lname, int salary){ Session session = factory.openSession(); Transaction tx = null; Integer employeeID = null; try{ tx = session.beginTransaction(); Employee employee employee = new Employee(fname, lname, salary); employeeID = (Integer) session.save(employee); tx.commit(); }catch (HibernateException e) { if (tx!=null) tx.rollback(); e.printStackTrace(); } finally { session.close(); } return employeeID; } /* Method to READ all the employees using Scalar Query */ public void listEmployeesScalar( ){ Session session = factory.openSession(); Transaction tx = null; try{ tx = session.beginTransaction(); String sql = "SELECT first_name, salary FROM EMPLOYEE"; SQLQuery query = session.createSQLQuery(sql); query.setResultTransformer(Criteria.ALIAS_TO_ENTITY_MAP); List data = query.list(); for(Object object: data) { Map row = (Map)object; System.out.print("First Name: " + row.get("first_name")); System.out.println(", Salary: " + row.get("salary")); } tx.commit(); }catch (HibernateException e) { if (tx!=null) tx.rollback(); e.printStackTrace(); } finally { session.close(); } } /* Method to READ all the employees using Entity Query */ public void listEmployeesEntity( ){ Session session = factory.openSession(); Transaction tx = null; try{ tx = session.beginTransaction(); String sql = "SELECT * FROM EMPLOYEE"; SQLQuery query = session.createSQLQuery(sql); query.addEntity(Employee.class); List employees = query.list(); for (Iterator iterator = employees.iterator(); iterator.hasNext();){ Employee employee = (Employee) iterator.next(); System.out.print("First Name: " + employee.getFirstName()); System.out.print(" Last Name: " + employee.getLastName()); System.out.println(" Salary: " + employee.getSalary()); } tx.commit(); } catch (HibernateException e) { if (tx!=null) tx.rollback(); e.printStackTrace(); } finally { session.close(); } }} Compile and execute:
Here are the steps to compile and run the above application. Please make sure that PATH and CLASSPATH are set appropriately before compiling and executing.
Execute the ManageEmployee binary file to run the program.
The following results will be obtained and the record will be created in the EMPLOYEE table.
$java ManageEmployee
......VARIOUS LOG MESSAGES WILL DISPLAY HERE.........First Name: Zara, Salary: 2000First Name: Daisy, Salary: 5000First Name: John, Salary: 5000First Name: Mohd, Salary: 3000First Name: Zara Last Name: Ali Salary: 2000First Name: Daisy Last Name: Das Salary: 5000First Name: John Last Name: Paul Salary: 5000First Name: Mohd Last Name: Yasee Salary: 3000
If you check the EMPLOYEE table, it should record that it has:
mysql> select * from EMPLOYEE;
+----------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------