From Software Development to Rock Music....

Freitag, 3. April 2009

Hibernate Statistics Wicket Page

I spent the last couple of days in Vienna and had two talks at the ejugdays. One of the talks was about Hibernate Tuning and during that talk I usually show a demo of a web application that has a very simple wicket page displaying the Hibernate's statistics. Once again I got asked by a few people if they could have the source code of that page. I decided to post the source code of the Wicket page to my blog.
As you might have already guessed: this code example is based on Apache Wicket, my favorite web framework. I'm sure that refactoring the page to JSF, Tapestry or whatever will not be too hard to achieve. As an entrance point I inject the Hibernate SessionFactory through the @SpringBean Annotation (part of Wicket's Spring support). If you don't use Spring you will have to code the SessionFactory's lookup. That shouldn't be too hard either.

HibernateStatisticsPage.java

package de.allschools.view.admin;

import java.util.ArrayList;
import java.util.List;

import org.apache.wicket.PageParameters;
import org.apache.wicket.markup.html.WebMarkupContainer;
import org.apache.wicket.markup.html.WebPage;
import org.apache.wicket.markup.html.basic.Label;
import org.apache.wicket.markup.html.list.ListItem;
import org.apache.wicket.markup.html.list.ListView;
import org.apache.wicket.model.CompoundPropertyModel;
import org.apache.wicket.model.LoadableDetachableModel;
import org.apache.wicket.model.Model;
import org.apache.wicket.spring.injection.annot.SpringBean;
import org.hibernate.SessionFactory;
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 class HibernateStatisticsPage extends WebPage {
@SpringBean(name = "sessionFactory")
private SessionFactory sf;

public HibernateStatisticsPage(PageParameters parameters) {
super(parameters);

final WebMarkupContainer st = new WebMarkupContainer("stats");
final CompoundPropertyModel model = new CompoundPropertyModel(new LoadableDetachableModel() {
protected Object load() {
return sf.getStatistics();
}
});
st.setModel(model);
st.add(new Label("isStatisticsEnabled"));
st.add(new Label("startTime"));
st.add(new Label("sessionOpenCount"));
st.add(new Label("sessionCloseCount"));
st.add(new Label("flushCount"));
st.add(new Label("connectCount"));
st.add(new Label("prepareStatementCount"));
st.add(new Label("closeStatementCount"));
st.add(new Label("entityLoadCount"));
st.add(new Label("entityUpdateCount"));
st.add(new Label("entityInsertCount"));
st.add(new Label("entityDeleteCount"));
st.add(new Label("entityFetchCount"));
st.add(new Label("collectionLoadCount"));
st.add(new Label("collectionUpdateCount"));
st.add(new Label("collectionRemoveCount"));
st.add(new Label("collectionRecreateCount"));
st.add(new Label("collectionFetchCount"));
st.add(new Label("secondLevelCacheHitCount"));
st.add(new Label("secondLevelCacheMissCount"));
st.add(new Label("secondLevelCachePutCount"));
st.add(new Label("queryExecutionCount"));
st.add(new Label("queryExecutionMaxTime"));
st.add(new Label("queryExecutionMaxTimeQueryString"));
st.add(new Label("queryCacheHitCount"));
st.add(new Label("queryCacheMissCount"));
st.add(new Label("queryCachePutCount"));
st.add(new Label("commitedTransactionCount"));
st.add(new Label("transactionCount"));
st.add(new Label("optimisticFailureCount"));
add(st);
String[] entities = ((Statistics) model.getObject()).getEntityNames();
List entityNames = new ArrayList();
for (int i = 0; i < entities.length; i++) {
entityNames.add(entities[i]);
}
ListView entityStats = new ListView("entities", entityNames) {
protected void populateItem(ListItem item) {
String entityName = (String) item.getModelObject();
final EntityStatistics entityStat = ((Statistics) model.getObject()).getEntityStatistics(entityName);
item.add(new Label("deleteCount", new Model(entityStat.getDeleteCount())));
item.add(new Label("updateCount", new Model(entityStat.getUpdateCount())));
item.add(new Label("fetchCount", new Model(entityStat.getFetchCount())));
item.add(new Label("insertCount", new Model(entityStat.getInsertCount())));
item.add(new Label("loadCount", new Model(entityStat.getLoadCount())));
item.add(new Label("optimisticFailureCount", new Model(entityStat.getOptimisticFailureCount())));
item.add(new Label("entityName", new Model(entityName)));
}
};
add(entityStats);

String[] collections = ((Statistics) model.getObject()).getCollectionRoleNames();
List collectionNames = new ArrayList();
for (int i = 0; i < collections.length; i++) {
collectionNames.add(collections[i]);
}
ListView collectionStats = new ListView("collections", collectionNames) {
protected void populateItem(ListItem item) {
String collName = (String) item.getModelObject();
CollectionStatistics collectionStatistics = ((Statistics) model.getObject()).getCollectionStatistics(collName);
item.add(new Label("recreateCount", new Model(collectionStatistics.getRecreateCount())));
item.add(new Label("updateCount", new Model(collectionStatistics.getUpdateCount())));
item.add(new Label("fetchCount", new Model(collectionStatistics.getFetchCount())));
item.add(new Label("removeCount", new Model(collectionStatistics.getRemoveCount())));
item.add(new Label("loadCount", new Model(collectionStatistics.getLoadCount())));
item.add(new Label("collName", new Model(collName)));
}
};
add(collectionStats);

String[] queries = ((Statistics) model.getObject()).getQueries();
List queryNames = new ArrayList();
for (int i = 0; i < queries.length; i++) {
queryNames.add(queries[i]);
}
ListView queryStats = new ListView("queries", queryNames) {
protected void populateItem(ListItem item) {
String queryName = (String) item.getModelObject();
QueryStatistics queryStatistics = ((Statistics) model.getObject()).getQueryStatistics(queryName);
item.setModel(new CompoundPropertyModel(queryStatistics));
item.add(new Label("cacheHitCount"));
item.add(new Label("cacheMissCount"));
item.add(new Label("cachePutCount"));
item.add(new Label("executionCount"));
item.add(new Label("executionRowCount"));
item.add(new Label("executionAvgTime"));
item.add(new Label("executionMaxTime"));
item.add(new Label("executionMinTime"));
item.add(new Label("categoryName"));
}
};
add(queryStats);

String[] caches = ((Statistics) model.getObject()).getSecondLevelCacheRegionNames();
List cacheNames = new ArrayList();
for (int i = 0; i < caches.length; i++) {
cacheNames.add(caches[i]);
}
ListView cacheStats = new ListView("caches", cacheNames) {
protected void populateItem(ListItem item) {
String cacheName = (String) item.getModelObject();
SecondLevelCacheStatistics cacheStatistics = ((Statistics) model.getObject()).getSecondLevelCacheStatistics(cacheName);
item.setModel(new CompoundPropertyModel(cacheStatistics));
item.add(new Label("hitCount"));
item.add(new Label("missCount"));
item.add(new Label("putCount"));
item.add(new Label("elementCountInMemory"));
item.add(new Label("elementCountOnDisk"));
item.add(new Label("sizeInMemory"));
item.add(new Label("categoryName"));
}
};
add(cacheStats);

add(new org.apache.wicket.markup.html.link.Link("switch_stats") {

@Override
public void onClick() {
sf.getStatistics().setStatisticsEnabled(!sf.getStatistics().isStatisticsEnabled());
sf.getStatistics().clear();
final CompoundPropertyModel model = new CompoundPropertyModel(new LoadableDetachableModel() {
protected Object load() {
return sf.getStatistics();
}
});
st.setModel(model);

}

});
}

}

