View Javadoc
1   /**
2    * Copyright 2005-2015 The Kuali Foundation
3    *
4    * Licensed under the Educational Community License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    * http://www.opensource.org/licenses/ecl2.php
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  package org.kuali.rice.krad.data.jpa;
17  
18  import org.kuali.rice.core.api.criteria.CountFlag;
19  import org.kuali.rice.core.api.criteria.GenericQueryResults;
20  import org.kuali.rice.core.api.criteria.QueryByCriteria;
21  
22  import javax.persistence.Query;
23  import java.util.List;
24  
25  /**
26   * Base class for QueryByCriteria lookups and deletes for JPA PersistenceProvider implementations.
27   *
28   * <p>
29   * Implements the core api CriteriaLookupService, as that is the exact interface required, however this class is not
30   * intended to be defined as a "service", it is merely a helper.
31   * </p>
32   *
33   * @author Kuali Rice Team (rice.collab@kuali.org)
34   */
35  abstract class DataObjectCriteriaQueryBase<C, Q> implements CriteriaQuery {
36  
37      /**
38       * Gets the QueryTranslator to translate from the API to implementation-specific classes.
39       *
40       * @return the QueryTranslator to translate from the API to implementation-specific classes.
41       */
42      protected abstract QueryTranslator<C, Q> getQueryTranslator();
43  
44      /**
45       * Gets the row count for the given query.
46       *
47       * @param query the query to count the rows on.
48       * @return the row count for the given query.
49       */
50      protected abstract int getRowCount(Q query);
51  
52      /**
53       * Gets the row count to include along with the results of the query.
54       *
55       * @param query the query to count the rows on.
56       * @param rows the result rows.
57       *
58       * @return The row count to include along with the results of the query.
59       */
60      protected abstract int getIncludedRowCount(Q query, List rows);
61  
62      /**
63       * Gets the results from the given query.
64       *
65       * @param query the query to use to get the results.
66       * @param <T> the type of results to return.
67       * @return a list of results from the given query.
68       */
69      protected abstract <T> List<T> getResults(Q query);
70  
71      /**
72       * Executes the given query.
73       *
74       * @param query the query to execute.
75       * @return the number of records successfully committed.
76       */
77      protected abstract int executeUpdate(Query query);
78  
79      /**
80       * {@inheritDoc}
81       */
82      public <T> void deleteMatching(Class<T> type, QueryByCriteria criteria) {
83  
84          if (type == null) {
85              throw new IllegalArgumentException("class type is null");
86          }
87  
88          // do not allow delete * on an entire table, by default
89          if (criteria == null || criteria.getPredicate() == null) {
90              throw new IllegalArgumentException("criteria is null");
91          }
92  
93          final C parent = getQueryTranslator().translateCriteria(type, criteria);
94          final Query query = getQueryTranslator().createDeletionQuery(type, parent);
95          executeUpdate(query);
96      }
97  
98      /**
99       * {@inheritDoc}
100      */
101     public <T> void deleteAll(Class<T> type) {
102         if (type == null) {
103             throw new IllegalArgumentException("class type is null");
104         }
105 
106         final C parent = getQueryTranslator().translateCriteria(type,
107                 QueryByCriteria.Builder.create().build());
108         final Query query = getQueryTranslator().createDeletionQuery(type, parent);
109         executeUpdate(query);
110     }
111 
112     /**
113      * {@inheritDoc}
114      */
115     @Override
116     public <T> GenericQueryResults<T> lookup(Class<T> queryClass, QueryByCriteria criteria) {
117         if (queryClass == null) {
118             throw new IllegalArgumentException("queryClass is null");
119         }
120 
121         if (criteria == null) {
122             throw new IllegalArgumentException("criteria is null");
123         }
124 
125         final C parent = getQueryTranslator().translateCriteria(queryClass, criteria);
126 
127         switch (criteria.getCountFlag()) {
128             case ONLY:
129                 return forCountOnly(queryClass, criteria, parent);
130             case NONE:
131                 return forRowResults(queryClass, criteria, parent, criteria.getCountFlag());
132             case INCLUDE:
133                 return forRowResults(queryClass, criteria, parent, criteria.getCountFlag());
134             default: throw new UnsupportedCountFlagException(criteria.getCountFlag());
135         }
136     }
137 
138     /**
139      * Gets results where the actual rows are requested.
140      *
141      * @param queryClass the type of the results to return.
142      * @param criteria the criteria to use to get the results.
143      * @param ojbCriteria the implementation-specific criteria.
144      * @param flag the indicator to whether the row count is requested in the results.
145      *
146      * @return results where the actual rows are requested.
147      */
148     protected <T> GenericQueryResults<T> forRowResults(final Class<T> queryClass, final QueryByCriteria criteria, final C ojbCriteria, CountFlag flag) {
149         final Q query = getQueryTranslator().createQuery(queryClass, ojbCriteria);
150         final GenericQueryResults.Builder<T> results = GenericQueryResults.Builder.<T>create();
151 
152         getQueryTranslator().convertQueryFlags(criteria, query);
153 
154         final List<T> rows = getResults(query);
155         if (flag == CountFlag.INCLUDE) {
156             results.setTotalRowCount(getIncludedRowCount(query, rows));
157         }
158 
159         if (criteria.getMaxResults() != null && rows.size() > criteria.getMaxResults()) {
160             results.setMoreResultsAvailable(true);
161             //remove the extra row that was returned
162             rows.remove(criteria.getMaxResults().intValue());
163         }
164 
165         results.setResults(rows);
166         return results.build();
167     }
168 
169     /**
170      * Gets results where only the count is requested.
171      *
172      * @param queryClass the type of the results to return.
173      * @param criteria the criteria to use to get the results.
174      * @param platformCriteria the implementation-specific criteria.
175      *
176      * @return results where only the count is requested.
177      */
178     protected <T> GenericQueryResults<T> forCountOnly(final Class<T> queryClass, final QueryByCriteria criteria, final C platformCriteria) {
179         final Q query = getQueryTranslator().createQuery(queryClass, platformCriteria);
180         final GenericQueryResults.Builder<T> results = GenericQueryResults.Builder.<T>create();
181         results.setTotalRowCount(getRowCount(query));
182         return results.build();
183     }
184 
185     /**
186      * An error to throw when the CountFlag is not recognized.
187      *
188      * <p>This is a fatal error since this implementation should support all known count flags.</p>
189      */
190     protected static class UnsupportedCountFlagException extends RuntimeException {
191 
192         /**
193          * Creates an exception for if the CountFlag is not recognized.
194          * @param flag the flag in error.
195          */
196         protected UnsupportedCountFlagException(CountFlag flag) {
197             super("Unsupported predicate [" + String.valueOf(flag) + "]");
198         }
199     }
200 }