001    /**
002     * Copyright 2005-2012 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     */
016    package org.kuali.rice.core.impl.persistence.dao;
017    
018    import org.apache.log4j.Logger;
019    import org.apache.ojb.broker.query.Criteria;
020    import org.apache.ojb.broker.query.Query;
021    import org.apache.ojb.broker.query.QueryByCriteria;
022    import org.apache.ojb.broker.query.QueryFactory;
023    import org.kuali.rice.core.api.config.property.Config;
024    import org.kuali.rice.core.api.config.property.ConfigContext;
025    import org.kuali.rice.core.api.util.RiceConstants;
026    import org.kuali.rice.core.framework.persistence.dao.GenericDao;
027    import org.kuali.rice.core.framework.persistence.ojb.SuffixableQueryByCriteria;
028    import org.kuali.rice.core.framework.persistence.platform.DatabasePlatform;
029    import org.springframework.dao.DataAccessException;
030    import org.springmodules.orm.ojb.support.PersistenceBrokerDaoSupport;
031    
032    import java.util.Collection;
033    import java.util.Iterator;
034    import java.util.List;
035    import java.util.Map;
036    
037    /**
038     * This class is the OJB implementation of the GenericDao interface. This
039     * class was adapted from the Kuali Nervous System
040     * (org.kuali.rice.krad.dao.impl.GenericDaoOjb).
041     * 
042     * @author Kuali Rice Team (rice.collab@kuali.org)
043     */
044    public class GenericDaoOjb extends PersistenceBrokerDaoSupport implements GenericDao {
045        private static final Logger LOG = Logger.getLogger(GenericDaoOjb.class);
046    
047        private boolean useSelectForUpdate = true;
048    
049        /**
050         * @param selectForUpdate whether to use select for update to implement pessimistic locking (testing/debugging purposes only)
051         */
052        public void setUseSelectForUpdate(boolean useSelectForUpdate) {
053            this.useSelectForUpdate = useSelectForUpdate;
054        }
055    
056        /**
057         * @see org.kuali.rice.core.framework.persistence.dao.GenericDao#findById(Class, Object)
058         */
059        public Object findById(Class clazz, Object id) {
060            return getPersistenceBrokerTemplate().getObjectById(clazz, id);
061        }
062    
063        /**
064         * @see org.kuali.rice.core.framework.persistence.dao.GenericDao#findByPrimaryKey(java.lang.Class, java.util.Map)
065         */
066        public Object findByPrimaryKey(Class clazz, Map primaryKeys) {
067            Criteria criteria = buildCriteria(primaryKeys);
068    
069            return getPersistenceBrokerTemplate().getObjectByQuery(QueryFactory.newQuery(clazz, criteria));
070        }
071    
072        /**
073         * @see org.kuali.rice.core.framework.persistence.dao.GenericDao#findByUniqueKey(java.lang.Class, java.util.Map)
074         */
075        public Object findByUniqueKey(Class clazz, Map uniqueKeys) {
076            Criteria criteria = buildCriteria(uniqueKeys);
077    
078            return getPersistenceBrokerTemplate().getObjectByQuery(QueryFactory.newQuery(clazz, criteria));
079        }
080    
081        /**
082         * @see org.kuali.rice.core.framework.persistence.dao.GenericDao#findAll(java.lang.Class)
083         */
084        public Collection findAll(Class clazz) {
085            return getPersistenceBrokerTemplate().getCollectionByQuery(QueryFactory.newQuery(clazz, (Criteria) null));
086        }
087    
088        /**
089         * @see org.kuali.rice.core.framework.persistence.dao.GenericDao#findAllOrderBy(java.lang.Class, java.lang.String, boolean)
090         */
091        public Collection findAllOrderBy(Class clazz, String sortField,
092                boolean sortAscending) {
093            QueryByCriteria queryByCriteria = new QueryByCriteria(clazz, (Criteria) null);
094    
095            if (sortAscending) {
096                queryByCriteria.addOrderByAscending(sortField);
097            } else {
098                queryByCriteria.addOrderByDescending(sortField);
099            }
100    
101            return getPersistenceBrokerTemplate().getCollectionByQuery(
102                    queryByCriteria);
103        }
104    
105        /**
106         * @see org.kuali.rice.core.framework.persistence.dao.GenericDao#findMatching(java.lang.Class, java.util.Map)
107         */
108        public Collection findMatching(Class clazz, Map fieldValues) {
109            Criteria criteria = buildCriteria(fieldValues);
110    
111            return getPersistenceBrokerTemplate().getCollectionByQuery(QueryFactory.newQuery(clazz, criteria));
112        }
113    
114        /**
115         * @see org.kuali.rice.core.framework.persistence.dao.GenericDao#countMatching(java.lang.Class, java.util.Map)
116         */
117        public int countMatching(Class clazz, Map fieldValues) {
118            Criteria criteria = buildCriteria(fieldValues);
119    
120            return getPersistenceBrokerTemplate().getCount(QueryFactory.newQuery(clazz, criteria));
121        }
122    
123        /**
124         * @see org.kuali.rice.core.framework.persistence.dao.GenericDao#countMatching(java.lang.Class, java.util.Map, java.util.Map)
125         */
126        public int countMatching(Class clazz, Map positiveFieldValues,
127                Map negativeFieldValues) {
128            Criteria criteria = buildCriteria(positiveFieldValues);
129            Criteria negativeCriteria = buildNegativeCriteria(negativeFieldValues);
130            criteria.addAndCriteria(negativeCriteria);
131            return getPersistenceBrokerTemplate().getCount(QueryFactory.newQuery(clazz, criteria));
132        }
133    
134        /**
135         * @see org.kuali.rice.core.framework.persistence.dao.GenericDao#findMatchingOrderBy(java.lang.Class, java.util.Map, java.lang.String, boolean)
136         */
137        public Collection findMatchingOrderBy(Class clazz, Map fieldValues,
138                String sortField, boolean sortAscending) {
139            Criteria criteria = buildCriteria(fieldValues);
140            QueryByCriteria queryByCriteria = new QueryByCriteria(clazz, criteria);
141    
142            if (sortAscending) {
143                queryByCriteria.addOrderByAscending(sortField);
144            } else {
145                queryByCriteria.addOrderByDescending(sortField);
146            }
147    
148            return getPersistenceBrokerTemplate().getCollectionByQuery(queryByCriteria);
149        }
150    
151        /**
152         * @see org.kuali.rice.core.framework.persistence.dao.GenericDao#save(java.lang.Object)
153         */
154        public void save(Object bo) throws DataAccessException {
155            getPersistenceBrokerTemplate().store(bo);
156        }
157    
158        /**
159         * @see org.kuali.rice.core.framework.persistence.dao.GenericDao#save(java.util.List)
160         */
161        public void save(List businessObjects) throws DataAccessException {
162            for (Iterator i = businessObjects.iterator(); i.hasNext();) {
163                Object bo = i.next();
164                getPersistenceBrokerTemplate().store(bo);
165            }
166        }
167    
168        /**
169         * @see org.kuali.rice.core.framework.persistence.dao.GenericDao#delete(java.lang.Object)
170         */
171        public void delete(Object bo) {
172            getPersistenceBrokerTemplate().delete(bo);
173        }
174    
175        /**
176         * @see org.kuali.rice.core.framework.persistence.dao.GenericDao#delete(java.util.List)
177         */
178        public void delete(List<Object> boList) {
179            for (Object bo : boList) {
180                getPersistenceBrokerTemplate().delete(bo);
181            }
182        }
183    
184        /**
185         * @see org.kuali.rice.core.framework.persistence.dao.GenericDao#deleteMatching(java.lang.Class, java.util.Map)
186         */
187        public void deleteMatching(Class clazz, Map fieldValues) {
188            Criteria criteria = buildCriteria(fieldValues);
189    
190            getPersistenceBrokerTemplate().deleteByQuery(QueryFactory.newQuery(clazz, criteria));
191    
192            // An ojb delete by query doesn't update the cache so we need to clear
193            // the cache for everything to work property.
194            // don't believe me? Read the source code to OJB
195            getPersistenceBrokerTemplate().clearCache();
196        }
197    
198        /**
199         * @see org.kuali.rice.core.framework.persistence.dao.GenericDao#retrieve(java.lang.Object)
200         */
201        public Object retrieve(Object object) {
202            return getPersistenceBrokerTemplate().getObjectByQuery(QueryFactory.newQueryByIdentity(object));
203        }
204    
205        /**
206         * @see org.kuali.rice.core.framework.persistence.dao.GenericDao#findMatchingByExample(java.lang.Object)
207         */
208        public Collection findMatchingByExample(Object object) {
209            return getPersistenceBrokerTemplate().getCollectionByQuery(QueryFactory.newQueryByExample(object));
210        }
211    
212        /**
213         * @see org.kuali.rice.core.framework.persistence.dao.GenericDao#findMatching(java.lang.Class, org.apache.ojb.broker.query.Criteria)
214         */
215        public Collection findMatching(Class clazz, Criteria criteria) {
216            return findMatching(clazz, criteria, false, RiceConstants.NO_WAIT);
217            /*return getPersistenceBrokerTemplate().getCollectionByQuery(
218            QueryFactory.newQuery(clazz, criteria));*/
219        }
220    
221        /**
222         * @see org.kuali.rice.core.framework.persistence.dao.GenericDao#findMatching(Class, Criteria, boolean)
223         */
224        public Collection findMatching(Class clazz, Criteria criteria, boolean selectForUpdate, long wait) {
225            Query query;
226            if (selectForUpdate && !useSelectForUpdate) {
227                LOG.warn("Pessimistic locking was requested but select for update is disabled");
228            }
229            if (selectForUpdate && useSelectForUpdate) {
230                SuffixableQueryByCriteria q = new SuffixableQueryByCriteria(clazz, criteria);
231                // XXX: hax
232                Config config = ConfigContext.getCurrentContextConfig();
233                DatabasePlatform platform = null;
234                try {
235                    platform = (DatabasePlatform) Class.forName(config.getProperty(Config.DATASOURCE_PLATFORM)).newInstance();
236                } catch (Exception e) {
237                    throw new RuntimeException(e.getMessage(), e);
238                }
239                q.setQuerySuffix(" " + platform.getSelectForUpdateSuffix(wait));
240                query = q;            
241            } else {
242                query = QueryFactory.newQuery(clazz, criteria);
243            }
244            return getPersistenceBrokerTemplate().getCollectionByQuery(query);
245    
246        }
247    
248        /**
249         * This method will build out criteria in the key-value paradigm
250         * (attribute-value).
251         * 
252         * @param fieldValues
253         * @return
254         */
255        private Criteria buildCriteria(Map fieldValues) {
256            Criteria criteria = new Criteria();
257            for (Iterator i = fieldValues.entrySet().iterator(); i.hasNext();) {
258                Map.Entry e = (Map.Entry) i.next();
259    
260                String key = (String) e.getKey();
261                Object value = e.getValue();
262                if (value instanceof Collection) {
263                    criteria.addIn(key, (Collection) value);
264                } else {
265                    criteria.addEqualTo(key, value);
266                }
267            }
268    
269            return criteria;
270        }
271    
272        /**
273         * This method will build out criteria in the key-value paradigm
274         * (attribute-value).
275         * 
276         * @param fieldValues
277         * @return
278         */
279        private Criteria buildNegativeCriteria(Map negativeFieldValues) {
280            Criteria criteria = new Criteria();
281            for (Iterator i = negativeFieldValues.entrySet().iterator(); i.hasNext();) {
282                Map.Entry e = (Map.Entry) i.next();
283    
284                String key = (String) e.getKey();
285                Object value = e.getValue();
286                if (value instanceof Collection) {
287                    criteria.addNotIn(key, (Collection) value);
288                } else {
289                    criteria.addNotEqualTo(key, value);
290                }
291            }
292    
293            return criteria;
294        }
295    
296        /**
297         * This overridden method ...
298         *
299         * @see org.kuali.rice.core.framework.persistence.dao.GenericDao#findMatching(java.lang.Class, org.kuali.rice.core.framework.persistence.jpa.criteria.Criteria)
300         */
301        @Override
302        public Collection findMatching(Class clazz,
303                org.kuali.rice.core.framework.persistence.jpa.criteria.Criteria criteria) {
304            // TODO g1zhang - THIS METHOD NEEDS JAVADOCS
305            return null;
306        }
307    
308        /**
309         * This overridden method ...
310         *
311         * @see org.kuali.rice.core.framework.persistence.dao.GenericDao#findMatching(java.lang.Class, org.kuali.rice.core.framework.persistence.jpa.criteria.Criteria, boolean, long)
312         */
313        @Override
314        public Collection findMatching(Class clazz,
315                org.kuali.rice.core.framework.persistence.jpa.criteria.Criteria criteria,
316                boolean selectForUpdate, long wait) {
317            // TODO g1zhang - THIS METHOD NEEDS JAVADOCS
318            return null;
319        }
320    
321    
322        /**
323         * This overridden method ...
324         *
325         * @see org.kuali.rice.core.framework.persistence.dao.GenericDao#findMatching(java.lang.Class, java.util.Map, boolean, long)
326         */
327        @Override
328        public Collection findMatching(Class clazz, Map criteria,
329                boolean selectForUpdate, long wait) {
330    
331            LOG.info("*******************************calling GenericDaoOjb.findMatching(Class clazz, Map criteria, boolean selectForUpdate, long wait)");
332            Criteria c = buildCriteria(criteria);
333            return findMatching(clazz, c, selectForUpdate, wait);
334        }
335    }