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    }