http://www.developer.com/java/ent/article.php/3897536/Combining-Hibernate-Cache-and-Ehcache-for-Better-Java-Scalability.htm
With advanced load balancers and decreasing hardware costs, ensuring the scalability of enterprise applications across multiple boxes is becoming more economically feasible. Ideally, an application's TPS (transactions per second) will rise in a linear fashion as the number of servers it runs on increases. However, sooner or later you will encounter performance issues on one box that you can't easily (or at least cheaply) alleviate by scaling across multiple servers, such as your relational database for example. Hence, it is critical to lower your database load. In particular, you can employ efficient caching strategies that reduce the number of SQL queries and the amount of data that needs to be transferred over the wire. One powerful caching strategy that meets these requirements is to combine Hibernate cache (second-level) and Ehcache. In this article I explain how to apply this strategy -- along with the Spring Framework -- to improve the scalability of your enterprise Java applications. I provide source code for a demo application as well. A typical Web request is wrapped around a filter (e.g. a Spring Hibernate session filter) that automatically opens and closes a Hibernate session during its duration. The Spring OpenSessionInViewFilter is an example of such as filter. Within each session, Hibernate maintains a first-level cache to ensure against multiple copies of the same entity within that session. This cache is discarded as soon as the session closes, and it is not persistent. However, Hibernate also offers a second-level cache, which is persistent. It is designed with the capability to cache serialized copies of entities over longer periods of time. Hibernate's cache mechanism is pluggable and various caching libraries offer integration modules for it. Among these, the most popular and robust is by far Ehcache -- an extremely powerful caching product. Using a local in-memory/disk cache can not only lower the load on the database, but it can also dramatically increase read speeds. In an internal test we ran at my organization, we were able to get a throughput of 20,000+ reads per second for a Hibernate entity from a local Ehcache disk cache. Retrieving it directly from the database maxed out at 5000 reads per second -- a significant difference. Ehcache can store large collections of objects in memory or automatically overflow them to disk. It also provides control over many aspects of the caching mechanism, including along others: Let's start off by defining the Spring Hibernate session filter I mentioned previously (OpenSessionInViewFilter) via web.xml: Next, in the Spring beans.xml configuration we will specify a typical Hibernate session factory. The important parts are bolded in the example below: The hibernate.cache.region.factory_class property configures Ehcache as the second-level cache provider. The net.sf.Ehcache.configurationResourceName property tells Ehcache the classpath location of the configuration file that fine-tunes its setting. Note that this file must be in the classpath, hence it's usually embedded in the JAR/WAR file itself. Here's our sample com.developer.ehcache.xml configuration file from the setup in the previous section: Ehcache creates a separate cache for every Hibernate entity, with a name equivalent to its full path. Hence, you can override every entity's cache settings if the need arises. However, in most cases the defaultCache settings are sufficient as a template. In this example we've chosen settings to: The list of Ehcache configuration options is incredibly extensive and goes far beyond this basic example. Just because we've configured Hibernate and Ehcache together, that does not mean that every single entity is now automatically cached. Every single Hibernate entity must be explicitly flagged via the The Hibernate documentation explains the different cache concurrency strategies, but NONSTRICT_READ_WRITE is the most common one. If you are running in a full JTA environment, you may choose TRANSACTIONAL instead. Attached to this is article is a sample application that shows how this all works in practice. It exposes a simple REST service for a basic Person entity under http://localhost:8080/services/person and requires Maven to run. The sample Log4J configuration is set for TRACE for both Hibernate and Ehcache in order to show the most information about what's going on under the hood. When you start the application via Now via the standard Linux Let's look at the server-side log to see what happened on that first request: Hibernate retrieved our entity from the database, stored it in the cache automatically and returned it to us. Now let's issue the same This time Hibernate automatically found our entity in the cache and loaded it directly from memory without hitting the database -- which was the main reason we wanted to use second-level cache in the first place. This of course sounds great and looks easy. However, all of this comes at a price, namely increased complexity (especially during testing). By relying on cached entities, you risk having an out-of-sync cache when an update happens in the database via a different request. As such, you must analyze every entity that can be cached to determine whether serving a potentially stale copy of it is acceptable. Stale copies may be fine for fairly static entities that rarely change, but they would not be appropriate for something such as a bank user's Account object, which should always reflect the latest state in the database. However, options are available for handling those situations. For example, you can configure Ehcache to do multicasting over the network in order to update the caches automatically on all servers whenever an entity gets updated. You even can wrap such a cache refresh within a JTA transaction context to ensure all the caches get updated together. But that's a topic for an upcoming article.
Combining Hibernate Cache and Ehcache for Better Java Scalability
August 9, 2010
Hibernate Second-level Cache and Ehcache
Configuring Spring, Hibernate and Ehcache for Improved Scalability
<filter> <filter-name>hibernateFilter</filter-name> <filter-class>org.springframework.orm.hibernate3.support.OpenSessionInViewFilter</filter-class> </filter> <filter-mapping> <filter-name>hibernateFilter</filter-name> <url-pattern>/services/*</url-pattern> </filter-mapping><bean id="sessionFactory" class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean"> <property name="dataSource" ref="dataSource" /> <property name="hibernateProperties"> <props> <prop key="hibernate.hbm2ddl.auto">update</prop> <prop key="hibernate.dialect">org.hibernate.dialect.H2Dialect</prop> <prop key="hibernate.connection.isolation">3</prop> <prop key="hibernate.cache.region.factory_class">net.sf.ehcache.hibernate.EhCacheRegionFactory</prop> <prop key="net.sf.ehcache.configurationResourceName">/com/developer/ehcache.xml</prop> </props> </property> <property name="annotatedClasses"> <list> <value>com.developer.article.model.Person</value> </list> </property></bean>/property></bean>Fine-tuning Ehcache Settings
<Ehcache> <diskStore path="java.io.tmpdir"/> <defaultCache maxElementsInMemory="10000" eternal="false" timeToIdleSeconds="120" timeToLiveSeconds="120" overflowToDisk="true" maxElementsOnDisk="10000000" diskPersistent="false" diskExpiryThreadIntervalSeconds="120" memoryStoreEvictionPolicy="LRU" /></Ehcache>
Configuring Hibernate/JPA Entities
@Cache annotation:@Entity @Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)public class Person {}Spring, Hibernate and Ehcache in Practice
mvn jetty:run it will show a long list of trace messages, out of which the key ones are:Cache region factory : net.sf.ehcache.hibernate.EhCacheRegionFactory...Creating EhCacheRegionFactory from a specified resource: /com/developer/ehcache.xml. ...Building cache for entity data [com.developer.article.model.Person]Couldn't find a specific Ehcache configuration for cache named [com.developer.article.model.Person]; using defaults.started Ehcache region: com.developer.article.model.Personcurl command (or any other REST client on your PC platform) we will send a GET request to our REST Web services to get a JSON representation of a single Person entity that we have in our database:curl http://localhost:8080/services/person/6{"Person":{"id":6,"firstName":"First 3","lastName":"Last 3"}}adding entity to second-level cache: [com.developer.article.model.Person#6]done materializing entity [com.developer.article.model.Person#6]curl command again and see what happens:loading entity: [com.developer.article.model.Person#6]attempting to resolve: [com.developer.article.model.Person#6]assembling entity from second-level cache: [com.developer.article.model.Person#6]Effective Strategy but Nothing Is Free
Code Download
About the Author
Jacek Furmankiewicz is a Senior Java EE, Python, Oracle and MySQL developer at Radialpoint. He has 16 years of IT experience in writing enterprise software for a wide variety of industries.