001 /*
002 * Copyright 2011 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.kfs.module.purap.dataaccess.impl;
017
018 import java.util.Map;
019
020 import org.apache.commons.lang.StringUtils;
021 import org.apache.ojb.broker.PersistenceBroker;
022 import org.apache.ojb.broker.metadata.ClassDescriptor;
023 import org.apache.ojb.broker.metadata.CollectionDescriptor;
024 import org.apache.ojb.broker.metadata.FieldDescriptor;
025 import org.apache.ojb.broker.platforms.PlatformMySQLImpl;
026 import org.apache.ojb.broker.query.Query;
027 import org.apache.ojb.broker.query.QueryByCriteria;
028
029 /**
030 * This class improves the default order by in OJB by enforcing consistency between Oracle and MySQLs handling of Null values in a
031 * column. Oracle by default sorts nulls last while MySQL does nulls first (i.e. 1,2,3,null MySQL:null,1,2,3; Oracle:1,2,3,null To
032 * get Mysql to sort correctly we need to negate the field that is being Sorted on (i.e. ORDER BY -column DESC = 1,2,3,null while
033 * ORDER BY column DESC = 3,2,1,null) the oracle default for ORDER BY is "NULLS LAST" which the above MySQL tweak should make it
034 * like. This could be improved to pass in nullsFirst to decide which way to display but that would be beyond what ojb currently
035 * does
036 */
037 public class PurapItemQueryCustomizer extends KualiQueryCustomizerDefaultImpl {
038 protected static final String MYSQL_NEGATION = "-";
039 public final static String ORDER_BY_FIELD = "orderByField.";
040 public final static String ASCENDING = "ASC";
041 public final static String DESCENDING = "DESC";
042
043 /**
044 * In addition to what the referenced method does, this also fixes a mysql order by issue (see class comments)
045 * @see org.apache.ojb.broker.accesslayer.QueryCustomizerDefaultImpl#customizeQuery(java.lang.Object,
046 * org.apache.ojb.broker.PersistenceBroker, org.apache.ojb.broker.metadata.CollectionDescriptor,
047 * org.apache.ojb.broker.query.QueryByCriteria)
048 */
049 @Override
050 public Query customizeQuery(Object anObject, PersistenceBroker broker, CollectionDescriptor cod, QueryByCriteria query) {
051 boolean platformMySQL = broker.serviceSqlGenerator().getPlatform() instanceof PlatformMySQLImpl;
052
053 Map<String, String> attributes = getAttributes();
054 for (String attributeName : attributes.keySet()) {
055 if (!attributeName.startsWith(ORDER_BY_FIELD)) {
056 continue;
057 }
058
059 String fieldName = attributeName.substring(ORDER_BY_FIELD.length());
060 ClassDescriptor itemClassDescriptor = broker.getClassDescriptor(cod.getItemClass());
061 FieldDescriptor orderByFieldDescriptior = itemClassDescriptor.getFieldDescriptorByName(fieldName);
062
063 // the column to sort on derived from the property name
064 String orderByColumnName = orderByFieldDescriptior.getColumnName();
065
066 // ascending or descending
067 String fieldValue = attributes.get(attributeName);
068 boolean ascending = (StringUtils.equals(fieldValue, ASCENDING));
069 // throw an error if not ascending or descending
070 if (!ascending && StringUtils.equals(fieldValue, DESCENDING)) {
071 throw new RuntimeException("neither ASC nor DESC was specified in ojb file for " + fieldName);
072 }
073
074 if (platformMySQL) {
075 // by negating the column name in MySQL we can get nulls last (ascending or descending)
076 String mysqlPrefix = (ascending) ? MYSQL_NEGATION : "";
077 query.addOrderBy(mysqlPrefix + orderByColumnName, false);
078 }
079 else {
080 query.addOrderBy(orderByColumnName, ascending);
081 }
082 }
083 return query;
084 }
085 }