001/** 002 * Copyright 2005-2015 The Kuali Foundation 003 * 004 * Licensed under the Educational Community License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.opensource.org/licenses/ecl2.php 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016package org.kuali.rice.krad.dao.impl; 017 018import org.apache.commons.lang.StringUtils; 019import org.apache.ojb.broker.query.Criteria; 020import org.apache.ojb.broker.query.QueryByCriteria; 021import org.apache.ojb.broker.query.QueryFactory; 022import org.kuali.rice.core.framework.persistence.ojb.dao.PlatformAwareDaoBaseOjb; 023import org.kuali.rice.krad.bo.BusinessObject; 024import org.kuali.rice.krad.bo.PersistableBusinessObject; 025import org.kuali.rice.krad.dao.BusinessObjectDao; 026import org.kuali.rice.krad.service.KRADServiceLocatorInternal; 027import org.kuali.rice.krad.service.KRADServiceLocatorWeb; 028import org.kuali.rice.krad.service.PersistenceStructureService; 029import org.kuali.rice.krad.util.KRADPropertyConstants; 030import org.kuali.rice.krad.util.ObjectUtils; 031import org.kuali.rice.krad.util.OjbCollectionAware; 032import org.springframework.dao.DataAccessException; 033 034import java.lang.reflect.InvocationTargetException; 035import java.util.Collection; 036import java.util.HashMap; 037import java.util.Iterator; 038import java.util.List; 039import java.util.Map; 040import java.util.Set; 041 042/** 043 * This class is the OJB implementation of the BusinessObjectDao interface and should be used for generic business object unit 044 * tests. 045 */ 046public class BusinessObjectDaoOjb extends PlatformAwareDaoBaseOjb implements BusinessObjectDao, OjbCollectionAware { 047 private static final org.apache.log4j.Logger LOG = org.apache.log4j.Logger.getLogger(BusinessObjectDaoOjb.class); 048 049 private PersistenceStructureService persistenceStructureService; 050 051 /** 052 * This constructs a {@link BusinessObjectDaoOjb} 053 */ 054 public BusinessObjectDaoOjb(PersistenceStructureService persistenceStructureService) { 055 this.persistenceStructureService = persistenceStructureService; 056 } 057 058 /** 059 * @see org.kuali.rice.krad.dao.BusinessObjectDao#findBySinglePrimaryKey(java.lang.Class, java.lang.Object) 060 */ 061 public <T extends BusinessObject> T findBySinglePrimaryKey(Class<T> clazz, Object primaryKey) { 062 if (primaryKey.getClass().getName().startsWith("java.lang.") 063 || primaryKey.getClass().getName().startsWith("java.sql.") 064 || primaryKey.getClass().getName().startsWith("java.math.") 065 || primaryKey.getClass().getName().startsWith("java.util.")) { 066 try { 067 return (T) getPersistenceBrokerTemplate().getObjectById(clazz, primaryKey); 068 } catch ( DataAccessException ex ) { 069 // it doesn't exist, just return null 070 return null; 071 } 072 } else { 073 Criteria criteria = buildCriteria(clazz, primaryKey); 074 075 return (T) getPersistenceBrokerTemplate().getObjectByQuery(QueryFactory.newQuery(clazz, criteria)); 076 } 077 } 078 079 /** 080 * @see org.kuali.rice.krad.dao.BusinessObjectDao#findByPrimaryKey(java.lang.Class, java.util.Map) 081 */ 082 public <T extends BusinessObject> T findByPrimaryKey(Class<T> clazz, Map<String, ?> primaryKeys) { 083 Criteria criteria = buildCriteria(primaryKeys); 084 085 return (T) getPersistenceBrokerTemplate().getObjectByQuery(QueryFactory.newQuery(clazz, criteria)); 086 } 087 088 /** 089 * Retrieves all of the records for a given class name. 090 * 091 * @param clazz - the name of the object being used, either KualiCodeBase or a subclass 092 * @return Collection 093 * @see org.kuali.rice.krad.dao.BusinessObjectDao#findAll(java.lang.Class) 094 */ 095 public <T extends BusinessObject> Collection<T> findAll(Class<T> clazz) { 096 return (Collection<T>)getPersistenceBrokerTemplate().getCollectionByQuery(QueryFactory.newQuery(clazz, (Criteria) null)); 097 } 098 099 /** 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}