HibernateStatisticsPage.html

<html>
<body>
<div id="header">Hibernate Statistics</div>
<a href="#" wicket:id="build_index">Create Search Index</a>
<a href="#" wicket:id="switch_stats">En- or Disable Statistics</a>
<div wicket:id="stats">
<table width="100%">
<tr><td>isStatisticsEnabled</td><td><span wicket:id="isStatisticsEnabled"/></td></tr>
<tr><td>startTime</td><td><span wicket:id="startTime"/></td></tr>
<tr><td>sessionOpenCount</td><td><span wicket:id="sessionOpenCount"/></td></tr>
<tr><td>sessionCloseCount</td><td><span wicket:id="sessionCloseCount"/></td></tr>
<tr><td>flushCount</td><td><span wicket:id="flushCount"/></td></tr>
<tr><td>connectCount</td><td><span wicket:id="connectCount"/></td></tr>
<tr><td>prepareStatementCount</td><td><span wicket:id="prepareStatementCount"/></td></tr>
<tr><td>closeStatementCount</td><td><span wicket:id="closeStatementCount"/></td></tr>
<tr><td>entityLoadCount</td><td><span wicket:id="entityLoadCount"/></td></tr>
<tr><td>entityUpdateCount</td><td><span wicket:id="entityUpdateCount"/></td></tr>
<tr><td>entityInsertCount</td><td><span wicket:id="entityInsertCount"/></td></tr>
<tr><td>entityDeleteCount</td><td><span wicket:id="entityDeleteCount"/></td></tr>
<tr><td>entityFetchCount</td><td><span wicket:id="entityFetchCount"/></td></tr>
<tr><td>collectionLoadCount</td><td><span wicket:id="collectionLoadCount"/></td></tr>
<tr><td>collectionUpdateCount</td><td><span wicket:id="collectionUpdateCount"/></td></tr>
<tr><td>collectionRemoveCount</td><td><span wicket:id="collectionRemoveCount"/></td></tr>
<tr><td>collectionRecreateCount</td><td><span wicket:id="collectionRecreateCount"/></td></tr>
<tr><td>collectionFetchCount</td><td><span wicket:id="collectionFetchCount"/></td></tr>
<tr><td>secondLevelCacheHitCount</td><td><span wicket:id="secondLevelCacheHitCount"/></td></tr>
<tr><td>secondLevelCacheMissCount</td><td><span wicket:id="secondLevelCacheMissCount"/></td></tr>
<tr><td>secondLevelCachePutCount</td><td><span wicket:id="secondLevelCachePutCount"/></td></tr>
<tr><td>queryExecutionCount</td><td><span wicket:id="queryExecutionCount"/></td></tr>
<tr><td>queryExecutionMaxTime</td><td><span wicket:id="queryExecutionMaxTime"/></td></tr>
<tr><td>queryExecutionMaxTimeQueryString</td><td><span wicket:id="queryExecutionMaxTimeQueryString"/></td></tr>
<tr><td>queryCacheHitCount</td><td><span wicket:id="queryCacheHitCount"/></td></tr>
<tr><td>queryCacheMissCount</td><td><span wicket:id="queryCacheMissCount"/></td></tr>
<tr><td>queryCachePutCount</td><td><span wicket:id="queryCachePutCount"/></td></tr>
<tr><td>commitedTransactionCount</td><td><span wicket:id="commitedTransactionCount"/></td></tr>
<tr><td>transactionCount</td><td><span wicket:id="transactionCount"/></td></tr>
<tr><td>optimisticFailureCount</td><td><span wicket:id="optimisticFailureCount"/></td></tr>
</table>
</div>
<br>
<div id="header">Entity Statistics</div>
<table witdh="100%" border ="1">
<tr>
<td>Entity</td>
<td>Load Count</td>
<td>Fetch Count</td>
<td>Insert Count</td>
<td>Delete Count</td>
<td>Update Count</td>
<td>Optimistic Failure Count</td>
</tr>
<tr wicket:id="entities">
<td><span wicket:id="entityName"/></td>
<td><span wicket:id="loadCount"/></td>
<td><span wicket:id="fetchCount"/></td>
<td><span wicket:id="insertCount"/></td>
<td><span wicket:id="deleteCount"/></td>
<td><span wicket:id="updateCount"/></td>
<td><span wicket:id="optimisticFailureCount"/></td>
</tr>
</table>
<br>
<div id="header">Collection Statistics</div>
<table witdh="100%" border ="1">
<tr>
<td>Collection</td>
<td>Load Count</td>
<td>Fetch Count</td>
<td>Recreate Count</td>
<td>Remove Count</td>
<td>Update Count</td>
</tr>
<tr wicket:id="collections">
<td><span wicket:id="collName"/></td>
<td><span wicket:id="loadCount"/></td>
<td><span wicket:id="fetchCount"/></td>
<td><span wicket:id="recreateCount"/></td>
<td><span wicket:id="removeCount"/></td>
<td><span wicket:id="updateCount"/></td>
</tr>
</table>
<br>
<div id="header">Query Statistics</div>
<table witdh="100%" border ="1">
<tr>
<td>Query</td>
<td>Execution Count</td>
<td>Execution Row Count</td>
<td>Avg Time</td>
<td>Min Time</td>
<td>Max Time</td>
<td>Cache Hit Count</td>
<td>Cache Miss Count</td>
<td>Cache Put Count</td>
</tr>
<tr wicket:id="queries">
<td><span wicket:id="categoryName"/></td>
<td><span wicket:id="executionCount"/></td>
<td><span wicket:id="executionRowCount"/></td>
<td><span wicket:id="executionAvgTime"/></td>
<td><span wicket:id="executionMinTime"/></td>
<td><span wicket:id="executionMaxTime"/></td>
<td><span wicket:id="cacheHitCount"/></td>
<td><span wicket:id="cacheMissCount"/></td>
<td><span wicket:id="cachePutCount"/></td>
</tr>
</table>
<br>
<div id="header">Cache Statistics</div>
<table witdh="100%" border ="1">
<tr>
<td>Cache</td>
<td>Hit Count</td>
<td>Miss Count</td>
<td>Put Count</td>
<td>Elements in Memory</td>
<td>Elements on Disk</td>
<td>Size in Memory</td>
</tr>
<tr wicket:id="caches">
<td><span wicket:id="categoryName"/></td>
<td><span wicket:id="hitCount"/></td>
<td><span wicket:id="missCount"/></td>
<td><span wicket:id="putCount"/></td>
<td><span wicket:id="elementCountInMemory"/></td>
<td><span wicket:id="elementCountOnDisk"/></td>
<td><span wicket:id="sizeInMemory"/></td>
</tr>
</table>

</body>
</html>