001/** 002 * Copyright 2005-2017 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.kim.api.common.history; 017 018import org.joda.time.DateTime; 019import org.kuali.rice.core.api.criteria.OrderByField; 020import org.kuali.rice.core.api.criteria.OrderDirection; 021import org.kuali.rice.core.api.criteria.Predicate; 022import org.kuali.rice.core.api.criteria.QueryByCriteria; 023 024import java.sql.Timestamp; 025import java.util.ArrayList; 026import java.util.List; 027 028import static org.kuali.rice.core.api.criteria.PredicateFactory.*; 029 030public final class HistoryQueryUtils { 031 032 private HistoryQueryUtils() { 033 throw new UnsupportedOperationException(); 034 } 035 036 public static QueryByCriteria historyQuery(String id, DateTime asOfDate) { 037 Timestamp asOfTimestamp = new Timestamp(asOfDate.getMillis()); 038 Predicate predicate = 039 and( 040 equal("id", id), 041 and( 042 or(isNull("activeFromDateValue"), lessThanOrEqual("activeFromDateValue", asOfTimestamp)), 043 or(isNull("activeToDateValue"), greaterThan("activeToDateValue", asOfTimestamp)) 044 ) 045 ); 046 047 QueryByCriteria.Builder criteria = QueryByCriteria.Builder.create(); 048 criteria.setMaxResults(Integer.valueOf(1)); 049 criteria.setPredicates(predicate); 050 List<OrderByField> orderByFields = new ArrayList<OrderByField>(); 051 orderByFields.add(OrderByField.Builder.create("activeFromDateValue", OrderDirection.DESCENDING).build()); 052 orderByFields.add(OrderByField.Builder.create("historyId", OrderDirection.DESCENDING).build()); 053 criteria.setOrderByFields(orderByFields); 054 055 return criteria.build(); 056 } 057 058 public static QueryByCriteria futureRecordQuery(String id, DateTime asOfDate) { 059 Timestamp asOfTimestamp = new Timestamp(asOfDate.getMillis()); 060 Predicate predicate = 061 and( 062 equal("id", id), 063 or(isNull("activeFromDateValue"), greaterThan("activeFromDateValue", asOfTimestamp)) 064 ); 065 066 QueryByCriteria.Builder criteria = QueryByCriteria.Builder.create(); 067 criteria.setMaxResults(Integer.valueOf(1)); 068 criteria.setPredicates(predicate); 069 List<OrderByField> orderByFields = new ArrayList<OrderByField>(); 070 orderByFields.add(OrderByField.Builder.create("activeFromDateValue", OrderDirection.DESCENDING).build()); 071 orderByFields.add(OrderByField.Builder.create("historyId", OrderDirection.DESCENDING).build()); 072 criteria.setOrderByFields(orderByFields); 073 074 return criteria.build(); 075 } 076 077 public static Predicate between(String startField,String endField, DateTime asOfDate) { 078 // the precision of this check should be to the second, not milliseconds, so we want to chop off any 079 // milliseconds and do a ceiling of our seconds. Sometimes changes are made in near real time after a record 080 // becomes activated or inactivated so we want to have the best result possible if they are still within the 081 // same second, so we essentially always round up to ensure that this check will never fail in high throughput 082 // environments 083 asOfDate = asOfDate.secondOfDay().roundCeilingCopy(); 084 return 085 and( 086 or(isNull(startField), lessThanOrEqual(startField, asOfDate.toDate())), 087 or(isNull(endField), greaterThan(endField, asOfDate.toDate())) 088 ); 089 } 090}