Home » Development » Currently Reading:

Seam and EhCache – A Short Primer

April 15, 2010 Development 5 Comments

There has been excellent coverage on how to attain the most performance out of your Seam-based Web applications. One aspect of improving Seam performance that, in my opinion, has received less attention, is the framework’s excellent built-in support for caching of entity java beans and query results.

In this article, I will briefly describe the steps I took in order to configure some basic to intermediate-level caching throughout our system. Although I did not go as far as to gather metrics on the performance gain, I will say that our entire team noticed a 30-50% improvement across the board – and all from some fairly easy work.

Because of our non-JBoss native application stack – we’re using WebLogic 11g – I chose to select EhCache as my cache provider of choice.

Package EhCache

For this primer we will be using EhCache 1.5. Download ehcache-1.5.0.jar (link) and place it in your /lib directory. Using your chosen build script (Ant, Maven, etc.), ensure that ehcache-1.5.0.jar is built to your EAR so that the classpath loader will find the library at startup.

Configure the Provider

Next, we need to make sure that EhCache (or your chosen cache provider) is properly configured. Modify components.xml and tell Seam to load the EhCache provider.

components.xml

<cache:eh-cache-provider name="cacheProvider"/>

The last step is to configure your persistence provider. Normally with Seam this configuration is broken up into several profiles (local, dev, test, and prod). In this example, I will be showing how I configured my persistence provider for our “local” profile.

Note that there are several options for hibernate.cache.provider_class; I have chosen to use the Singleton provider in this example.

persistence-local.xml

<!-- ... other properties .... ->
<property name="hibernate.cache.use_query_cache" value="true"/>
        <property name="hibernate.use_second_level_cache" value="true"/>
        <property name="hibernate.default_batch_fetch_size" value="16"/>
        <property name="hibernate.generate_statistics" value="true"/>
<property name="hibernate.cache.provider_class" value="net.sf.ehcache.hibernate.SingletonEhCacheProvider"/>
<property name="net.sf.ehcache.configurationResourceName" value="/ehcache2.xml"/>

Note in the example above that we have set hibernate.generate_statistics equal to true; this will initially help you tune your caching provider by enabling you to compare the query statistics (once properly setup via your log4j configuration).

Annotate Your Entities

Be aware that Seam comes with built-in caching; you can always annotate your entity java beans with the @Cache annotation. However, in this primer, we are building a path towards a more robust and advanced cache configuration. Even though the annotations remain the same, how they will be interpreted and used by EhCache will be up to us.

Annotating your entities are a simple and effective means to provide high-level caching of your application’s data. The three main cache strategies that we will concern ourselves with are:

    READ_WRITE – should be used for objects that will be read and written to; these objects will change
    READ_ONLY – should be used in cases where the object does not change; this tells Hibernate and EhCache to strongly cache this object
    NONSTRICT_READ_WRITE – should be used in cases where an object is rarely written to; it will be cached to a degree less than READ but more than READ_WRITE
    TRANSACTIONAL – this strategy is not available with EhCache but can be used with other cache providers

In my first entity, I have an object (User.java) that is both read and written; I want it to be cached, but it will need to be designated as READ_WRITE. User objects are heavily queried throughout our system but often modified. Later, I will show how you can provide more control over the caching of a specific object or region.

