viernes 24 de agosto de 2007

Hibernate statistics in the Enterprise 5 world

One of the useful features of Hibernate, although a little bit hidden, is the ability to collect statistics. It applies mainly to development (staging) environments and offers a somehow complete view of the Hibernate work and performance. When looking at Hibernate statistics I'm usually interested in second level cache performance (entities and queries). Let's see how to configure everything using the new JEE5 standards:

The first step is to add cache capabilities to the JPA entities. This is quite easy using the Hibernate annotations extension

import org.hibernate.annotations.Cache;
import org.hibernate.annotations.CacheConcurrencyStrategy;

@Entity
@Table(name = "USERS")
@Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)
public class Users {
   ...
}

Nothing more is needed. From now on if a second level cache manager is executing user objects will be retrieved from the cache if possible. Unfortunately, caching queries is not that easy (or should I say elegant) and you have to fallback to query hints. Of course, Hibernate has to now it should parse and use the cache annotation. Just a couple of lines in the persistence.xml files are in order:

<persistence version="1.0"...>
   <persistence-unit name="myUnit" transaction-type="JTA">
      <provider>org.hibernate.ejb.HibernatePersistence</provider>
      <jta-data-source>myDS</jta-data-source>
      <properties>
         ...
         <property name="hibernate.cache.use_second_level_cache" value="true" />
         <property name="hibernate.cache.provider_class"
                                  value="org.hibernate.cache.EhCacheProvider" />
      </properties>
   </persistence-unit>
</persistence>

I have used EHCache for no particular reason here (other than being the standard in Hibernate). Choose the one that fits you most. The rest of the file is the usual configuration for a container managed usage. We are in a position where collecting statistics is interesting. To do it, we need to activate the Statistics Service. A couple of properties in the persistence.xml are added, the first one initializes the collector and the second maps the session factory to the JNDI context (so it can be retrieved later):

<property name="hibernate.generate_statistics" value="true" />
<property name="hibernate.session_factory_name" value="SessionFactory" />

Hibernate exposes the results via a JMX MBean. The easiest way to work with JMX in an application server professionally is using Spring. Spring helps with JMX in several ways (it can even start a JMX server if needed), we will leverage the exporter bean, so the Hibernate service is integrated in the server context (in this case, Weblogic 10).

<jee:jndi-lookup id="server" jndi-name="java:comp/env/jmx/runtime" />

<jee:jndi-lookup id="hibernateSessionFactory" jndi-name="SessionFactory" />

<bean id="hibernateStatistics" class="org.hibernate.jmx.StatisticsService">
   <property name="statisticsEnabled" value="true" />
   <property name="sessionFactory" ref="hibernateSessionFactory"/>
</bean>

<bean class="org.springframework.jmx.export.MBeanExporter" lazy-init="false">
   <property name="server" ref="server" />
   <property name="beans">
      <map>
        <entry key="Hibernate:name=statistics" value-ref="hibernateStatistics"/>
      </map>
   </property>
</bean>

Everything is done by now. A JMX console is useful to look at the values. Sun's JDK include jconsole and it should be enough. Search for the Hibernate key and the statistics node there. You should expect something like: