/*
* Databinder: a simple bridge from Wicket to Hibernate
* Copyright (C) 2007 Nathan Hamblen nathan@technically.us
* Copyright (C) 2007 xoocode.org project
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
package net.databinder.components.hib;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import net.databinder.hib.Databinder;
import net.databinder.models.hib.HibernateObjectModel;
import org.apache.wicket.Component;
import org.apache.wicket.ajax.AjaxRequestTarget;
import org.apache.wicket.ajax.markup.html.form.AjaxButton;
import org.apache.wicket.extensions.markup.html.repeater.data.table.AbstractColumn;
import org.apache.wicket.extensions.markup.html.repeater.data.table.DataTable;
import org.apache.wicket.extensions.markup.html.repeater.data.table.HeadersToolbar;
import org.apache.wicket.extensions.markup.html.repeater.data.table.IColumn;
import org.apache.wicket.extensions.markup.html.repeater.data.table.NavigationToolbar;
import org.apache.wicket.extensions.markup.html.repeater.data.table.PropertyColumn;
import org.apache.wicket.markup.html.WebMarkupContainer;
import org.apache.wicket.markup.html.basic.Label;
import org.apache.wicket.markup.html.form.Form;
import org.apache.wicket.markup.html.form.TextArea;
import org.apache.wicket.markup.html.panel.Panel;
import org.apache.wicket.markup.repeater.Item;
import org.apache.wicket.markup.repeater.data.IDataProvider;
import org.apache.wicket.model.CompoundPropertyModel;
import org.apache.wicket.model.IModel;
import org.apache.wicket.model.Model;
import org.apache.wicket.model.PropertyModel;
import org.apache.wicket.util.string.Strings;
import org.hibernate.Query;
import org.hibernate.QueryException;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.metadata.ClassMetadata;
import org.hibernate.type.Type;
/**
* A Panel used to display a textarea to enter an HQL query and execute it
* against the current session of a {@link SessionFactory}.
* <p>
* The panel result is displayed in a data table, where columns are created
* according to the query.
* <p>
* For instance, a query like:
* <pre>
* select job.name as name, job.id as id from JobModel job
* </pre>
* will result in two columns, 'name' and 'id', in the result data table.
* <p>
* If you run:
* <pre>
* from JobModel
* </pre>
* the columns in the result table will be the available properties of a
* JobModel
*/
public class QueryPanel extends Panel {
private static final long serialVersionUID = 1L;
/**
* Bean used to store the query
*/
private QueryBean query = new QueryBean();
/**
* Stores information about the query execution (executed query, time, ...)
*/
private String executionInfo;
/**
* Constructs an {@link QueryPanel}
* @param id
* the panel identifier. Must not be null.
*/
public QueryPanel(String id) {
super(id);
final WebMarkupContainer resultsHolder = new WebMarkupContainer("resultsHolder");
resultsHolder.add(new Label("executionInfo", new PropertyModel(this, "executionInfo")));
resultsHolder.add(getResultsTable());
resultsHolder.setOutputMarkupId(true);
add(resultsHolder);
Form<QueryBean> form = new Form<QueryBean>("form", new CompoundPropertyModel<QueryBean>(query));
form.setOutputMarkupId(true);
form.add(new TextArea("query"));
form.add(new AjaxButton("submit", form) {
private static final long serialVersionUID = 1L;
protected void onSubmit(AjaxRequestTarget target, Form form)
{
if (resultsHolder.get("results") != null) {
resultsHolder.remove("results");
}
try {
resultsHolder.add(getResultsTable());
} catch (QueryException e) {
note(e);
} catch (IllegalArgumentException e) {
note(e);
} catch (IllegalStateException e) {
note(e);
}
target.addComponent(resultsHolder);
}
private void note(Exception e) {
resultsHolder.add(new Label("results",
e.getClass().getSimpleName()+ ": " + e.getMessage()));
}
});
add(form);
}
/**
* Creates a result table for the current query.
* @return a result table, or an empty label if there is no current query
*/
@SuppressWarnings("unchecked")
private Component getResultsTable() {
if (Strings.isEmpty(query.getQuery())) {
return new Label("results", "");
} else {
IDataProvider dataProvider = new IDataProvider() {
private static final long serialVersionUID = 1L;
public void detach() {
}
public int size() {
Session sess = Databinder.getHibernateSession();
Query query = sess.createQuery(getQuery());
return query.list().size();
}
public String getQuery() {
return QueryPanel.this.query.getQuery();
}
@SuppressWarnings("unchecked")
public IModel<?> model(Object object) {
return new CompoundPropertyModel(new HibernateObjectModel(object));
}
public Iterator iterator(int first, int count) {
Session sess = Databinder.getHibernateSession();
long start = System.nanoTime();
try {
Query q = sess.createQuery(getQuery());
q.setFirstResult(first);
q.setMaxResults(count);
return q.iterate();
} finally {
float nanoTime = ((System.nanoTime()-start) / 1000) / 1000.0f;
setExecutionInfo("query executed in "+nanoTime+" ms: "+getQuery());
}
}
};
IColumn[] columns;
Session sess = Databinder.getHibernateSession();
Query q = sess.createQuery(query.getQuery());
String[] aliases;
Type[] returnTypes;
try {
aliases = q.getReturnAliases();
returnTypes = q.getReturnTypes();
} catch (NullPointerException e) { // thrown on updates
return new Label("results", "");
}
if (returnTypes.length != 1) {
columns = new IColumn[returnTypes.length];
for (int i = 0; i < returnTypes.length; i++) {
String alias = aliases == null || aliases.length <= i?returnTypes[i].getName():aliases[i];
final int index = i;
columns[i] = new AbstractColumn(new Model(alias)) {
private static final long serialVersionUID = 1L;
public void populateItem(Item cellItem, String componentId, IModel rowModel) {
Object[] objects = (Object[]) rowModel.getObject();
cellItem.add(new Label(componentId,
new Model(objects[index]==null?"":objects[index].toString())));
}
};
}
} else {
Type returnType = returnTypes[0];
if (returnType.isEntityType()) {
Class clss = returnType.getReturnedClass();
ClassMetadata metadata = Databinder.getHibernateSessionFactory().getClassMetadata(clss);
List<IColumn> cols = new ArrayList<IColumn>();
String idProp = metadata.getIdentifierPropertyName();
cols.add(new PropertyColumn(new Model(idProp), idProp));
String[] properties = metadata.getPropertyNames();
for (String prop : properties) {
Type type = metadata.getPropertyType(prop);
if (type.isCollectionType()) {
// TODO: see if we could provide a link to the collection value
} else {
cols.add(new PropertyColumn(new Model(prop), prop));
}
}
columns = (IColumn[]) cols.toArray(new IColumn[cols.size()]);
} else {
String alias = aliases == null || aliases.length == 0?returnType.getName():aliases[0];
columns = new IColumn[] {new AbstractColumn(new Model(alias)) {
private static final long serialVersionUID = 1L;
public void populateItem(Item cellItem, String componentId, IModel rowModel) {
cellItem.add(new Label(componentId, rowModel));
}
}};
}
}
DataTable dataTable = new DataTable("results", columns, dataProvider, 10);
dataTable.addTopToolbar(new HeadersToolbar(dataTable, null));
dataTable.addBottomToolbar(new NavigationToolbar(dataTable));
dataTable.setOutputMarkupId(true);
return dataTable;
}
}
private static class QueryBean implements Serializable {
private static final long serialVersionUID = 1L;
private String query;
public String getQuery() {
return query;
}
public void setQuery(String query) {
this.query = query;
}
}
public String getExecutionInfo() {
return executionInfo;
}
public void setExecutionInfo(String executionInfo) {
this.executionInfo = executionInfo;
}
}