/* * Copyright 2009 The Fornax Project Team, including the original * author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.sculptor.framework.accessimpl.jpa; import java.util.ArrayList; import java.util.Collection; import java.util.List; import javax.persistence.criteria.CriteriaBuilder; import javax.persistence.criteria.CriteriaQuery; import javax.persistence.criteria.Expression; import javax.persistence.criteria.Order; import javax.persistence.criteria.Path; import javax.persistence.criteria.Predicate; import javax.persistence.criteria.Root; import javax.persistence.criteria.Selection; import org.sculptor.framework.accessapi.ConditionalCriteria; import org.sculptor.framework.accessapi.FindByConditionAccess2; import org.sculptor.framework.accessapi.ConditionalCriteria.Operator; /** * <p> * Implementation of Access command FindByConditionAccess. * </p> * <p> * Command design pattern. * </p> */ public class JpaFindByConditionAccessImplGeneric<T,R> extends JpaCriteriaQueryAccessBase<T,R> implements FindByConditionAccess2<R> { private List<ConditionalCriteria> conditionalCriterias = new ArrayList<ConditionalCriteria>(); public JpaFindByConditionAccessImplGeneric() { super(); } public JpaFindByConditionAccessImplGeneric(Class<T> type) { super(type); } public JpaFindByConditionAccessImplGeneric(Class<T> type, Class<R> resultType) { super(type, resultType); } public void setCondition(List<ConditionalCriteria> criteria) { conditionalCriterias=criteria; } public void addCondition(ConditionalCriteria criteria) { conditionalCriterias.add(criteria); } public List<R> getResult() { return getListResult(); } @Override protected List<Predicate> preparePredicates() { List<Predicate> predicates = new ArrayList<Predicate>(); for (ConditionalCriteria criteria : conditionalCriterias) { Predicate predicate = preparePredicate(criteria); if (predicate != null) { predicates.add(preparePredicate(criteria)); } } return predicates; } @Override protected void prepareConfig(QueryConfig config) { config.setDistinct(false); } @SuppressWarnings("unchecked") protected void prepareSelect(CriteriaQuery<R> criteriaQuery, Root<T> root, QueryConfig config) { List<Selection<?>> selections = new ArrayList<Selection<?>>(); for (ConditionalCriteria criteria : conditionalCriterias) { Selection<?> selection = null; if (Operator.Select.equals(criteria.getOperator())) { selection = getPath(root, criteria.getPropertyFullName()); } else if (Operator.Max.equals(criteria.getOperator())) { selection = getCriteriaBuilder().max((Expression<? extends Number>)getPath(root, criteria.getPropertyFullName())); } else if (Operator.Min.equals(criteria.getOperator())) { selection = getCriteriaBuilder().min((Expression<? extends Number>)getPath(root, criteria.getPropertyFullName())); } else if (Operator.Avg.equals(criteria.getOperator())) { selection = getCriteriaBuilder().avg((Expression<? extends Number>)getPath(root, criteria.getPropertyFullName())); } else if (Operator.Sum.equals(criteria.getOperator())) { selection = getCriteriaBuilder().sum((Expression<? extends Number>)getPath(root, criteria.getPropertyFullName())); } else if (Operator.SumAsLong.equals(criteria.getOperator())) { selection = getCriteriaBuilder().sumAsLong(getPath(root, criteria.getPropertyFullName()).as(Integer.class)); } else if (Operator.SumAsDouble.equals(criteria.getOperator())) { selection = getCriteriaBuilder().sumAsDouble(getPath(root, criteria.getPropertyFullName()).as(Float.class)); } else if (Operator.Count.equals(criteria.getOperator())) { selection = getCriteriaBuilder().count(getPath(root, criteria.getPropertyFullName()).as(Long.class)); } else if (Operator.CountDistinct.equals(criteria.getOperator())) { selection = getCriteriaBuilder().countDistinct(getPath(root, criteria.getPropertyFullName()).as(Long.class)); } if (selection != null) { if (criteria.getPropertyAlias() != null) { selection.alias(criteria.getPropertyAlias()); } selections.add(selection); } } if (!selections.isEmpty()) { if (selections.size() == 1) criteriaQuery.select((Selection<? extends R>) selections.get(0)); else criteriaQuery.multiselect(selections); } } @Override protected void prepareDistinct(QueryConfig config) { for (ConditionalCriteria criteria : conditionalCriterias) { ConditionalCriteria.Operator operator = criteria.getOperator(); if (Operator.DistinctRoot.equals(operator)) { config.setDistinct(true); break; } } } @Override protected void prepareFetch(Root<T> root, QueryConfig config) { for (ConditionalCriteria criteria : conditionalCriterias) { if (Operator.FetchEager.equals(criteria.getOperator())) { // TODO: this is not tested root.fetch(criteria.getPropertyFullName()); } else if (Operator.FetchLazy.equals(criteria.getOperator())) { // TODO: fetchLazy is not supported actually } } } @Override protected void prepareOrderBy(CriteriaQuery<R> criteriaQuery, Root<T> root, QueryConfig config) { List<Order> orderByList = new ArrayList<Order>(); for (ConditionalCriteria criteria : conditionalCriterias) { if (Operator.OrderAsc.equals(criteria.getOperator())) { orderByList.add(getCriteriaBuilder().asc(getPath(root, criteria.getPropertyFullName()))); } else if (Operator.OrderDesc.equals(criteria.getOperator())) { orderByList.add(getCriteriaBuilder().desc(getPath(root, criteria.getPropertyFullName()))); } } if (!orderByList.isEmpty()) { criteriaQuery.orderBy(orderByList); } } @Override protected void prepareGroupBy(CriteriaQuery<R> criteriaQuery, Root<T> root, QueryConfig config) { List<Expression<?>> groups = new ArrayList<Expression<?>>(); for (ConditionalCriteria criteria : conditionalCriterias) { if (Operator.GroupBy.equals(criteria.getOperator())) { groups.add(getPath(root, criteria.getPropertyFullName())); } } if (!groups.isEmpty()) { criteriaQuery.groupBy(groups); } } /** * Map conditional criteria to type safe predicates using unchecked casts. * * @param criteria * @return */ @SuppressWarnings({ "unchecked", "rawtypes" }) private Predicate preparePredicate(ConditionalCriteria criteria) { CriteriaBuilder builder = getCriteriaBuilder(); Root<T> root = getRoot(); Path<?> path = getPath(root, criteria.getPropertyFullName()); ConditionalCriteria.Operator operator = criteria.getOperator(); if (Operator.Equal.equals(operator)) { return builder.equal(path, criteria.getFirstOperant()); } else if (Operator.IgnoreCaseEqual.equals(operator)) { return builder.equal(builder.upper(path.as(String.class)), ((String) criteria.getFirstOperant()).toUpperCase()); } else if (Operator.LessThan.equals(operator)) { return builder.lessThan((Expression<Comparable>) path, (Comparable) criteria.getFirstOperant()); } else if (Operator.LessThanOrEqual.equals(operator)) { return builder.lessThanOrEqualTo((Expression<Comparable>) path, (Comparable) criteria.getFirstOperant()); } else if (Operator.GreatThan.equals(operator)) { return builder.greaterThan((Expression<Comparable>) path, (Comparable) criteria.getFirstOperant()); } else if (Operator.GreatThanOrEqual.equals(operator)) { return builder.greaterThanOrEqualTo((Expression<Comparable>) path, (Comparable) criteria.getFirstOperant()); } else if (Operator.Like.equals(operator)) { return builder.like((Expression<String>) path, (String) criteria.getFirstOperant()); } else if (Operator.IgnoreCaseLike.equals(operator)) { return builder.like(builder.upper((Expression<String>) path), ((String) criteria.getFirstOperant()).toUpperCase()); } else if (Operator.IsNull.equals(operator)) { return builder.isNull(path); } else if (Operator.IsNotNull.equals(operator)) { return builder.isNotNull(path); } else if (Operator.IsEmpty.equals(operator)) { // TODO: support additional types like Map,... if (getAttribute(root.getModel(), criteria.getPropertyFullName()).isCollection()) { return builder.isEmpty((Expression<Collection>)path); } else { return null; } } else if (Operator.IsNotEmpty.equals(operator)) { // TODO: support additional types like Map,... if (getAttribute(root.getModel(), criteria.getPropertyFullName()).isCollection()) { return builder.isNotEmpty((Expression<Collection>)path); } else { return null; } } else if (Operator.Between.equals(operator)) { return builder.between((Expression<Comparable>) path, (Comparable) criteria.getFirstOperant(), (Comparable) criteria.getSecondOperant()); } else if (Operator.Not.equals(operator)) { return builder.not(preparePredicate((ConditionalCriteria) criteria.getFirstOperant())); } else if (Operator.Or.equals(operator) && criteria.getFirstOperant() instanceof List<?>) { Predicate disjunction = builder.disjunction(); List<ConditionalCriteria> list = (List<ConditionalCriteria>) criteria.getFirstOperant(); for (ConditionalCriteria condition : list) { disjunction = builder.or(disjunction, preparePredicate(condition)); } return disjunction; } else if (Operator.Or.equals(operator)) { return builder.or( preparePredicate((ConditionalCriteria) criteria.getFirstOperant()), preparePredicate((ConditionalCriteria) criteria.getSecondOperant())); } else if (Operator.And.equals(operator) && criteria.getFirstOperant() instanceof List<?>) { Predicate conjunction = builder.conjunction(); List<ConditionalCriteria> list = (List<ConditionalCriteria>) criteria.getFirstOperant(); for (ConditionalCriteria condition : list) { conjunction = builder.and(conjunction, preparePredicate(condition)); } return conjunction; } else if (Operator.And.equals(operator)) { return builder.and( preparePredicate((ConditionalCriteria) criteria.getFirstOperant()), preparePredicate((ConditionalCriteria) criteria.getSecondOperant())); } else if (Operator.In.equals(operator)) { if (criteria.getFirstOperant() instanceof Collection<?>) { return path.in((Collection<?>) criteria.getFirstOperant()); } else { return path.in((Object[])criteria.getFirstOperant()); } } else if (Operator.EqualProperty.equals(operator)) { return builder.equal(path, getPath(root, (String) criteria.getFirstOperant())); } else if (Operator.LessThanProperty.equals(operator)) { return builder.lessThan((Expression<Comparable>) path, (Expression<Comparable>) getPath(root, (String) criteria.getFirstOperant())); } else if (Operator.LessThanOrEqualProperty.equals(operator)) { return builder.lessThanOrEqualTo((Expression<Comparable>) path, (Expression<Comparable>) getPath(root, (String) criteria.getFirstOperant())); } else if (Operator.GreatThanProperty.equals(operator)) { return builder.greaterThan((Expression<Comparable>) path, (Expression<Comparable>) getPath(root, (String) criteria.getFirstOperant())); } else if (Operator.GreatThanOrEqualProperty.equals(operator)) { return builder.greaterThanOrEqualTo((Expression<Comparable>) path, (Expression<Comparable>) getPath(getRoot(), (String) criteria.getFirstOperant())); } else if (Operator.ProjectionRoot.equals(operator)) { // TODO: support projectionRoot, if possible if (getConfig().throwExceptionOnConfigurationError()) { throw new QueryConfigException("Operator 'ProjectionRoot' is not supported"); } return null; } else { return null; } } @Override public void executeCount() { executeResultCount(); } }