CriteriaBuilder.java
package com.tradecloud.domain.infrastructure.persistence;
import com.tradecloud.domain.base.utils.DateRange;
import com.tradecloud.domain.base.utils.DateUtils;
import com.tradecloud.domain.search.SearchParams;
import org.hibernate.FetchMode;
import org.hibernate.criterion.DetachedCriteria;
import org.hibernate.criterion.MatchMode;
import org.hibernate.criterion.Order;
import org.hibernate.criterion.Restrictions;
import org.joda.time.ReadablePartial;
import javax.persistence.criteria.Join;
import javax.persistence.criteria.JoinType;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;
import java.util.Collections;
import java.util.Date;
import java.util.List;
/**
* TODO. This needs to move to repository. There is a temp CriteriaBuilder2
* there in the mean time.
*
* @author jon
*/
public class CriteriaBuilder {
private final static String ACTIVE = "active";
public static Order createOrdering(String orderBy, boolean ascending) {
return ascending ? Order.asc(orderBy) : Order.desc(orderBy);
}
public static void applySearchParams(DetachedCriteria criteria, SearchParams searchParams) {
// Can be null in the case of tests, where they don't care
if (searchParams != null) {
if (searchParams.getOrderBy() != null) {
criteria.addOrder(createOrdering(searchParams.getOrderBy(), searchParams.isDescending()));
}
// Check if we are only searching for active entities
if (searchParams.isActiveOnly()) {
criteria.add(Restrictions.eq(ACTIVE, true));
}
}
}
public static void addDateRangeCriteria(DetachedCriteria criteria, String dateColumn, ReadablePartial dateFrom, ReadablePartial dateTo) {
if (dateFrom != null) {
criteria.add(Restrictions.ge(dateColumn, dateFrom));
}
if (dateTo != null) {
criteria.add(Restrictions.le(dateColumn, dateTo));
}
}
public static void addDateRangeCriteria(DetachedCriteria criteria, String dateColumn, Date dateFrom, Date dateTo) {
addDateRangeCriteria(criteria, dateColumn, dateFrom, dateTo, false);
}
public static void addDateRangeCriteria(DetachedCriteria criteria, String dateColumn, Date dateFrom, Date dateTo, boolean outerBounds) {
if (dateFrom != null || dateTo != null) {
criteria.add(Restrictions.isNotNull(dateColumn));
if (dateFrom != null) {
criteria.add(Restrictions.ge(dateColumn, outerBounds ? DateUtils.getStartOfDay(dateFrom) : dateFrom));
}
if (dateTo != null) {
criteria.add(Restrictions.le(dateColumn, outerBounds ? DateUtils.getEndOfDay(dateTo) : dateTo));
}
}
}
public static void addDateRangeCriteria2(javax.persistence.criteria.CriteriaBuilder cb, List<Predicate> predicates, Root root, String dateColumn,
Date dateFrom, Date dateTo) {
addDateRangeCriteria2(cb, predicates, root, dateColumn, dateFrom, dateTo, false);
}
public static void addDateRangeCriteria2(javax.persistence.criteria.CriteriaBuilder cb, List<Predicate> predicates, Root root, String dateColumn,
Date dateFrom, Date dateTo, boolean outerBounds) {
if (dateFrom != null || dateTo != null) {
// Ensure the date column is not null
Predicate isNotNull = cb.isNotNull(root.get(dateColumn));
// Create predicates for date range
Predicate dateFromPredicate = null;
Predicate dateToPredicate = null;
if (dateFrom != null) {
Date startDate = outerBounds ? DateUtils.getStartOfDay(dateFrom) : dateFrom;
dateFromPredicate = cb.greaterThanOrEqualTo(root.get(dateColumn), startDate);
}
if (dateTo != null) {
Date endDate = outerBounds ? DateUtils.getEndOfDay(dateTo) : dateTo;
dateToPredicate = cb.lessThanOrEqualTo(root.get(dateColumn), endDate);
}
// Combine the predicates using AND logic
if (dateFromPredicate != null && dateToPredicate != null) {
predicates.add(cb.and(isNotNull, dateFromPredicate, dateToPredicate));
} else if (dateFromPredicate != null) {
predicates.add(cb.and(isNotNull, dateFromPredicate));
} else if (dateToPredicate != null) {
predicates.add(cb.and(isNotNull, dateToPredicate));
}
}
}
public static void addDateRangeCriteria(DetachedCriteria criteria, String dateColumn, DateRange dateRange) {
addDateRangeCriteria(criteria, dateColumn, dateRange, false);
/*
if (dateRange != null) {
if (dateRange.getFrom() != null) {
criteria.add(Restrictions.ge(dateColumn, dateRange.getFrom()));
}
if (dateRange.getTo() != null) {
criteria.add(Restrictions.le(dateColumn, dateRange.getTo()));
}
}
*/
}
public static void addDateRangeCriteria(DetachedCriteria criteria, String dateColumn, DateRange dateRange, boolean outerBounds) {
if (dateRange != null) {
addDateRangeCriteria(criteria, dateColumn, dateRange.getFrom(), dateRange.getTo(), outerBounds);
/*
if (dateRange.getFrom() != null) {
criteria.add(Restrictions.ge(dateColumn, outerBounds ? dateRange.getFromAsStartOfDay() : dateRange.getFrom()));
}
if (dateRange.getTo() != null) {
criteria.add(Restrictions.le(dateColumn, outerBounds ? dateRange.getToAsEndOfDay() : dateRange.getTo()));
}
*/
}
}
public static void addDateLessOrEqualRangeCriteria(DetachedCriteria criteria, String dateColumn, Date date) {
if (date != null) {
criteria.add(Restrictions.le(dateColumn, date));
}
}
public static void addDateGreaterOrEqualRangeCriteria(DetachedCriteria criteria, String dateColumn, Date date) {
if (date != null) {
criteria.add(Restrictions.ge(dateColumn, date));
}
}
public static void addEventDateRangeSearchCriteria(DetachedCriteria criteria, DateRange dateRange, Enum eventType) {
addEventDateRangeSearchCriteria(criteria, dateRange.getFrom(), dateRange.getTo(), eventType);
}
public static void addEventDateRangeSearchCriteria(DetachedCriteria criteria, Date fromDate, Date toDate, Enum eventType) {
if (fromDate != null && toDate != null) {
criteria.createAlias("events", "events");
criteria.setFetchMode("events", FetchMode.SELECT);
criteria.add(Restrictions.like("events.eventType", eventType));
criteria.add(Restrictions.ge("events.createDateTime", DateUtils.getStartOfDay(fromDate)));
criteria.add(Restrictions.le("events.createDateTime", DateUtils.getEndOfDay(toDate)));
}
}
public static void addEventDateRangeSearchCriteria2(javax.persistence.criteria.CriteriaBuilder cb, List<Predicate> predicates, Root root, DateRange dateRange,
Enum eventType) {
addEventDateRangeSearchCriteria2(cb, predicates, root, dateRange.getFrom(), dateRange.getTo(), eventType);
}
public static void addEventDateRangeSearchCriteria2(javax.persistence.criteria.CriteriaBuilder cb, List<Predicate> predicates, Root root, Date fromDate,
Date toDate, Enum eventType) {
if (fromDate != null && toDate != null) {
// Join the 'events' relationship
Join<Object, Object> eventsJoin = root.join("events", JoinType.INNER);
// Add predicates for event type, start and end dates
Predicate eventTypePredicate = cb.equal(eventsJoin.get("eventType"), eventType);
Predicate startDatePredicate = cb.greaterThanOrEqualTo(eventsJoin.get("createDateTime"), DateUtils.getStartOfDay(fromDate));
Predicate endDatePredicate = cb.lessThanOrEqualTo(eventsJoin.get("createDateTime"), DateUtils.getEndOfDay(toDate));
predicates.add(eventTypePredicate);
predicates.add(startDatePredicate);
predicates.add(endDatePredicate);
}
}
/**
* Adds a {@code Restrictions#like(String, Object)} to the {@code criteria}
* supplied only if the supplied {@code value} is not null.
*
* @param criteria The {@code DetachedCriteria} to apply the restriction to
* @param propertyName The name of the property the restriction is on
* @param value The value of the applied restriction (if not null)
*/
public static void addLikeRestriction(DetachedCriteria criteria, String propertyName, Object value) {
if (value != null) {
criteria.add(Restrictions.like(propertyName, value));
}
}
/**
* Adds a {@code Restrictions#ilike(String, Object)} to the {@code criteria}
* supplied only if the supplied {@code value} is not null.
*
* TODO. Use addiLikeAnyRestriction. That is what all the callers are
* currently doing by passing in %value% manually. Don't know how to easily
* test ELC to make sure I haven't broken anything though.
*
* @param criteria The {@code DetachedCriteria} to apply the restriction to
* @param propertyName The name of the property the restriction is on
* @param value The value of the applied restriction (if not null)
*/
@Deprecated
public static void addiLikeRestriction(DetachedCriteria criteria, String propertyName, Object value) {
if (value != null) {
criteria.add(Restrictions.ilike(propertyName, value));
}
}
public static void addiLikeAnyRestriction(DetachedCriteria criteria, String propertyName, String value) {
if (value != null) {
criteria.add(Restrictions.ilike(propertyName, value, MatchMode.ANYWHERE));
}
}
/**
* Adds a {@code Restrictions#eq(String, Object)} to the {@code criteria}
* supplied only if the supplied {@code value} is not null.
*
* @param criteria The {@code DetachedCriteria} to apply the restriction to
* @param propertyName The name of the property the restriction is on
* @param value The value of the applied restriction (if not null)
*/
public static void addEqRestriction(DetachedCriteria criteria, String propertyName, Object value) {
if (value != null) {
criteria.add(Restrictions.eq(propertyName, value));
}
}
public static void addNotEqRestriction(DetachedCriteria criteria, String propertyName, Object value) {
if (value != null) {
criteria.add(Restrictions.not(Restrictions.eq(propertyName, value)));
}
}
public static void addLeRestriction(DetachedCriteria criteria, String propertyName, Object value) {
if (value != null) {
criteria.add(Restrictions.le(propertyName, value));
}
}
public static void addNestedEqRestriction(DetachedCriteria criteria, String nestedProperty, String nestedPropertyName, Object value) {
if (value != null) {
criteria.createCriteria(nestedProperty).add(Restrictions.eq(nestedPropertyName, value));
}
}
public static void addEqOrNullRestriction(DetachedCriteria criteria, String propertyName, Object value) {
if (value != null) {
criteria.add(Restrictions.in(propertyName, new Object[]{value, null}));
}
}
/**
* Handle restrictions from the state parameter. The normal case is if
* nothing is selected, you return everything in an active state. In
* practice, the only inactive state is DELETED, but am being generic. If a
* state is selected, you return that.
*
* @param <X>
* @param criteria
* @param propertyName Should be 'state'
* @param value The state
* @param inactiveStates The inactive states. Using negative logic, because
* there is normally only one inactive.
*/
public static <X extends Enum> void addEqActiveStateRestriction(DetachedCriteria criteria, String propertyName, X value, X... inactiveStates) {
if (value != null) {
criteria.add(Restrictions.eq(propertyName, value));
} else {
criteria.add(Restrictions.not(Restrictions.in(propertyName, inactiveStates)));
}
}
/**
* Adds a {@code Restrictions#eqProperty(String, String)} to the
* {@code criteria}. This will be used in conjunction with a sql join. Both
* {@code propertyName} and {@code otherPropertyName} must contain values.
*
* @param criteria The {@code DetachedCriteria} to apply the restriction to
* @param propertyName The name of the first property the restriction is on
* @param otherPropertyName The name of the second property the restriction
* is on (on another table)
*/
public static void addEqPropertyRestriction(DetachedCriteria criteria, String propertyName, String otherPropertyName) {
if (propertyName != null && !propertyName.isEmpty() && otherPropertyName != null && !otherPropertyName.isEmpty()) {
criteria.add(Restrictions.eqProperty(propertyName, otherPropertyName));
}
}
public static void addExactMatch(DetachedCriteria criteria, String field, Object value) {
if (value != null) {
criteria.add(Restrictions.eq(field, value));
} else {
criteria.add(Restrictions.isNull(field));
}
}
public static void addDisjunction(DetachedCriteria criteria, String field, Object value) {
List<Object> values = value == null ? null : Collections.singletonList(value);
addDisjunctionIn(criteria, field, values);
}
public static <X> void addDisjunctionIn(DetachedCriteria criteria, String field, List<X> values) {
if (values != null && !values.isEmpty()) {
//disjunction is the same as logical OR
criteria.add(Restrictions.disjunction().add(Restrictions.isNull(field)).add(Restrictions.in(field, values)));
} else {
criteria.add(Restrictions.isNull(field));
}
}
/**
* only use when using sql restriction.
*
* @param s
* @return
*/
public static String ilikeSql(String s) {
return " ilike '" + MatchMode.ANYWHERE.toMatchString(escape(s)) + "'";
}
public static String exactSql(String s) {
return " ='" + MatchMode.EXACT.toMatchString(escape(s)) + "'";
}
private static String escape(String value) {
return value
.replace("%", "\\%")
.replace("'", "''")
.replace("_", "\\_");
}
}