Thursday, December 01, 2011

Hibernate Statistics & Criteria Queries

Recently we had to check the performance of our systems and a main part of that is beside the UI the database performance. For that purpose we came over Hibernate Statistics (of course because we access our DB over Hibernate)

As we are using Spring to configure our back end we had to define the statistics bean using JMX because it fits better in our project. It can be done using the following definition
<bean id="jmxExporter"
    class="org.springframework.jmx.export.MBeanExporter">
    <property name="beans">
        <map>
            <entry key="Hibernate:name=statistics" >
                <ref local="databaseStatisticsService" />
            </entry>
        </map>
    </property>
</bean>
<bean id="databaseStatisticsService"
    class="it.mydomain.core.service.impl.DatabaseStatisticsServiceImpl">
    <property name="statisticsEnabled" value="${hibernate.generate_statistics}" />
    <property name="sessionFactory" ref="sessionFactory"/>
</bean>
The property "${hibernate.generate_statistics}" contains just "true" and needs to be added also to the hibernate properties in the format "hibernate.generate_statistics=${hibernate.generate_statistics}"

The "databaseStatisticsService" is just a simple extension of the "org.hibernate.jmx.StatisticsService" providing some simple methods to retrieve the statistics in a convenient way. It's interface its like the following

import org.hibernate.jmx.StatisticsServiceMBean;
import org.hibernate.stat.CollectionStatistics;
import org.hibernate.stat.EntityStatistics;
import org.hibernate.stat.QueryStatistics;
import org.hibernate.stat.SecondLevelCacheStatistics;
import org.hibernate.stat.Statistics;
public interface DatabaseStatisticsService extends StatisticsServiceMBean {
boolean isDbStatisticsEnabled();
Statistics getDbStatistics();
List getDbStatisticSummary();
List getDbStatisticCollections();
List getDbStatisticEntities();
List getDbStatisticQueries();
List getDbStatisticSecondLevelCache();
}

In this interface you can already see, that there is a method "List getDbStatisticQueries();" which should contain the data for each performed query; at least we thought that it should be like that :-)

However by default it contains just the data for the HQL queries but not the ones for the Criteria ones. After a short Google excursion we found that there is an issue that is still open in the hibernate bug tracker. Usually in my experience efficient database queries are a main factor of the applications performance, so we had to find a way to add this data, which turned out to be much simpler as expected.

The main idea is just to add a simple method to the "org.hibernate.loader.criteria.CriteriaLoader" class that allows the Statistic collector to index also that data. The simplest way to do so is just to add the method using an aspect like the following
package it.unibz.ict.core.util.aspect;

import org.hibernate.loader.criteria.CriteriaLoader;
import org.hibernate.stat.Statistics;

/**
 * Aspect adding a method to {@link CriteriaLoader} to allow Hibernate
 * {@link Statistics} pick up criteria queries, too.
 */
public privileged aspect CriteriaStatisticsAspect {
public static final String PREFIX = "[CRITERIA] ";
/**
* @see http://opensource.atlassian.com/projects/hibernate/browse/HHH-3452
* @return
*/
public String CriteriaLoader.getQueryIdentifier() {
return PREFIX + getSQLString();
}
/**
* Dummy method; without this Eclipse would see an error in the getSQLString
* call above
*
* @return dummy value
*/
private String getSQLString() {
return null;
}
}
Problem solved! At least until this method is added by default by the hibernate developer team.


No comments: