Hibernate Cache Monitor einrichten mit AspectJ und Spring

Um in Hibernate die Geschwindigkeit zu erhöhen, kann man unter anderem einen Second Level Cache einrichten. Dieser kann die Anzahl der Datenbank-Abfragen verringern, indem er die Entities zwischen den Hibernate Sessions zwischenspeichert.

Manchmal ist es aber nicht ganz offensichtlich, ob der Cache funktioniert bzw. wieviel Cache-Hits man hat. Deshalb wollen wir uns ansehen, wie wir die Statistiken mitloggen können und einen Cache Monitor hierfür einrichten:

Dazu verwenden wir die AspectJ Library. Diese ermöglicht uns, um jede Methode einer Data Access Object Klasse (DAO) eine eigene Funktion auszuführen und so alle potentiellen Cache Hits mitzuloggen. Hierzu müssen wir die Library in unsere pom.xml aufnehmen, zusammen mit Spring AOP:

<dependency>
	<groupId>org.aspectj</groupId>
	<artifactId>aspectjweaver</artifactId>
	<version>${aspectj.version}</version>
</dependency>

<dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-aop</artifactId>
        <version>${spring.framework.version}</version>
</dependency>

Danach erstellen wir eine Klasse CacheMonitor in unserem Projekt. Hier wird die Statistik aus der Hibernate Session Factory ausgelesen, welche alle für uns relevanten Daten mitführt.
Eine Übersicht über alle Statistiken könnt Ihr in der Doku nachlesen: https://docs.jboss.org/hibernate/orm/3.5/api/org/hibernate/stat/Statistics.html

package com.mycompany.myproject.aspect;

import org.apache.log4j.Logger;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.eclipse.jetty.util.log.Log;
import org.hibernate.SessionFactory;
import org.hibernate.stat.Statistics;
import org.springframework.beans.factory.annotation.Autowired;

@Aspect
public class CacheMonitor {
	private final static Logger LOG = Logger.getLogger(CacheMonitor.class);

	private SessionFactory sessionFactory;
	
	public SessionFactory getSessionFactory() {
		return sessionFactory;
	}

	@Autowired
	public void setSessionFactory(SessionFactory sessionFactory) {
		this.sessionFactory = sessionFactory;
	}

        // hier muss das Package der eigenen DAO Klassen angegeben werden
	@Around("execution(* com.mycompany.myproject.dao.*.*(..))")
	public Object log(ProceedingJoinPoint pjp) throws Throwable {
	
		if (!LOG.isDebugEnabled()) {
			return pjp.proceed();
		}
		
		if (this.sessionFactory == null) {
			Log.warn("sessionFactory is null");
			return pjp.proceed();
		}

		Statistics statistics = sessionFactory.getStatistics();
		statistics.setStatisticsEnabled(true);

		Long hit = statistics.getSecondLevelCacheHitCount();
		Long miss = statistics.getSecondLevelCacheMissCount();
		
		Log.debug(String.format("hit: %s / miss: %s / Signatur=%s#%s()", hit.toString()
                                , miss.toString(), pjp.getTarget().getClass().getName(), pjp.getSignature().toShortString());

		Object result = pjp.proceed();
		return result;
	}
}

Zum Schluss muß noch eine Zeile in der Spring-Konfiguration hinzugefügt werden:

	<aop:aspectj-autoproxy />
	<bean class="com.mycompany.myproject.aspect.CacheMonitor" factory-method="aspectOf" />

Wenn alles funktioniert, tauchen im Log entsprechende Meldungen auf und man kann kontrollieren, wieweit der Second Level Cache funktioniert und der Applikation Performance bringt.