Hibernate makes it very easy for us to use caching framework of our own choice. Now if you know the application that your developing is going to be deployed in WebSphere Application Server, then you might want to use the
DynaCache caching framework which ships with IBM WebSphere Application Server. I wanted to try that so i did create this sample application that talks to database using Hibernate and makes use of DynaCache for caching. You can download the sample application from
here
In order to ask Hibernate to use DynaCache as caching implementation first thing that you will have to do is create a class that implements
org.hibernate.cache.CacheProvider
interface, this interface gets invoked whenever Hibernate wants to initialize cache for particular region.
package com.webspherenotes.hibernate.cache;
import java.util.Date;
import java.util.Properties;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import org.hibernate.cache.Cache;
import org.hibernate.cache.CacheException;
import org.hibernate.cache.CacheProvider;
import com.ibm.websphere.cache.DistributedMap;
public class DynaCacheProvider implements CacheProvider {
public DynaCacheProvider() throws NamingException {
System.out.println("Entering DynaCacheProvider() constructor");
}
public Cache buildCache(String regionName, Properties properties)
throws CacheException {
System.out.println("Inside DynaCacheProvider.buildCache " + regionName);
String cacheInstanceJNDIName =(String) properties.get(regionName);
if(cacheInstanceJNDIName == null){
cacheInstanceJNDIName = (String) properties.get("dynacache.default.cacheinstancename");
if(cacheInstanceJNDIName == null){
cacheInstanceJNDIName = "services/cache/distributedmap";
}
}
System.out.println("Cache instance JNDI name " + cacheInstanceJNDIName);
DistributedMap distributedMap;
DynaCache cache = null;
try {
InitialContext ic = new InitialContext();
distributedMap = (DistributedMap) ic.lookup(cacheInstanceJNDIName);
System.out.println("DistributedMap found in JNDI " + distributedMap);
cache = new DynaCache(distributedMap, regionName);
} catch (NamingException e) {
e.printStackTrace(System.out);
}
return cache;
}
public boolean isMinimalPutsEnabledByDefault() {
return false;
}
public long nextTimestamp() {
return new Date().getTime();
}
public void start(Properties properties) throws CacheException {
}
public void stop() {
}
}
,.
The
buildCache
method is responsible for returning object of class implementing
org.hibernate.cache.Cache
interface that should be used for caching objects for this region. In my case i want to have granular control on which DynaCache instance should be used to store the cached object. The rule goes like this
- First check if there is region specific instance, if yes use that
- If there is no region specific instance check the value of
dynacache.default.cacheinstancename
to see if that's set if yes use it.
- The last option is to use
services/cache/distributedmap
which is name of the default cache instance in WAS
Next i had to create
DynaCache
class that implements
org.hibernate.cache.Cache
, the object of this class gets control for getting, setting, removing entries from cache. I am actually calling methods on
DistributedMap
package com.webspherenotes.hibernate.cache;
import java.util.Map;
import org.hibernate.cache.Cache;
import org.hibernate.cache.CacheException;
import com.ibm.websphere.cache.DistributedMap;
public class DynaCache implements Cache {
private DistributedMap map;
private String regionName;
public DynaCache(DistributedMap map, String regionName) {
System.out.println("Creating object of HibernateCache() " + regionName);
this.map = map;
this.regionName = regionName;
}
public void clear() throws CacheException {
map.clear();
}
public Object get(Object key) throws CacheException {
System.out.println("Inside HibernateCache.get " + key);
return map.get(getMapKey(key));
}
public String getRegionName() {
System.out.println("Inside HibernateCache.getRegionName " + regionName);
return regionName;
}
public void put(Object key, Object value) throws CacheException {
System.out.println("Inside HibernateCache.put " + key + " " + value);
System.out.println(value.getClass().getName());
map.put(getMapKey(key), value);
}
public Object read(Object key) throws CacheException {
System.out.println("Inside HibernateCache.read " + key);
return map.get(getMapKey(key));
}
public void remove(Object key) throws CacheException {
map.remove(getMapKey(key));
}
public void update(Object key, Object value) throws CacheException {
map.put(getMapKey(key), value);
}
private String getMapKey(Object key) {
return regionName + "." + key;
}
@Override
public void destroy() throws CacheException {
}
@Override
public long getElementCountInMemory() {
return this.map.size();
}
@Override
public long getElementCountOnDisk() {
return 0;
}
@Override
public long getSizeInMemory() {
return 0;
}
@Override
public int getTimeout() {
return 0;
}
@Override
public void lock(Object arg0) throws CacheException {
}
@Override
public long nextTimestamp() {
return 0;
}
@Override
public Map toMap() {
return this.map;
}
@Override
public void unlock(Object arg0) throws CacheException {
}
}
Last thing is to define the
com.webspherenotes.hibernate.cache.DynaCacheProvider
as cacheProvider in hibernate.cfg.xml this tells the Hibernate framework to use the DynaCacheProvider that we just created.
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-configuration PUBLIC
"-//Hibernate/Hibernate Configuration DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd">
<hibernate-configuration>
<session-factory>
<!-- Database connection settings -->
<property name="connection.driver_class">org.apache.derby.jdbc.ClientDriver</property>
<property name="connection.url">jdbc:derby://localhost:1527/C:/data1/contact</property>
<property name="connection.username">dbadmin</property>
<property name="connection.password">dbadmin</property>
<!-- JDBC connection pool (use the built-in) -->
<property name="connection.pool_size">1</property>
<!-- SQL dialect -->
<property name="dialect">org.hibernate.dialect.DerbyDialect</property>
<property name="cache.provider_class">com.webspherenotes.hibernate.cache.DynaCacheProvider</property>
<property name="dynacache.default.cacheinstancename">services/cache/distributedmap</property>
<property name="com.javaworld.memcache.Contact">services/cache/contact</property>
<property name="hibernate.cache.use_second_level_cache">true</property>
<property name="cache.use_query_cache">true</property>
<property name="">localhost:11211</property>
<!-- Echo all executed SQL to stdout -->
<property name="show_sql">true</property>
<!-- Drop and re-create the database schema on startup
<property name="hbm2ddl.auto">create</property>-->
<mapping resource="com/javaworld/memcache/Contact.hbm.xml"/>
<mapping resource="com/javaworld/memcache/Address.hbm.xml"/>
</session-factory>
</hibernate-configuration>
In this file i am telling hibernate to use
service/cache/contact
cache instance for caching Contact object but the Address objects would get stored in the default cache. So after deploying the code i did hit the cache few times and this what i see in the cachemonitor