In this example im trying to create a simple application using Hibernate’s caching feature. What is cache anyway? A cache is designed to reduce traffic between your application and the database by conserving data already loaded from the database and put it whether in memory or in file. Database access is necessary only when retrieving data that is not currently available in the cache. So basically not all queries are taken from database, but from cache instead.
Hibernate use EHCache for default caching, but now im trying to demonstrate both OSCache and EHCache caching library. But you can only use one of it at one time. Both of them only differ in caching configuration, but the rest are still the same.
First is a simple sql file, a java bean and its hibernate xml configuration
CREATE TABLE matakuliah ( kodematakuliah VARCHAR(10) NOT NULL, namamatakuliah VARCHAR(20), PRIMARY KEY (kodematakuliah) ) ENGINE=InnoDB DEFAULT CHARSET=latin1; INSERT INTO matakuliah ( kodematakuliah, namamatakuliah ) VALUES ( '123', 'ccc' );
package com.edw.bean; public class Matakuliah implements java.io.Serializable { public class Matakuliah implements java.io.Serializable { private String kodematakuliah; private String namamatakuliah; public Matakuliah() { } public Matakuliah(String kodematakuliah) { this.kodematakuliah = kodematakuliah; } public Matakuliah(String kodematakuliah, String namamatakuliah) { this.kodematakuliah = kodematakuliah; this.namamatakuliah = namamatakuliah; } // other setters and getters }
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd"> <hibernate-mapping> <class catalog="kampus" name="com.edw.bean.Matakuliah" table="matakuliah"> <!-- caching configuration --> <cache usage="read-write" /> <id name="kodematakuliah" type="string"> <column length="10" name="kodematakuliah"/> <generator class="assigned"/> </id> <property name="namamatakuliah" type="string"> <column length="20" name="namamatakuliah"/> </property> </class> </hibernate-mapping>
please take a look at line 7, im using read-write because my data sometimes get updated. If you are planning to have static data that never changes, use read-only.
Next is registering EhCacheProvider on hibernate.cfg.xml file
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE hibernate-configuration PUBLIC "-//Hibernate/Hibernate Configuration DTD 3.0//EN" "http://hibernate.sourceforge.net/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> <property name="hibernate.connection.url">jdbc:mysql://localhost:3307/kampus</property> <property name="hibernate.connection.username">root</property> <property name="hibernate.connection.autocommit">true</property> <property name="hibernate.cache.use_second_level_cache">true</property> <property name="hibernate.cache.use_query_cache">true</property> <property name="hibernate.cache.provider_class">org.hibernate.cache.EhCacheProvider</property> <property name="hibernate.show_sql">true</property> <property name="hibernate.format_sql">true</property> <mapping resource="com/edw/bean/Matakuliah.hbm.xml"/> </session-factory> </hibernate-configuration>
create a java file to load your hibernate configuration file
package com.edw.util; import org.hibernate.cfg.AnnotationConfiguration; import org.hibernate.SessionFactory; public class HiberUtil { private static final SessionFactory sessionFactory; static { try { sessionFactory = new AnnotationConfiguration().configure().buildSessionFactory(); } catch (Throwable ex) { throw new ExceptionInInitializerError(ex); } } public static SessionFactory getSessionFactory() { return sessionFactory; } }
and dont forget your ehcache.xml configuration
<?xml version="1.0" encoding="UTF-8"?> <!-- caching configuration --> <ehcache> <defaultCache maxElementsInMemory="10" eternal="false" timeToIdleSeconds="0" timeToLiveSeconds="0" overflowToDisk="false"/> </ehcache>
and this is my java file to test hibernate’s caching ability
package com.edw.main.caching; import com.edw.bean.Matakuliah; import com.edw.util.HiberUtil; import java.util.Date; import org.hibernate.Session; public class CachingMain { public CachingMain() { } /** * do some repeated queries for table Matakuliah * query results are taken from cache memory instead of database */ private void withCache() { Session session = null; try { for (int i = 0; i < 3; i++) { // open session session = HiberUtil.getSessionFactory().openSession(); // time needed long now = new Date().getTime(); // select Matakuliah matakuliah = (Matakuliah)session.load(Matakuliah.class, "123"); // print System.out.println("matakuliah "+matakuliah.getNamamatakuliah()); System.out.println("Time : " + (new Date().getTime() - now) + " ms"); // sleep for 3seconds Thread.sleep(3000); } } catch (Exception e) { e.printStackTrace(); } finally { if (session != null) { session.close(); } } } public static void main(String[] args) { CachingMain main = new CachingMain(); main.withCache(); } }
you can see in your console, if you use log4j, this is what happen when Hibernate is querying from cache instead of database
DEBUG org.hibernate.impl.SessionImpl:220 - opened session at timestamp: 5364197853302784 DEBUG org.hibernate.jdbc.ConnectionManager:404 - aggressively releasing JDBC connection DEBUG org.hibernate.impl.SessionImpl:832 - initializing proxy: [com.edw.bean.Matakuliah#123] DEBUG org.hibernate.cache.EhCache:68 - key: com.edw.bean.Matakuliah#123 DEBUG org.hibernate.engine.StatefulPersistenceContext:790 - initializing non-lazy collections matakuliah ccc Time : 1 ms
Moving from EhCache to OsCache is very simple, all you have to do is replacing 1 line in your xml file, and adding an oscache.properties file.
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE hibernate-configuration PUBLIC "-//Hibernate/Hibernate Configuration DTD 3.0//EN" "http://hibernate.sourceforge.net/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> <property name="hibernate.connection.url">jdbc:mysql://localhost:3307/kampus</property> <property name="hibernate.connection.username">root</property> <property name="hibernate.connection.autocommit">false</property> <property name="hibernate.cache.use_second_level_cache">true</property> <property name="hibernate.cache.use_query_cache">true</property> <property name="hibernate.cache.provider_class">org.hibernate.cache.OSCacheProvider</property> <property name="hibernate.show_sql">true</property> <property name="hibernate.format_sql">true</property> <mapping resource="com/edw/bean/Matakuliah.hbm.xml"/> </session-factory> </hibernate-configuration>
and this is my oscache.properties
cache.capacity=1000 cache.path=c:\\cache\\ cache.persistence.class=com.opensymphony.oscache.plugins.diskpersistence.HashDiskPersistenceListener
this is what happen on my Netbeans console when im using OsCache
DEBUG org.hibernate.impl.SessionImpl:220 - opened session at timestamp: 5364208706285568 DEBUG org.hibernate.jdbc.ConnectionManager:404 - aggressively releasing JDBC connection DEBUG org.hibernate.impl.SessionImpl:832 - initializing proxy: [com.edw.bean.Matakuliah#123] DEBUG com.opensymphony.oscache.base.algorithm.AbstractConcurrentReadCache:694 - get called (key=com.edw.bean.Matakuliah#123.com.edw.bean.Matakuliah) DEBUG org.hibernate.engine.StatefulPersistenceContext:790 - initializing non-lazy collections matakuliah ccc Time : 1 ms
Hope it can help others. Have fun. (*)