Есть ли хороший надежный способ определить, откуда запрос Hibernate получает свои данные (например, кэш sesison или кэш второго уровня или из базы данных)? Если я настраиваю Hibernate со следующими свойствами:
<prop key="hibernate.show_sql">true</prop>
<prop key="hibernate.format_sql">true</prop>
Я получаю тонну информации о выбранных выборках (хотя я не знаю, откуда они извлекаются). Поскольку это приложение Spring MVC, и все наши запросы обрабатываются с помощью контроллеров Spring, я создал следующий перехватчик:
public class ProfilingInterceptor implements HandlerInterceptor {
@Autowired private SessionFactory sessionFactory;
private static final String STATS = "hibernateStats";
private static final String START_TIME = "startTime";
private static final Logger LOGGER = Logger.getLogger(ProfilingInterceptor.class);
@Override public boolean preHandle(HttpServletRequest request,
HttpServletResponse response,
Object handler) throws Exception {
request.getSession().setAttribute(START_TIME, System.currentTimeMillis());
request.getSession().setAttributes(STATS, new HibernateStatistics(sessionFactory.getStatistics());
return true;
}
@Override public boolean postHandle(HttpServletRequest request,
HttpServletResponse response,
Object handler) throws Exception {
HibernateStatistics stats = (HibernateStatistics)
request.getSession().getAttribute("STATS");
stats.update(sessionFactory.getStatistics());
request.getSession().setAttribute(STATS, new HibernateStatistics(sessionFactory.getStatistics()));
LOGGER.debug(stats);
}
@Override public boolean postHandle(HttpServletRequest request,
HttpServletResponse response,
Object handler,
Exception ex) throws Exception {
long startTime = (Long) request.getSession().getAttribute(START_TIME);
long currentTime = System.currentTimeMillis();
request.getSession().setAttribute(START_TIME, null);
long totalTime = currentTime - startTime;
LOGGER.debug("URI: " request.getRequestURI() + " Method: "
+ request.getMethod() + " took " + totalTime + "ms.");
HibernateStatistics stats = (HibernateStatistics)
request.getSession().getAttribute(STATS);
stats.update(sessionFactory.getStatistics());
LOGGER.debug(stats);
}
}
Вот как выглядит объект HibernateStatistics (это внутренний класс):
private static final class HibernateStatistics implements Serializable {
private static final long serialVersionUID = 1L;
private long queryExecutions = 0;
private long transactions = 0;
private long entityLoads = 0;
private long connects = 0;
private long time = 0;
private double secondLevelHits = 0;
private double secondLevelMisses = 0;
private double queryHits = 0;
private double queryMisses = 0;
public HibernateStatistics(Statistics stats) {
synchronized(stats) {
queryExecutions = -stats.getQueryExecutionCount();
transactions = -stats.getTransactionCount();
entityLoads = -stats.getEntityLoadCount();
connects = -stats.getConnectCount();
secondLevelHits = -stats.getSecondLevelCacheHitCount();
secondLevelMisses = -stats.getSecondLevelCacheMissCount();
queryHits = -stats.getQueryCacheHitCount();
queryMisses = -stats.getQueryCacheMissCount();
time = -System.currentTimeMillis();
}
}
public void update(Statistics stats) {
synchronized(stats) {
queryExecutions += stats.getQueryExecutionCount();
transactions += stats.getTransactionCount();
entityLoads += stats.getEntityLoadCount();
connects += stats.getConnectCount();
secondLevelHits += stats.getSecondLevelCacheHitCount();
secondLevelMisses += stats.getSecondLevelCacheMissCount();
queryHits += stats.getQueryCacheHitCount();
queryMisses += stats.getQueryCacheMissCount();
time += System.currentTimeMillis();
}
}
@Override
public String toString() {
return "Stats"
+ "[ queries=" + queryExecutions
+ ", xactions=" + transactions
+ ", loads=" + entityLoads
+ ", connects=" + connects
+ ", queryCacheHits=" + queryHits
+ ", secondLevelCacheHits=" + secondLevelHits
+ ", time=" + time + " ]";
}
}
Я использовал однопоточные тесты, чтобы найти некоторые ситуации, когда у нас может быть проблема с запросом N + 1 в коде, но на этом этапе наше количество соединений и транзакций выглядит хорошо на всех наших страницах.
Теперь мне нужен хороший способ определить, насколько эффективен наш второй уровень и кеширование запросов. Проблема заключается в том, что HibernateStatistics не является потокобезопасным. Поэтому, когда мы пытаемся выполнить многопоточность, я вижу странные числа для этих значений. Это так же просто, как собрать статистику и отобразить ее на странице для дальнейшего анализа?