package fr.openwide.core.jpa.querydsl; import static com.google.common.base.Preconditions.checkNotNull; import java.io.Serializable; import java.util.Collection; import java.util.Comparator; import java.util.List; import java.util.Map; import javax.annotation.Nonnull; import javax.annotation.Nullable; import com.google.common.base.Function; import com.google.common.collect.Lists; import com.google.common.collect.Range; import com.google.common.collect.Table; import com.querydsl.core.Tuple; import com.querydsl.core.group.GroupBy; import com.querydsl.core.types.Expression; import com.querydsl.core.types.MappingProjection; import com.querydsl.core.types.Path; import com.querydsl.core.types.dsl.BooleanExpression; import com.querydsl.core.types.dsl.CaseBuilder; import com.querydsl.core.types.dsl.ComparableExpression; import com.querydsl.core.types.dsl.Expressions; import com.querydsl.core.types.dsl.NumberExpression; import com.querydsl.core.types.dsl.SimpleExpression; import com.querydsl.jpa.JPQLQuery; import fr.openwide.core.jpa.business.generic.model.GenericEntity; import fr.openwide.core.jpa.business.generic.model.QGenericEntity; import fr.openwide.core.jpa.querydsl.group.GroupBy2; public final class Expressions2 { private Expressions2() { } public static <T> NumberExpression<Integer> countIf(BooleanExpression condition) { /** * We use Expressions.* rather than constants, in order to avoid introducing request parameters. * Otherwise, Hibernate would freak out because it can't guess the CASE expression data type * (which seems legitimate). */ return new CaseBuilder().when(condition).then(Expressions.ONE).otherwise(Expressions.ZERO).sum(); } public static <T> NumberExpression<Integer> sumIf(NumberExpression<Integer> expression, BooleanExpression condition) { if (condition == null) { return expression.sum(); } else { /** * We use Expressions.* rather than constants, in order to avoid introducing request parameters. * Otherwise, Hibernate would freak out because it can't guess the CASE expression data type * (which seems legitimate). */ return new CaseBuilder().when(condition).then(expression).otherwise(Expressions.ZERO).sum(); } } public static BooleanExpression parentheses(BooleanExpression condition) { return condition == null ? null : Expressions.booleanTemplate("({0})", condition); } public static BooleanExpression isFalseNotNull(BooleanExpression condition) { if (condition == null) { return null; } else { BooleanExpression parenthesesCondition = parentheses(condition); return parenthesesCondition.isNotNull().and(parenthesesCondition.not()); } } public static BooleanExpression isFalseOrNull(BooleanExpression condition) { if (condition == null) { return null; } else { BooleanExpression parenthesesCondition = parentheses(condition); return parenthesesCondition.isNull().or(parenthesesCondition.not()); } } public static BooleanExpression isTrueNotNull(BooleanExpression condition) { if (condition == null) { return null; } else { BooleanExpression parenthesesCondition = parentheses(condition); return parenthesesCondition.isNotNull().and(parenthesesCondition); } } public static BooleanExpression isTrueOrNull(BooleanExpression condition) { if (condition == null) { return null; } else { BooleanExpression parenthesesCondition = parentheses(condition); return parenthesesCondition.isNull().or(parenthesesCondition); } } /** * @deprecated Use <code>query.transform(GroupBy2.transformer(GroupBy.sortedMap(key, value, keyComparator)))</code> */ @Deprecated public static <K, V> Map<K, V> map(JPQLQuery<?> query, Expression<K> key, Comparator<? super K> keyComparator, Expression<V> value) { return query.transform(GroupBy2.transformer(GroupBy.sortedMap(key, value, keyComparator))); } /** * @deprecated Use <code>map.putAll(query.transform(GroupBy2.transformer(GroupBy.map(key, value))))</code> */ @Deprecated public static <K, V> Map<K, V> map( Map<K, V> map, JPQLQuery<?> query, Expression<? extends K> key, Expression<? extends V> value) { map.putAll(query.transform(GroupBy.groupBy(key).as(value))); return map; } /** * @deprecated Use <code>query.transform(GroupBy2.transformer(GroupBy2.sortedTable(row, column, value, rowComparator, columnComparator)))</code> */ @Deprecated public static <R, C, V> Table<? extends R, ? extends C, ? extends V> mapToTable(JPQLQuery<?> query, Expression<? extends R> row, Comparator<? super R> rowComparator, Expression<? extends C> column, Comparator<? super C> columnComparator, Expression<? extends V> value) { return query.transform(GroupBy2.transformer(GroupBy2.sortedTable(row, column, value, rowComparator, columnComparator))); } /** * @deprecated Use <code>table.putAll(query.transform(GroupBy2.transformer(GroupBy2.table(row, column, value))))</code> */ @Deprecated public static <R, C, V> Table<R, C, V> mapToTable( Table<R, C, V> table, JPQLQuery<?> query, Expression<? extends R> row, Expression<? extends C> column, Expression<? extends V> value) { List<Tuple> list = query.select(row, column, value).fetch(); for (Tuple tuple : list) { table.put(tuple.get(row), tuple.get(column), tuple.get(value)); } return table; } /** * @see #selectConstant(Class, Object) */ @SuppressWarnings("unchecked") public static <T> Expression<? extends T> selectConstant(@Nonnull final T value) { return selectConstant((Class<T>)value.getClass(), value); } /** * Hibernate/HQL refuses parameters inside select, so using <code>Expressions.constant(T)</code> * in the select won't work... * <p>This method returns an expression which Hibernate won't ever hear of. */ public static <T> Expression<T> selectConstant(Class<T> clazz, @Nullable final T value) { return new MappingProjection<T>(clazz, new Expression[] {}) { private static final long serialVersionUID = 1L; @Override protected T map(Tuple row) { return value; } }; } public static <T, F> MappingProjection<F> fromFunction(final Class<T> inputClazz, Expression<T> inputExpression, Class<F> outputClazz, final Function<? super T, ? extends F> function) { return new MappingProjection<F>(outputClazz, inputExpression) { private static final long serialVersionUID = 1L; @Override protected F map(Tuple row) { T fromValue = row.get(0, inputClazz); return function.apply(fromValue); } }; } /** * Allows Hibernate to guess the parameter types, especially inside a CASE statement. */ public static <E extends Enum<E>> Expression<E> enumLiteral(E value) { checkNotNull(value); return Expressions.template(value.getDeclaringClass(), value.getDeclaringClass().getName() + "." + value.name()); } public static <T extends Comparable<T>> BooleanExpression inRange(ComparableExpression<? extends T> value, Range<T> range) { @SuppressWarnings("unchecked") // If T is comparable to T, ? extends T also is comparable to T => geo(), gt() etc. should accept T as a parameter ComparableExpression<T> castedValue = (ComparableExpression<T>) value; BooleanExpression expression = null; if (range.hasLowerBound()) { switch (range.lowerBoundType()) { case CLOSED: expression = Expressions.allOf(expression, castedValue.goe(range.lowerEndpoint())); break; case OPEN: expression = Expressions.allOf(expression, castedValue.gt(range.lowerEndpoint())); break; default: throw new IllegalStateException("Impossible switch value"); } } if (range.hasUpperBound()) { switch (range.upperBoundType()) { case CLOSED: expression = Expressions.allOf(expression, castedValue.loe(range.upperEndpoint())); break; case OPEN: expression = Expressions.allOf(expression, castedValue.lt(range.upperEndpoint())); break; default: throw new IllegalStateException("Impossible switch value"); } } return expression; } public static <T> BooleanExpression eqIfGiven(SimpleExpression<T> path, T value) { return value == null ? null : path.eq(value); } /** * Use this when you don't want Hibernate to make a join just for this single comparison. * <p>This avoids Exceptions when comparing inside a HQL WITH or JPQL ON, where conditions must * rely on only one entity. */ public static <K extends Serializable & Comparable<K>, T extends GenericEntity<K, ?>> BooleanExpression entityIdEqIfGiven(Path<T> path, T value) { if (value != null) { QGenericEntity genericEntityPath = new QGenericEntity(path); return genericEntityPath.id.eq(value.getId()); } else { return null; } } public static <K extends Serializable & Comparable<K>, T extends GenericEntity<K, ?>> BooleanExpression idEqIfGiven(SimpleExpression<K> path, T value) { if (value != null) { return path.eq(value.getId()); } else { return null; } } public static <T> BooleanExpression inIfGiven(SimpleExpression<T> path, Collection<? extends T> subset) { if (subset != null && !subset.isEmpty()) { return path.in(subset); } else { return null; } } /** * @see #entityIdEqIfGiven(Path, GenericEntity) */ public static <K extends Serializable & Comparable<K>, T extends GenericEntity<K, ?>> BooleanExpression entityIdInIfGiven(Path<T> path, Collection<? extends T> subset) { if (subset != null && !subset.isEmpty()) { QGenericEntity genericEntityPath = new QGenericEntity(path); return genericEntityPath.id.in(getIds(subset)); } else { return null; } } private static <K> List<K> getIds(Collection<? extends GenericEntity<? extends K, ?>> entities) { if (entities == null) { return Lists.newArrayList(); } else { List<K> ids = Lists.newArrayListWithCapacity(entities.size()); for (GenericEntity<? extends K, ?> entity : entities) { ids.add(entity.getId()); } return ids; } } }