@Entity @Audited
@Table(name="USERS")
@Cache(usage=CacheConcurrencyStrategy.READ_WRITE
@Name("user")
@Scope(ScopeType.EVENT)
@BypassInterceptors
public class User implements Serializable
{
    /** the users internal id **/
	@Id
	@GeneratedValue(strategy = GenerationType.AUTO)
	private Long id;

    /** the userid/logon **/
	@UserPrincipal
	@Length(min=5, max=32)
	private String userId;

 // Rest of class below...

In my second entity I have an object called AdminValue.java; this object is used as metadata throughout the system. This object populates dropdowns and for the most part will not change.

@Entity @Audited
@Table(name = "APP_ADMIN_VALUES")
@Cache(usage=CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)
@Name("adminValue")
@Scope(ScopeType.EVENT)
public class AdminValue implements Serializable
{

    /** the id **/
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    @Column(name="ID")
    private Long id;

    /**
     * name of the value
     */
    @NotNull
    @Length(max=50)
    @Index(name="IDX_ADMIN_VALUE_NAME")
    private String name;

    // Rest of class below...

Next, we have an object (MenuItem.java) that truly does not change. It is populated when the application deploys and will rarely, if ever, change. Because it is used throughout interface, though, I want it to be cached heavily to speed up page rendering.

@Entity
@Cache(usage=CacheConcurrencyStrategy.READ_ONLY, region = "com.bah.englink.MenuItem")
@Name("menuItem")
@Scope(ScopeType.EVENT)
@Table(name="APP_MENU_ITEMS")
public class MenuItem implements Serializable
{
	@Id
	@GeneratedValue(strategy = GenerationType.AUTO)
	private int id; //Uniquie id for the menu item

	@ManyToOne(optional=true)
	@Index(name="IDX_MENU_ITEM_FK01")
	private MenuItem parent; //Defines the parent associated with item, can be null for the top level

	@NotNull
	@Length(max=64)
	@Index(name="IDX_MENU_ITEM_NAME")
	private String name; //Name that displays on the screen

        // Rest of class below...

Using Query Hints with Named Queries

Named queries are particularly useful; they are strongly typed, and are therefore checked at compile-time, and cached via Seam/Hibernate JPA. You can provide further caching of named queries like so:

@NamedQueries({
@NamedQuery(
    name="lookupCategoryByName",
    query="from AdminCategory ac where ac.name=:name order by ac.name asc",
    hints={@QueryHint(name="org.hibernate.cacheable", value="true")}),
@NamedQuery(
    name="lookupCategoryByNameByModule",
    query="from AdminCategory ac where ac.name=:categoryName and ac.adminModule.name=:moduleName",
    hints={@QueryHint(name="org.hibernate.cacheable", value="true")})
})
public class AdminCategory implements Serializable
{

Advanced Configuration Via Regions

Here is where the fun begins; using the ehcache2.xml that we specified in our persistence provider, we can define minute details regarding how a particular object (or region of objects) are cached. This feature provides a high-level of control. There are many aspects of these configuration options that are a bit above my head, so I’ve outlined some examples that I used to show what is possible.

Whenever specifying a region with your @Cache annotation, you should try to have a corresponding region defined in your ehcache2.xml. Here are some examples.

//@Cache(usage=CacheConcurrencyStrategy.READ_WRITE, region = "com.bah.englink.ejb.User")

<cache name="com.bah.englink.ejb.User"
           maxElementsInMemory="10000"
           maxElementsOnDisk="20000"
           eternal="false"
           overflowToDisk="true"
           diskSpoolBufferSizeMB="100"
           timeToIdleSeconds="4000"
           timeToLiveSeconds="8000"
           memoryStoreEvictionPolicy="LRU"
            />
@Cache(usage=CacheConcurrencyStrategy.READ_ONLY, region = "com.bah.englink.MenuItem")

<cache name="com.bah.englink.MenuItem"
           maxElementsInMemory="10000"
           maxElementsOnDisk="5000"
           eternal="false"
           overflowToDisk="true"
           diskSpoolBufferSizeMB="40"
           timeToIdleSeconds="9000"
           timeToLiveSeconds="18000"
           memoryStoreEvictionPolicy="LFU"
            />

Note, the bundled ehcache2.xml that comes with ehcache-1.5.0.jar has many more examples for you to review and understand.

Logging

If you would like to see exactly which objects EhCache is storing, and when/if cache objects are being hit, you can turn on debug output from EhCache via your log4j.xml settings.

 <category name="org.hibernate.cache">
	<priority value="DEBUG"/>
   </category>
[/sourecode]

After doing so, you should see cache output, such as in the example below.
[sourcecode language='text']
15:42:44,107 DEBUG [ReadOnlyCache] Caching: com.bah.englink.MenuItem#-90
15:42:44,107 DEBUG [ReadOnlyCache] Caching: com.bah.englink.MenuItem#-66
15:42:44,107 DEBUG [ReadOnlyCache] Caching: com.bah.englink.MenuItem#-65
15:42:44,107 DEBUG [ReadOnlyCache] Caching: com.bah.englink.MenuItem#-51
15:42:44,107 DEBUG [ReadOnlyCache] Caching: com.bah.englink.MenuItem#-89
15:42:44,107 DEBUG [ReadOnlyCache] Caching: com.bah.englink.MenuItem#-82
15:42:44,107 DEBUG [ReadOnlyCache] Caching: com.bah.englink.MenuItem#-88
15:42:44,107 DEBUG [ReadOnlyCache] Caching: com.bah.englink.MenuItem#-1004
15:42:44,107 DEBUG [ReadOnlyCache] Caching: com.bah.englink.MenuItem#-1005
15:42:44,107 DEBUG [ReadOnlyCache] Caching: com.bah.englink.MenuItem#-14
15:42:44,107 DEBUG [ReadOnlyCache] Caching: com.bah.englink.MenuItem#-83
15:42:44,123 DEBUG [ReadOnlyCache] Caching: com.bah.englink.MenuItem#-26
15:42:44,123 DEBUG [ReadOnlyCache] Caching: com.bah.englink.MenuItem#-64

Conclusion

I hope this has been a useful, if intermediate, review of caching in your Seam-based application using Hibernate and EhCache. Please feel free to let me know what I have missed or if you find any mistakes.

More Resources

Hibernate: Truly Understanding Second-Level Cache

Second-Level Cache with EhCache

Share:
  • Digg
  • del.icio.us
  • Facebook
  • Google Bookmarks
  • DZone
  • Reddit
  • StumbleUpon
  • Slashdot
  • Tumblr
  • Yahoo! Buzz

Currently there are "5 comments" on this Article:

  1. steve says:

    Great blog though I’m somewhat curious why you used such an old version of ehcache?

  2. admin says:

    Thanks for your post and you make a very good point; I need to look into upgrading. I suppose I have been using the version that ships with Seam, and I believe they are currently distributing with version 1.5 of EhCache. Regardless, I do need to upgrade. I’ll make reference of this in the post. Thanks.

  3. steve says:

    Noticed the point about transactional not working with ehcache. Good news, we are going into beta on transactions for ehcache next week. Maybe we can work together to make sure it works well with seam.

  4. admin says:

    @Steve: Absolutely; I’d be prepared to give the beta a whirl and provide some feedback. Thanks for your posts.

  5. [...] This post was mentioned on Twitter by Richard Laksana, geeklisted. geeklisted said: RT @DZone "Seam and EhCache – A Short Primer" http://dzone.com/FjQC [...]

Comment on this Article:







Featured Geek Stuff

Geek Fitness

July 31, 2010

Geek Fitness

Over the course of 5 and a half years of playing WoW, I managed to pack on a few pounds…and by few I mean somewhere in the range of 80-90 pounds. It was a gradual gain over the years, compacted by my lifestyle decisions, namely:
1. Job as a sys admin where I sat majority of [...]

Seam Recipes [Part 1]: s:decorate

September 21, 2009

Seam Recipes [Part 1]: s:decorate

As a developer, forms are one of my biggest grievances whenever I am building a Web application. Why? Well, for one, there is nothing pretty about creating a form. Even the best Web designers, with all of their fancy CSS wizardy, struggle to build clean, consistent, and reusable form layers that can be used anywhere within an application. To complicate matters, nothing in the HTML 4 spec (though, this will change with HTML 5) provide any sort of functionality around form elements.

A Trip Down LucasArts Memory Lane; or, the Game That Started It All

September 11, 2009

A Trip Down LucasArts Memory Lane; or, the Game That Started It All

Today I consider myself a computer techie; I’m a developer by trade, a “technologist” in my free time, and a gamer at heart.
All of that had to start somewhere, right?  Well, for me, it did when, at the age of 13, my father came home talking about a PC game he saw at our [...]

First Impressions: PS3 Slim

September 10, 2009

First Impressions: PS3 Slim

Sony recently announced the long rumored Playstation 3 Slim at the GamesCom conference in Germany, as well as a (much needed) price drop to $299. The new PS3 Slim has gotten rid of the shiny, fingerprint laden exterior and replaced it with a matte black casing, removed the Spiderman 3 font, changed the power button, [...]

WoW: The B Squad

September 10, 2009

WoW: The B Squad

I recently came across an article on Elder Game regarding World of Warcraft and how many of Blizzard’s employees had shifted onto new projects (Starcraft 2, Diablo 3, Unannounced MMO), while a newer and less experienced team was handling the current live content for WoW (dubbed the B Squad). Being a longtime (and current) player [...]

RSS Twitter: geeklisted

Look Who\'s Talking

  • vin: i agree they are not responding to me either. first time i h...
  • admin: @Steve: Absolutely; I'd be prepared to give the beta a whirl...
  • steve: Noticed the point about transactional not working with ehcac...
  • admin: Thanks for your post and you make a very good point; I need ...
  • steve: Great blog though I'm somewhat curious why you used such an ...