Hibernate is an open source O-R mapping tool which is being used extensively for domain modeling as well as bridge between the Object Oriented and Relational design paradigm. It is basically used to map the Object oriented domain entities to a corresponding relational container.
Hibernate’s architecture offer caching of data at 2 levels:
1. Within a session aka. Transaction
2. Outside the session.
Caching within the session is provided using the “Session” API in hibernate which maintains a transaction scoped cache of persistent data abstracted within itself.
Hibernate provides a plug and play architecture to integrate a second level cache to provide caching across transactions i.e. Session.
In this case study we will use a scenario to configure JBoss TreeCache as hibernate second level cache.
JBoss Cache has dependency on the following libraries which should be in the classpath of the runtime environment where the cache is being configured i.e. for JBoss 4.2 “..\jboss-4.2.3.GA\server\default\lib”.
1. jbosscache-core.jar
2. commons-logging.jar
3. jboss-common-core.jar
4. jgroups.jar
5. Hibernate-Jbosscache2.jar
Jboss Cache is also dependent on the javax.transaction package which will be available either through the JEE jars in the application server classpath or through hibernate jars.
One instance of cache is identified in the configuration as “Cache Region”. The cache regions can be load balanced / clustered using the application server’s load balancing attributes. We can create more than one cache regions for a given application depending upon the characteristics of the data being cached. i.e. Domain entities, Files, demographics information etc. Advantage of segregating the data amongst different cache regions is that we can register different initialization as well as invalidation policies for each one of them.
Using Jboss Cache as a second level cache with hibernate, we can cache following entities:
1. Entities
2. Collections
3. Query results
We need to be careful with query results while configuring a cluster as replication of query results across cluster is very costly. Hence we should have good reasons for configuring the query results across cluster.
Before proceeding forward we need to be aware of the dependencies between hibernate versions as well as JBoss cache version. For all hibernate versions above 3.3.x the recommended JBoss Cache versions to be used are 3.x. JBoss AS 5.x and above supports Cache version 3.x and hibernate version 3.3.x. In any other scenario proper dependencies will have to be verified and dependent jars needs to be copied to the AS classpath to make it work.
Best way to register JBoss cache in the Application Server is via mbeans. We need to provide an xml file to register JBoss cache within application server as mbean i.e. in the directory “..\jboss-4.2.3.GA\server\default\deploy” for JBoss AS 4.2.x . This will also provide a name to the cache service. Example xml is provided below:
<server>
<mbean code="org.jboss.cache.jmx.CacheJmxWrapper"
name="jboss.cache:service=TreeCache">
<depends>jboss:service=Naming</depends>
<depends>jboss:service=TransactionManager</depends>
.
.
</server>
As you can see a jmx wrapper is being used to register the cache with the application server. In this case it is identified with the name “jboss.cache:service=TreeCache”. The configuration file is also communicating to the application server about the dependencies of this service on the other services running within application server context. For example JBoss cache will be using the transaction manager which is running within the application server and which is accessible through the naming service.
After the Jboss Cache service is registered with the application server next step is to make hibernate aware of the running Jboss cache service which can be used as its second level cache.
Hibernate Session factory needs to be provided with certain options to enable the second level caching using already started Jboss cache within Application server context.
Following parameters needs to be provided to Session factory:
hibernate.cache.use_second_level_cache | true | This option enables hibernate session factory to use second level caching mechanism |
hibernate.cache.region.factory_class | org.hibernate.cache.jbc2.MultiplexedJBossCacheRegionFactory | This option initializes the Cache region factory to be used to handle caching in hibernate |
hibernate.treecache.mbean.object_name | jboss.cache:service=TreeCache | This option is to connect to the TreeCache instance configured through mbean in application server. |
hibernate.cache.use_query_cache | true | This option enables caching of query results |
hibernate.cache.region.jbc2.query.localonly | true | This option stops the replication of query results across cache clusters i.e. query results are cached locally |
Another and more object oriented version of Tree Cache has been release named as POJOCache. This cache sits atop Jboss Tree cache and is mainly responsible for the caching of plain old java objects. In this version POJO’s are inserted into the cache which in turn:
1. Keeps track of the modifications on POJO’s
2. Replicate the POJO’s across cache clusters
3. Participate in transactions to persist data into DB.
4. AOP can be used for configuration of POJO’s and cache.
1.4. References
JBoss TreeCache Reference manual
JBoss Hibernate Configuration Guide.
Sample TreeCache XML File configuration
<?xml version="1.0" encoding="UTF-8"?>
<server>
<mbean code="org.jboss.cache.jmx.CacheJmxWrapper" name="jboss.cache:service=TreeCache">
<depends>jboss:service=Naming</depends>
<depends>jboss:service=TransactionManager</depends>
<!--
Configure the TransactionManager
-->
<attribute name="TransactionManagerLookupClass">org.jboss.cache.transaction.GenericTransactionManagerLookup
</attribute>
<!--
Isolation level : SERIALIZABLE
REPEATABLE_READ (default)
READ_COMMITTED
READ_UNCOMMITTED
NONE
-->
<attribute name="IsolationLevel">REPEATABLE_READ</attribute>
<!--
Valid modes are LOCAL
REPL_ASYNC
REPL_SYNC
INVALIDATION_ASYNC
INVALIDATION_SYNC
-->
<attribute name="CacheMode">REPL_SYNC</attribute>
<!--
Just used for async repl: use a replication queue
-->
<attribute name="UseReplQueue">false</attribute>
<!--
Replication interval for replication queue (in ms)
-->
<attribute name="ReplQueueInterval">0</attribute>
<!--
Max number of elements which trigger replication
-->
<attribute name="ReplQueueMaxElements">0</attribute>
<!-- Name of cluster. Needs to be the same for all TreeCache nodes in a
cluster in order to find each other.
-->
<attribute name="ClusterName">test-JBossCache-Cluster</attribute>
<attribute name="ClusterConfig">
<config>
<UDP mcast_addr="228.10.10.10"
mcast_port="45588"
tos="8"
ucast_recv_buf_size="20000000"
ucast_send_buf_size="640000"
mcast_recv_buf_size="25000000"
mcast_send_buf_size="640000"
loopback="false"
discard_incompatible_packets="true"
max_bundle_size="64000"
max_bundle_timeout="30"
use_incoming_packet_handler="true"
ip_ttl="2"
enable_bundling="false"
enable_diagnostics="true"
use_concurrent_stack="true"
thread_naming_pattern="pl"
thread_pool.enabled="true"
thread_pool.min_threads="1"
thread_pool.max_threads="25"
thread_pool.keep_alive_time="30000"
thread_pool.queue_enabled="true"
thread_pool.queue_max_size="10"
thread_pool.rejection_policy="Run"
oob_thread_pool.enabled="true"
oob_thread_pool.min_threads="1"
oob_thread_pool.max_threads="4"
oob_thread_pool.keep_alive_time="10000"
oob_thread_pool.queue_enabled="true"
oob_thread_pool.queue_max_size="10"
oob_thread_pool.rejection_policy="Run"/>
<PING timeout="2000" num_initial_members="3"/>
<MERGE2 max_interval="30000" min_interval="10000"/>
<FD_SOCK/>
<FD timeout="10000" max_tries="5" shun="true"/>
<VERIFY_SUSPECT timeout="1500"/>
<pbcast.NAKACK use_mcast_xmit="false" gc_lag="0"
retransmit_timeout="300,600,1200,2400,4800"
discard_delivered_msgs="true"/>
<UNICAST timeout="300,600,1200,2400,3600"/>
<pbcast.STABLE stability_delay="1000" desired_avg_gossip="50000"
max_bytes="400000"/>
<pbcast.GMS print_local_addr="true" join_timeout="5000"
join_retry_timeout="2000" shun="false"
view_bundling="true" view_ack_collection_timeout="5000"/>
<FRAG2 frag_size="60000"/>
<pbcast.STREAMING_STATE_TRANSFER use_reading_thread="true"/>
<!-- <pbcast.STATE_TRANSFER/> -->
<pbcast.FLUSH timeout="0"/>
</config>
</attribute>
<!--
Whether or not to fetch state on joining a cluster
NOTE this used to be called FetchStateOnStartup and has been renamed to be more descriptive.
-->
<attribute name="FetchInMemoryState">true</attribute>
<!--
The max amount of time (in milliseconds) we wait until the
state (ie. the contents of the cache) are retrieved from
existing members in a clustered environment
-->
<attribute name="StateRetrievalTimeout">15000</attribute>
<!--
Number of milliseconds to wait until all responses for a
synchronous call have been received.
-->
<attribute name="SyncReplTimeout">15000</attribute>
<!-- Max number of milliseconds to wait for a lock acquisition -->
<attribute name="LockAcquisitionTimeout">10000</attribute>
<!--
Indicate whether to use region based marshalling or not. Set this to true if you are running under a scoped
class loader, e.g., inside an application server. Default is "false".
-->
<attribute name="UseRegionBasedMarshalling">true</attribute>
</mbean>
</server>