View Javadoc

1   /**
2    * Copyright 2005-2013 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.dao.impl;
17  
18  import org.apache.commons.lang.StringUtils;
19  import org.apache.ojb.broker.query.Criteria;
20  import org.apache.ojb.broker.query.QueryByCriteria;
21  import org.apache.ojb.broker.query.QueryFactory;
22  import org.kuali.rice.core.framework.persistence.ojb.dao.PlatformAwareDaoBaseOjb;
23  import org.kuali.rice.krad.bo.BusinessObject;
24  import org.kuali.rice.krad.bo.PersistableBusinessObject;
25  import org.kuali.rice.krad.dao.BusinessObjectDao;
26  import org.kuali.rice.krad.service.KRADServiceLocatorInternal;
27  import org.kuali.rice.krad.service.KRADServiceLocatorWeb;
28  import org.kuali.rice.krad.service.PersistenceStructureService;
29  import org.kuali.rice.krad.util.KRADPropertyConstants;
30  import org.kuali.rice.krad.util.ObjectUtils;
31  import org.kuali.rice.krad.util.OjbCollectionAware;
32  import org.springframework.dao.DataAccessException;
33  
34  import java.lang.reflect.InvocationTargetException;
35  import java.util.Collection;
36  import java.util.HashMap;
37  import java.util.Iterator;
38  import java.util.List;
39  import java.util.Map;
40  import java.util.Set;
41  
42  /**
43   * This class is the OJB implementation of the BusinessObjectDao interface and should be used for generic business object unit
44   * tests.
45   */
46  public class BusinessObjectDaoOjb extends PlatformAwareDaoBaseOjb implements BusinessObjectDao, OjbCollectionAware {
47      private static final org.apache.log4j.Logger LOG = org.apache.log4j.Logger.getLogger(BusinessObjectDaoOjb.class);
48  
49      private PersistenceStructureService persistenceStructureService;
50  
51      /**
52  	 * This constructs a {@link BusinessObjectDaoOjb}
53  	 */
54  	public BusinessObjectDaoOjb(PersistenceStructureService persistenceStructureService) {
55  		this.persistenceStructureService = persistenceStructureService;
56  	}
57  
58      /**
59  	 * @see org.kuali.rice.krad.dao.BusinessObjectDao#findBySinglePrimaryKey(java.lang.Class, java.lang.Object)
60  	 */
61  	public <T extends BusinessObject> T findBySinglePrimaryKey(Class<T> clazz, Object primaryKey) {
62  		if (primaryKey.getClass().getName().startsWith("java.lang.")
63                  || primaryKey.getClass().getName().startsWith("java.sql.")
64                  || primaryKey.getClass().getName().startsWith("java.math.")
65                  || primaryKey.getClass().getName().startsWith("java.util.")) {
66  			try {
67  				return (T) getPersistenceBrokerTemplate().getObjectById(clazz, primaryKey);
68  			} catch ( DataAccessException ex ) {
69  	    		// it doesn't exist, just return null
70  				return null;
71  			}
72  		} else {
73  			Criteria criteria = buildCriteria(clazz, primaryKey);
74  
75  	        return (T) getPersistenceBrokerTemplate().getObjectByQuery(QueryFactory.newQuery(clazz, criteria));
76  		}
77  	}
78  
79      /**
80       * @see org.kuali.rice.krad.dao.BusinessObjectDao#findByPrimaryKey(java.lang.Class, java.util.Map)
81       */
82      public <T extends BusinessObject> T findByPrimaryKey(Class<T> clazz, Map<String, ?> primaryKeys) {
83          Criteria criteria = buildCriteria(primaryKeys);
84  
85          return (T) getPersistenceBrokerTemplate().getObjectByQuery(QueryFactory.newQuery(clazz, criteria));
86      }
87  
88      /**
89       * Retrieves all of the records for a given class name.
90       *
91       * @param clazz - the name of the object being used, either KualiCodeBase or a subclass
92       * @return Collection
93       * @see org.kuali.rice.krad.dao.BusinessObjectDao#findAll(java.lang.Class)
94       */
95      public <T extends BusinessObject> Collection<T> findAll(Class<T> clazz) {
96          return (Collection<T>)getPersistenceBrokerTemplate().getCollectionByQuery(QueryFactory.newQuery(clazz, (Criteria) null));
97      }
98  
99      /**
100      * @see org.kuali.rice.krad.dao.BusinessObjectDao#findAllOrderBy(java.lang.Class, java.lang.String, boolean)
101      */
102     public <T extends BusinessObject> Collection<T> findAllOrderBy(Class<T> clazz, String sortField, boolean sortAscending) {
103         QueryByCriteria queryByCriteria = new QueryByCriteria(clazz, (Criteria) null);
104 
105         if (sortAscending) {
106             queryByCriteria.addOrderByAscending(sortField);
107         }
108         else {
109             queryByCriteria.addOrderByDescending(sortField);
110         }
111 
112         return (Collection<T>)getPersistenceBrokerTemplate().getCollectionByQuery(queryByCriteria);
113     }
114 
115     /**
116      * This is the default impl that comes with Kuali - uses OJB.
117      *
118      * @see org.kuali.rice.krad.dao.BusinessObjectDao#findMatching(java.lang.Class, java.util.Map)
119      */
120     public <T extends BusinessObject> Collection<T> findMatching(Class<T> clazz, Map<String, ?> fieldValues) {
121         Criteria criteria = buildCriteria(fieldValues);
122 
123         return (Collection<T>)getPersistenceBrokerTemplate().getCollectionByQuery(QueryFactory.newQuery(clazz, criteria));
124     }
125 
126 
127     /**
128 	 * Throws an UnsupportedOperationException
129 	 * @see org.kuali.rice.krad.dao.BusinessObjectDao#findMatching(org.kuali.rice.core.framework.persistence.jpa.criteria.Criteria)
130 	 */
131 	//public <T extends BusinessObject> Collection<T> findMatching(org.kuali.rice.core.jpa.criteria.Criteria criteria) {
132 	//	throw new UnsupportedOperationException("OJB does not support finding matching business objects using JPA criteria");
133 	//}
134 
135 	/**
136      * @see org.kuali.rice.krad.dao.BusinessObjectDao#findAllActive(java.lang.Class)
137      */
138     public <T extends BusinessObject> Collection<T> findAllActive(Class<T> clazz) {
139         return (Collection<T>)getPersistenceBrokerTemplate().getCollectionByQuery(QueryFactory.newQuery(clazz, buildActiveCriteria()));
140     }
141 
142     /**
143      * @see org.kuali.rice.krad.dao.BusinessObjectDao#findAllActive(java.lang.Class)
144      */
145     public <T extends BusinessObject> Collection<T> findAllInactive(Class<T> clazz) {
146         return (Collection<T>)getPersistenceBrokerTemplate().getCollectionByQuery(QueryFactory.newQuery(clazz, buildInactiveCriteria()));
147     }
148 
149     /**
150      * @see org.kuali.rice.krad.dao.BusinessObjectDao#findAllActiveOrderBy(java.lang.Class, java.lang.String, boolean)
151      */
152     public <T extends BusinessObject> Collection<T> findAllActiveOrderBy(Class<T> clazz, String sortField, boolean sortAscending) {
153         QueryByCriteria queryByCriteria = new QueryByCriteria(clazz, buildActiveCriteria());
154 
155         if (sortAscending) {
156             queryByCriteria.addOrderByAscending(sortField);
157         }
158         else {
159             queryByCriteria.addOrderByDescending(sortField);
160         }
161 
162         return (Collection<T>)getPersistenceBrokerTemplate().getCollectionByQuery(queryByCriteria);
163     }
164 
165     /**
166      * @see org.kuali.rice.krad.dao.BusinessObjectDao#findMatchingActive(java.lang.Class, java.util.Map)
167      */
168     public <T extends BusinessObject> Collection<T> findMatchingActive(Class<T> clazz, Map<String, ?> fieldValues) {
169         Criteria criteria = buildCriteria(fieldValues);
170         criteria.addAndCriteria(buildActiveCriteria());
171 
172         return (Collection<T>)getPersistenceBrokerTemplate().getCollectionByQuery(QueryFactory.newQuery(clazz, criteria));
173     }
174 
175     /**
176      * This is the default impl that comes with Kuali - uses OJB.
177      *
178      * @see org.kuali.rice.krad.dao.BusinessObjectDao#countMatching(java.lang.Class, java.util.Map)
179      */
180     public int countMatching(Class clazz, Map<String, ?> fieldValues) {
181         Criteria criteria = buildCriteria(fieldValues);
182 
183         return getPersistenceBrokerTemplate().getCount(QueryFactory.newQuery(clazz, criteria));
184     }
185 
186     /**
187      * This is the default impl that comes with Kuali - uses OJB.
188      *
189      * @see org.kuali.rice.krad.dao.BusinessObjectDao#countMatching(java.lang.Class, java.util.Map, java.util.Map)
190      */
191     public int countMatching(Class clazz, Map<String, ?> positiveFieldValues, Map<String, ?> negativeFieldValues) {
192         Criteria criteria = buildCriteria(positiveFieldValues);
193         Criteria negativeCriteria = buildNegativeCriteria(negativeFieldValues);
194         criteria.addAndCriteria(negativeCriteria);
195         return getPersistenceBrokerTemplate().getCount(QueryFactory.newQuery(clazz, criteria));
196     }
197 
198 
199     /**
200      * This is the default impl that comes with Kuali - uses OJB.
201      *
202      * @see org.kuali.rice.krad.dao.BusinessObjectDao#findMatching(java.lang.Class, java.util.Map)
203      */
204     public <T extends BusinessObject> Collection<T> findMatchingOrderBy(Class<T> clazz, Map<String, ?> fieldValues, String sortField, boolean sortAscending) {
205         Criteria criteria = buildCriteria(fieldValues);
206         QueryByCriteria queryByCriteria = new QueryByCriteria(clazz, criteria);
207 
208         if (sortAscending) {
209             queryByCriteria.addOrderByAscending(sortField);
210         }
211         else {
212             queryByCriteria.addOrderByDescending(sortField);
213         }
214 
215         return (Collection<T>)getPersistenceBrokerTemplate().getCollectionByQuery(queryByCriteria);
216     }
217 
218 	/**
219 	 * Saves a business object.
220 	 *
221 	 * @see org.kuali.rice.krad.dao.BusinessObjectDao#save(org.kuali.rice.krad.bo.PersistableBusinessObject)
222 	 */
223 	public PersistableBusinessObject save(PersistableBusinessObject bo) throws DataAccessException {
224 		// if collections exist on the BO, create a copy and use to process the
225 		// collections to ensure
226 		// that removed elements are deleted from the database
227 		Set<String> boCollections = getPersistenceStructureService().listCollectionObjectTypes(bo.getClass()).keySet();
228 		PersistableBusinessObject savedBo = null;
229 		if (!boCollections.isEmpty()) {
230 			// refresh bo to get db copy of collections
231 			savedBo = (PersistableBusinessObject) ObjectUtils.deepCopy(bo);
232 			for (String boCollection : boCollections) {
233 				if (getPersistenceStructureService().isCollectionUpdatable(savedBo.getClass(), boCollection)) {
234 					savedBo.refreshReferenceObject(boCollection);
235 				}
236 			}
237             KRADServiceLocatorInternal.getOjbCollectionHelper().processCollections(this, bo, savedBo);
238         }
239 
240 		getPersistenceBrokerTemplate().store(bo);
241 		return bo;
242 	}
243 
244     /**
245      * Saves a business object.
246      *
247      * @see org.kuali.rice.krad.dao.BusinessObjectDao#save(org.kuali.rice.krad.bo.PersistableBusinessObject)
248      */
249     public List<? extends PersistableBusinessObject> save(List businessObjects) throws DataAccessException {
250     	if ( LOG.isDebugEnabled() ) {
251     		LOG.debug( "About to persist the following BOs:" );
252     		for ( Object bo : businessObjects ) {
253     			LOG.debug( "   --->" + bo );
254     		}
255     	}
256         for (Iterator i = businessObjects.iterator(); i.hasNext();) {
257             Object bo = i.next();
258             getPersistenceBrokerTemplate().store(bo);
259         }
260         return businessObjects;
261     }
262 
263 
264     /**
265      * Deletes the business object passed in.
266      *
267      * @param bo
268      * @throws DataAccessException
269      * @see org.kuali.rice.krad.dao.BusinessObjectDao#delete(org.kuali.rice.krad.bo.PersistableBusinessObject)
270      */
271     public void delete(PersistableBusinessObject bo) {
272         getPersistenceBrokerTemplate().delete(bo);
273     }
274 
275     /**
276      * @see org.kuali.rice.krad.dao.BusinessObjectDao#delete(java.util.List)
277      */
278     public void delete(List<? extends PersistableBusinessObject> boList) {
279         for (PersistableBusinessObject bo : boList) {
280             getPersistenceBrokerTemplate().delete(bo);
281         }
282     }
283 
284 
285     /**
286      * @see org.kuali.rice.krad.dao.BusinessObjectDao#deleteMatching(java.lang.Class, java.util.Map)
287      */
288     public void deleteMatching(Class clazz, Map<String, ?> fieldValues) {
289         Criteria criteria = buildCriteria(fieldValues);
290 
291         getPersistenceBrokerTemplate().deleteByQuery(QueryFactory.newQuery(clazz, criteria));
292 
293         // An ojb delete by query doesn't update the cache so we need to clear the cache for everything to work property.
294         // don't believe me? Read the source code to OJB
295         getPersistenceBrokerTemplate().clearCache();
296     }
297 
298     /**
299      * @see org.kuali.rice.krad.dao.BusinessObjectDao#retrieve(org.kuali.rice.krad.bo.PersistableBusinessObject)
300      */
301     public PersistableBusinessObject retrieve(PersistableBusinessObject object) {
302         return (PersistableBusinessObject) getPersistenceBrokerTemplate().getObjectByQuery(QueryFactory.newQueryByIdentity(object));
303     }
304 
305     /**
306 	 * OJB does not support this method
307 	 * @see org.kuali.rice.krad.dao.BusinessObjectDao#findByPrimaryKey(java.lang.Class, java.lang.Object)
308 	 */
309 	public  <T extends BusinessObject> T findByPrimaryKeyUsingKeyObject(Class<T> clazz, Object pkObject) {
310 		throw new UnsupportedOperationException("OJB does not support this option");
311 	}
312 
313 	/**
314 	 * No need to do anything - avoid saving and OJB will "manage read only"
315 	 * @see org.kuali.rice.krad.dao.BusinessObjectDao#manageReadOnly(org.kuali.rice.krad.bo.PersistableBusinessObject)
316 	 */
317 	public PersistableBusinessObject manageReadOnly(PersistableBusinessObject bo) {
318 		return bo;
319 	}
320 
321 	/**
322      * This method will build out criteria in the key-value paradigm (attribute-value).
323      *
324      * @param fieldValues
325      * @return
326      */
327     private Criteria buildCriteria(Map<String, ?> fieldValues) {
328         Criteria criteria = new Criteria();
329         for (Iterator i = fieldValues.entrySet().iterator(); i.hasNext();) {
330             Map.Entry<String, Object> e = (Map.Entry<String, Object>) i.next();
331 
332             String key = e.getKey();
333             Object value = e.getValue();
334             if (value instanceof Collection) {
335                 criteria.addIn(key, (Collection) value);
336             } else if(value instanceof String && ((String)value).contains("*")){
337                value = ((String)value).replace("*","%");
338                criteria.addLike(key,value);
339             }
340             else {
341                 criteria.addEqualTo(key, value);
342             }
343         }
344 
345         return criteria;
346     }
347 
348     
349     private <T extends BusinessObject> Criteria buildCriteria(Class<T> clazz, Object primaryKey) {
350         Map<String, Object> fieldValues = new HashMap<String, Object>();
351         List<String> fieldNames = getPersistenceStructureService().getPrimaryKeys(clazz);
352 
353         //create map of values
354         for (String fieldName : fieldNames) {
355             Object fieldValue;
356 
357             try {
358                 fieldValue = primaryKey.getClass().getMethod("get" + StringUtils.capitalize(fieldName)).invoke(primaryKey);
359                 fieldValues.put(fieldName, fieldValue);
360             } catch (IllegalArgumentException e) {
361                 e.printStackTrace();
362             } catch (IllegalAccessException e) {
363                 e.printStackTrace();
364             } catch (SecurityException e) {
365                 e.printStackTrace();
366             } catch (InvocationTargetException e) {
367                 e.printStackTrace();
368             } catch (NoSuchMethodException e) {
369                 e.printStackTrace();
370             }
371         }
372         return this.buildCriteria(fieldValues);
373     }
374     
375     /**
376      * Builds a Criteria object for active field set to true
377      * @return Criteria
378      */
379     private Criteria buildActiveCriteria(){
380         Criteria criteria = new Criteria();
381         criteria.addEqualTo(KRADPropertyConstants.ACTIVE, true);
382 
383         return criteria;
384     }
385 
386     /**
387      * Builds a Criteria object for active field set to true
388      * @return Criteria
389      */
390     private Criteria buildInactiveCriteria(){
391         Criteria criteria = new Criteria();
392         criteria.addEqualTo(KRADPropertyConstants.ACTIVE, false);
393 
394         return criteria;
395     }
396 
397     /**
398      * This method will build out criteria in the key-value paradigm (attribute-value).
399      *
400      * @param negativeFieldValues
401      * @return
402      */
403     private Criteria buildNegativeCriteria(Map<String, ?> negativeFieldValues) {
404         Criteria criteria = new Criteria();
405         for (Iterator i = negativeFieldValues.entrySet().iterator(); i.hasNext();) {
406             Map.Entry<String, Object> e = (Map.Entry<String, Object>) i.next();
407 
408             String key = e.getKey();
409             Object value = e.getValue();
410             if (value instanceof Collection) {
411                 criteria.addNotIn(key, (Collection) value);
412             }
413             else {
414                 criteria.addNotEqualTo(key, value);
415             }
416         }
417 
418         return criteria;
419     }
420 
421     /**
422      * Gets the persistenceStructureService attribute.
423      * @return Returns the persistenceStructureService.
424      */
425     protected PersistenceStructureService getPersistenceStructureService() {
426         return persistenceStructureService;
427     }
428 
429     /**
430      * Sets the persistenceStructureService attribute value.
431      * @param persistenceStructureService The persistenceStructureService to set.
432      */
433     public void setPersistenceStructureService(PersistenceStructureService persistenceStructureService) {
434         this.persistenceStructureService = persistenceStructureService;
435     }
436 
437 }