Thursday, 17 January 2013

Configuring CacheConcurrencyStrategy in JPA2

Edited to add - to clarify I'm using EhCache as my Cache Provider

This will be a slightly more technical post than usual, I've struggled through a few problems whilst writing a chapter on "Second Level Caching" for our new Hibernate and JPA course, and I wanted to capture the results of the struggles.

This will be a long post, so sorry for the waffle, but the executive summary is:

JPA2 has a @Cacheable annotation, but it allows no parameters to tune the CacheConcurrencyStrategy. It isn't clear, but the default setting will be READ_WRITE.

This blog post assumes you already understand the basics of the Second Level Cache (2LC), so I'll dive straight into the depths:

(Of course, if you don't know about how 2LC works - then you'll need our course! Get it from our website, the release date will be early February).

What is the CacheConcurrencyStrategy?

When configuring a 2LC using Hibernate, it is necessary to set the Concurrency Strategy for each cache region. This was either done in the XML mapping file, or using their @Cache annotation:

@org.hibernate.annotations.Cache(usage=CacheConcurrencyStrategy.READ_ONLY)
public class Tutor
{

   // etc
}
The Concurrency Strategy is quite an important decision, as it is a fine tune of the cache region which could affect performance. You can choose from:

  • READ_ONLY. This sets the cache region as a read only cache, and this means it can be very performant as the cache doesn't have to worry about locking. If you try to modify any of these objects though, you will get an exception.

  • NONSTRICT_READ_WRITE. This allows you to modify cacheable objects, but the cache provider doesn't need to implement a strict lock on the cache. This means there is a potential for stale objects (ie one transaction has modified an object, but another object picks up the old version of the object from the cache). Choose this for writeable objects, but only if you don't care about stale data.

  • READ_WRITE. The "safest" but least performant option. The cache provider will lock the cache when the object is updated, ensuring that all transactions will see the most up to date version.
(There is also "Transactional" which only applies to distributed caches. I won't even think about that right now).

Ok. So far so good, and we've always had to do this when working with Hibernate 2LCs.

However, for this new video course I want to use JPA2 annotations as much as possible, in order to keep with the standards. The odd thing is that replacement for org.hibernate.annotations.Cache is javax.persistence.Cacheable and it allows no parameters.
@Cacheable    // eeek - we can't specify anything here!
public class Tutor
{

   // etc
}

So, what will the concurrency strategy be if we can't specify it?

Well, it took me a good few hours to work through it, but I eventually found a few Hibernate JIRA issues (see here and here).

The key is that Hibernate "asks" EhCache what it's default strategy should be, and it does this via call to the  EHCacheRegionFactory.getDefaultAccessType() method. 

And at last we can get the answer - checking the JavaDoc for this method here, we can see the following:

Default access-type used when the configured using JPA 2.0 config. JPA 2.0 allows @Cacheable(true) to be attached to an entity without any access type or usage qualification.
We are conservative here in specifying AccessType.READ_WRITE so as to follow the mantra of "do no harm".

So, if you just use @Cacheable you will get a READ_WRITE Cache Region. Safe, but possibly not as performant as you would like.

If you don't like this, then until JPA supports this property (if it ever does - it is an implementation specific feature), then you will need to add the old org.hibernate annotation:
@Cacheable
@org.hibernate.annotations.Cache(usage=CacheConcurrencyStrategy.READ_ONLY)
public class Tutor
{

I guess you may as well remove the javax.persistence.Cachable annotation, it is really redundant.

I can't decide if this is the fault of JPA2 or EhCache/Hibernate. I'm leaning towards the latter - why should we need to configure the Concurrency Strategy through the code when there's a perfectly good place to do it, in the ehcache.xml file. This configures the details of each cache region, why isn't it done in there?

Do feel free to comment and set me straight on anything I've missed or misunderstood!

Thursday, 3 January 2013

Hibernate and JPA Course Progress

This course has now been released (on Feb 14 2013) - full details here
I'm very sorry for the long delay in blog posts - but I've been very busy recording the Hibernate course. I'm glad to report that despite taking a long time (I started in July!), the course is looking great, and getting close to completion (current estimate - end of January).

I've found the big problem with Hibernate is that whilst it's easy enough to get up and running (you can persist your first object in no time), Hibernate is deep. But it's deep in a different way to - say for example - Spring.

With Spring, you can learn the fundamentals and start working on a project. The depth in Spring comes from the fact that it's huge and has many different features. But you don't need to know all these features right away - if you have the fundamentals (mainly Dependency Injection), you can learn the pieces you need as you go along.

But with Hibernate, a little knowledge can be dangerous. Sure, you can persist a few objects, but you will soon hit a brick wall. And it's very easy to blame Hibernate for being too complex/too buggy/too random - when it's really a lack of knowledge causing your frustration.

I've been determined that in this course, I will cover everything you need to know to be fully competent in Hibernate, and so I'm not shying away from drilling really quite deep. 

A second problem is that it is easy enough to work with Hibernate when you're opening a session in a simple method, doing a bit of persistence and then closing the session. But real architectures don't do this, so I've recorded a chapter showing how Hibernate works in Web Applications, in JavaEE applications and in Spring Applications.

Yet another difficulty is that we really need to cover BOTH JPA and Hibernate, and that's hard to do without confusing (the major textbook on Hibernate tries to cover both at the same time, and it's a bit of a mess). So my compromise is that we start with classic Hibernate, and fully understand the Session API, and then halfway through the course we switch over to JPA and work exclusively with EntityManager. I hope this approach has worked.

Anyway, I'll get back to it now and I will announce when it's finished. I've still to do some quite hard stuff - Lazy Initialisation and Caching are two big ones that I'm particularly dreading, but the end is in sight. I hope the wait will be worth it!