View Javadoc

1   /*
2    * Copyright 2011 The Kuali Foundation
3    *
4    * Licensed under the the Educational Community License, Version 1.0
5    * (the "License"); you may not use this file except in compliance
6    * with the License.  You may obtain a copy of the License at
7    *
8    * http://www.opensource.org/licenses/ecl1.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
13   * implied.  See the License for the specific language governing
14   * permissions and limitations under the License.
15   */
16  
17  package org.kuali.student.r2.common.dao;
18  
19  import org.apache.commons.lang.StringUtils;
20  import org.kuali.student.r2.common.entity.PersistableEntity;
21  import org.kuali.student.r2.common.exceptions.DoesNotExistException;
22  
23  import javax.persistence.EntityManager;
24  import javax.persistence.NoResultException;
25  import javax.persistence.NonUniqueResultException;
26  import javax.persistence.PersistenceContext;
27  import javax.persistence.Query;
28  import javax.persistence.TypedQuery;
29  import java.lang.reflect.ParameterizedType;
30  import java.lang.reflect.Type;
31  import java.util.ArrayList;
32  import java.util.HashSet;
33  import java.util.Iterator;
34  import java.util.List;
35  import java.util.Set;
36  
37  /**
38   * @author Kuali Student Team
39   */
40  public class GenericEntityDao<T extends PersistableEntity<String>> implements EntityDao<String,T> {
41  
42      /**
43       * Entity class.
44       */
45      protected Class<T> entityClass;
46  
47      @PersistenceContext
48      protected EntityManager em;
49  
50      protected Boolean enableMaxIdFetch = Boolean.TRUE;
51      protected Integer maxInClauseElements = 1000;
52  
53      public GenericEntityDao() {
54          entityClass = getEntityClass();
55      }
56  
57      @Override
58      public T find(String primaryKey) {
59          return em.find(entityClass, primaryKey);
60      }
61  
62      @Override
63      public List<T> findByIds(String primaryKeyMemberName, List<String> primaryKeys) throws DoesNotExistException {
64  
65          // fix for jira KSENROLL-2949
66          if (primaryKeys == null || primaryKeys.isEmpty())
67              return new ArrayList<T>();
68  
69          Set<String>primaryKeySet = new HashSet<String>(primaryKeys.size());
70          // remove duplicates from the key list
71          primaryKeySet.addAll(primaryKeys);
72  
73          StringBuilder queryString = new StringBuilder();
74  
75          TypedQuery<T> query = buildQuery(queryString, primaryKeyMemberName, primaryKeySet);
76  
77          List<T> resultList = query.getResultList();
78  
79          verifyResults(resultList, primaryKeySet);
80  
81          return resultList;
82      }
83  
84      /**
85       * Returns a query of find by ids. Breaks up the query into multiple ORed IN() clauses if the set of ids is larger
86       * than maxInClauseElements
87       * @param queryStringRef a reference to the query string
88       * @param primaryKeyMemberName name of the column that is the primary key
89       * @param primaryKeySet set of primary key strings
90       * @return a typed query that finds entities for the set of keys
91       */
92      protected TypedQuery<T> buildQuery(StringBuilder queryStringRef, String primaryKeyMemberName, Set<String> primaryKeySet) {
93  
94          TypedQuery<T> queryRef;
95  
96          if (!enableMaxIdFetch || primaryKeySet.size() <= maxInClauseElements) {
97  
98              queryStringRef.append("from ").append(entityClass.getSimpleName()).append(" where ").append(primaryKeyMemberName).append(" in (:ids)");
99              queryRef = em.createQuery(queryStringRef.toString(), entityClass).setParameter("ids", primaryKeySet);
100 
101         } else {
102             //Max fetchh is enabled so break uip the where clause into multiple IN() clauses
103             List<List<String>> brokenLists = new ArrayList<List<String>>();
104             List<String> lst = new ArrayList<String>();
105 
106             queryStringRef.append("from ").append(entityClass.getSimpleName());
107 
108             Iterator<String> itr = primaryKeySet.iterator();
109             for (int index = 0; itr.hasNext(); index++) {
110 
111                 if (index % maxInClauseElements == 0) {
112 
113                     brokenLists.add(lst);
114 
115                     if (brokenLists.size() == 1) {
116                         queryStringRef.append(" where ").append(primaryKeyMemberName).append(" in (:ids1)");
117                     } else {
118                         queryStringRef.append(" or ").append(primaryKeyMemberName).append(" in (:ids").append(brokenLists.size()).append(")");
119                     }
120 
121                 }
122                 lst.add(itr.next());
123             }
124 
125             queryRef = em.createQuery(queryStringRef.toString(), entityClass);
126 
127             for (int i = 1; i <= brokenLists.size(); i++) {
128                 queryRef.setParameter("ids" + i, brokenLists.get(i - 1));
129             }
130 
131         }
132         return queryRef;
133     }
134 
135     /**
136      * Check if an entity exists with the primary key given.
137      * 
138      * @param primaryKey the primary key identifier for the entity that will be checked.
139      * @return true if there is a row/entity with the primary key given in the database; false otherwise.
140      * 
141      */
142     public boolean entityExists (String primaryKey) {
143         
144         // TODO: see if this can be externalized as a named query.
145         Query q = em.createQuery("select id from " + entityClass.getSimpleName() + " where id = :key").setParameter("key", primaryKey);
146     
147         try {
148             q.getSingleResult();
149         }
150         catch (NonUniqueResultException e) {
151             // more than 1 match (should never happen...)
152             return false;
153         } 
154         catch (NoResultException e) {
155             // zero matches
156             return false;
157         }
158 
159         // all other cases
160         return true;
161         
162     }
163 
164     protected void verifyResults(List<T> resultList, Set<String> primaryKeys) throws DoesNotExistException {
165 
166     	 if (resultList.size() == 0){
167 
168              throw new DoesNotExistException("No data was found for : " + StringUtils.join(primaryKeys, ", "));
169 
170          } else if (resultList.size() != primaryKeys.size()) {
171         	 // only found some of the keys given.
172          	Set<String> unmatchedKeySet = new HashSet<String> ();
173          
174          	unmatchedKeySet.addAll(primaryKeys);
175 
176          	for (T t : resultList) {
177  				
178          		unmatchedKeySet.remove(t.getId());
179  			}
180          	
181          	throw new DoesNotExistException("Missing data for : " + StringUtils.join(unmatchedKeySet.iterator(), ", "));
182          	
183          }
184 	}
185 
186 	@Override
187     public List<T> findByIds(List<String> primaryKeys) throws DoesNotExistException {
188 
189         return this.findByIds("id", primaryKeys);
190 
191     }
192 
193     @Override
194     @SuppressWarnings("unchecked")
195     public List<T> findAll() {
196         return (List<T>) em.createQuery("from " + entityClass.getSimpleName()).getResultList();
197     }
198 
199     @Override
200     public void persist(T entity) {
201         em.persist(entity);
202     }
203 
204     @Override
205     public void update(T entity) {
206         em.merge(entity);
207     }
208 
209     @Override
210     public void remove(T entity) {
211         em.remove(entity);
212     }
213 
214     @Override
215     public T merge(T entity) {
216         
217         if (em.contains(entity))
218             em.detach(entity);
219         
220         return em.merge(entity);
221     }
222 
223     @SuppressWarnings("unchecked")
224     protected <C extends T> Class<C> getEntityClass() {
225         if (entityClass == null) {
226             entityClass = (Class<T>) getEntityType(this);
227         }
228         return (Class<C>) entityClass;
229     }
230 
231     private Type getEntityType(Object object) {
232         Type type = object.getClass().getGenericSuperclass();
233         if (type instanceof ParameterizedType) {
234             ParameterizedType paramType = (ParameterizedType) type;
235             Type result = paramType.getActualTypeArguments()[0];
236             return result;
237         } else {
238             throw new IllegalArgumentException("Could not guess entity type by reflection.");
239         }
240     }
241 
242     @Override
243     public void setEm(EntityManager em) {
244         this.em = em;
245     }
246 
247     @Override
248     public EntityManager getEm() {
249         return em;
250     }
251 
252     public void setEnableMaxIdFetch(Boolean enableMaxIdFetch) {
253         this.enableMaxIdFetch = enableMaxIdFetch;
254     }
255 
256     public void setMaxInClauseElements(Integer maxInClauseElements) {
257         this.maxInClauseElements = maxInClauseElements;
258     }
259 
260 }