001 /*
002 * Databinder: a simple bridge from Wicket to Hibernate
003 * Copyright (C) 2006 Nathan Hamblen nathan@technically.us
004
005 * This library is free software; you can redistribute it and/or
006 * modify it under the terms of the GNU Lesser General Public
007 * License as published by the Free Software Foundation; either
008 * version 2.1 of the License, or (at your option) any later version.
009 *
010 * This library is distributed in the hope that it will be useful,
011 * but WITHOUT ANY WARRANTY; without even the implied warranty of
012 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
013 * Lesser General Public License for more details.
014 *
015 * You should have received a copy of the GNU Lesser General Public
016 * License along with this library; if not, write to the Free Software
017 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
018 */
019 package net.databinder.models.hib;
020
021 import java.util.Iterator;
022
023 import net.databinder.hib.Databinder;
024 import net.databinder.models.PropertyDataProvider;
025
026 import org.apache.wicket.model.IModel;
027 import org.hibernate.Criteria;
028 import org.hibernate.Session;
029 import org.hibernate.criterion.Projections;
030
031 /**
032 * Provides query results to DataView and related components. Like the Hibernate model classes,
033 * the results of this provider can be altered by query binders and criteria builders. By default
034 * this provider wraps items in a compound property model in addition to a Hibernate model.
035 * This is convenient for mapping DataView subcomponents as bean properties (as with
036 * PropertyListView). However, <b>DataTable will not work with a compound property model.</b>
037 * Call setWrapWithPropertyModel(false) when using with DataTable, DataGridView, or any
038 * other time you do not want a compound property model.
039 * @author Nathan Hamblen
040 */
041 public class HibernateProvider extends PropertyDataProvider {
042 private Class objectClass;
043 private OrderingCriteriaBuilder criteriaBuilder;
044 private QueryBuilder queryBuilder, countQueryBuilder;
045
046 private Object factoryKey;
047
048 /**
049 * Provides all entities of the given class.
050 */
051 public HibernateProvider(Class objectClass) {
052 this.objectClass = objectClass;
053 }
054
055 /**
056 * Provides all entities of the given class using a distinct criteria builder for the order query.
057 * @param objectClass
058 * @param criteriaBuilder base criteria builder
059 * @param criteriaOrderer add ordering information ONLY, base criteria will be called first
060 */
061 public HibernateProvider(Class objectClass, final CriteriaBuilder criteriaBuilder, final CriteriaBuilder criteriaOrderer) {
062 this(objectClass);
063 this.criteriaBuilder = new OrderingCriteriaBuilder() {
064 public void buildOrdered(Criteria criteria) {
065 criteriaBuilder.build(criteria);
066 criteriaOrderer.build(criteria);
067 }
068 public void buildUnordered(Criteria criteria) {
069 criteriaBuilder.build(criteria);
070 }
071 };
072 }
073
074 /**
075 * Provides all entities of the given class.
076 * @param objectClass
077 * @param criteriaBuider builds different criteria objects for iterator() and size()
078 */
079 public HibernateProvider(Class objectClass, OrderingCriteriaBuilder criteriaBuider) {
080 this(objectClass);
081 this.criteriaBuilder = criteriaBuider;
082 }
083
084 /** Provides entities of the given class meeting the supplied criteria. */
085 public HibernateProvider(Class objectClass, final CriteriaBuilder criteriaBuilder) {
086 this(objectClass, new OrderingCriteriaBuilder() {
087 public void buildOrdered(Criteria criteria) {
088 criteriaBuilder.build(criteria);
089 }
090 public void buildUnordered(Criteria criteria) {
091 criteriaBuilder.build(criteria);
092 }
093 });
094 }
095
096 /**
097 * Provides entities matching the given query. The count query
098 * is derived by prefixing "select count(*)" to the given query; this will fail if
099 * the supplied query has a select clause.
100 */
101 public HibernateProvider(String query) {
102 this(query, makeCount(query));
103 }
104
105 /**
106 * Provides entities matching the given queries.
107 */
108 public HibernateProvider(final String query, final String countQuery) {
109 this(new QueryBinderBuilder(query), new QueryBinderBuilder(countQuery));
110 }
111
112 /**
113 * Provides entities matching the given query with bound parameters. The count query
114 * is derived by prefixing "select count(*)" to the given query; this will fail if
115 * the supplied query has a select clause.
116 */
117 public HibernateProvider(String query, QueryBinder queryBinder) {
118 this(query, queryBinder, makeCount(query), queryBinder);
119 }
120
121 /**
122 * Provides entities matching the given queries with bound parameters.
123 * @param query query to return entities
124 * @param queryBinder binder for the standard query
125 * @param countQuery query to return count of entities
126 * @param countQueryBinder binder for the count query (may be same as queryBinder)
127 */
128 public HibernateProvider(final String query, final QueryBinder queryBinder, final String countQuery, final QueryBinder countQueryBinder) {
129 this(new QueryBinderBuilder(query, queryBinder), new QueryBinderBuilder(countQuery, countQueryBinder));
130 }
131
132 public HibernateProvider(QueryBuilder queryBuilder, QueryBuilder countQueryBuilder) {
133 this.queryBuilder = queryBuilder;
134 this.countQueryBuilder = countQueryBuilder;
135 }
136
137 /** @return query with select count(*) prepended */
138 static protected String makeCount(String query) {
139 return "select count(*) " + query;
140 }
141
142 /** @return session factory key, or null for the default factory */
143 public Object getFactoryKey() {
144 return factoryKey;
145 }
146
147 /**
148 * Set a factory key other than the default (null).
149 * @param key session factory key
150 * @return this, for chaining
151 */
152 public HibernateProvider setFactoryKey(Object key) {
153 this.factoryKey = key;
154 return this;
155 }
156
157 /**
158 * It should not normally be necessary to override (or call) this default implementation.
159 */
160 public Iterator iterator(int first, int count) {
161 Session sess = Databinder.getHibernateSession(factoryKey);
162
163 if(queryBuilder != null) {
164 org.hibernate.Query q = queryBuilder.build(sess);
165 q.setFirstResult(first);
166 q.setMaxResults(count);
167 return q.iterate();
168 }
169
170 Criteria crit = sess.createCriteria(objectClass);
171 if (criteriaBuilder != null)
172 criteriaBuilder.buildOrdered(crit);
173
174 crit.setFirstResult(first);
175 crit.setMaxResults(count);
176 return crit.list().iterator();
177 }
178
179 /**
180 * Only override this method if a single count query or
181 * criteria projection is not possible.
182 */
183 public int size() {
184 Session sess = Databinder.getHibernateSession(factoryKey);
185
186 if(countQueryBuilder != null) {
187 org.hibernate.Query q = countQueryBuilder.build(sess);
188 Object obj = q.uniqueResult();
189 return ((Number) obj).intValue();
190 }
191
192 Criteria crit = sess.createCriteria(objectClass);
193
194 if (criteriaBuilder != null)
195 criteriaBuilder.buildUnordered(crit);
196 crit.setProjection(Projections.rowCount());
197 Integer size = (Integer) crit.uniqueResult();
198 return size == null ? 0 : size;
199 }
200
201
202 @Override
203 protected IModel dataModel(Object object) {
204 return new HibernateObjectModel(object);
205 }
206
207 /** does nothing */
208 public void detach() {
209 }
210 }