package de.flower.rmt.repository;
import de.flower.common.util.Check;
import org.hibernate.ejb.criteria.CriteriaBuilderImpl;
import org.hibernate.ejb.criteria.predicate.BooleanStaticAssertionPredicate;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.data.jpa.domain.Specifications;
import javax.persistence.criteria.*;
import javax.persistence.metamodel.Attribute;
import javax.persistence.metamodel.ListAttribute;
import javax.persistence.metamodel.PluralAttribute;
import javax.persistence.metamodel.SingularAttribute;
import java.util.List;
/**
* @author flowerrrr
*/
public class Specs {
public static <X, T> Specification eq(final SingularAttribute<X, T> attribute, final T object) {
return new Specification<X>() {
@Override
public Predicate toPredicate(Root<X> root, CriteriaQuery<?> query, CriteriaBuilder cb) {
return cb.equal(root.get(attribute), object);
}
};
}
public static <X, T> Specification isMember(final T object, final ListAttribute<X, T> attribute) {
return new Specification<X>() {
@Override
public Predicate toPredicate(Root<X> root, CriteriaQuery<?> query, CriteriaBuilder cb) {
return cb.isMember(object, root.get(attribute));
}
};
}
public static <X, T> Specification in(final SingularAttribute<X, T> attribute, final List<T> collection) {
return new Specification<X>() {
@Override
public Predicate toPredicate(final Root<X> root, final CriteriaQuery<?> query, final CriteriaBuilder cb) {
return root.get(attribute).in(collection);
}
};
}
/**
* Not the ideal solution. Want to separate building the predicate and the join.
*/
public static <X, Y, T> Specification joinEq(final ListAttribute<X, Y> joinAttribute, final SingularAttribute<Y, T> attribute, final T object) {
return new Specification<X>() {
@Override
public Predicate toPredicate(Root<X> root, CriteriaQuery<?> query, CriteriaBuilder cb) {
final ListJoin<X, Y> join = root.join(joinAttribute);
return cb.equal(join.get(attribute), object);
}
};
}
public static <X> Specification fetch(final Attribute<X, ?>... attributes) {
Check.notNull(attributes);
return new Specification<X>() {
@Override
public Predicate toPredicate(Root<X> root, CriteriaQuery<?> query, CriteriaBuilder cb) {
for (Attribute attribute : attributes) {
if (attribute.isCollection()) {
root.fetch((PluralAttribute<? super X, ?, Object>) attribute, JoinType.LEFT);
// force distinct results. fetching associations might lead to duplicate root entities in the result set.
query.distinct(true);
} else {
root.fetch((SingularAttribute<? super X, Object>) attribute, JoinType.LEFT);
}
}
// return always-true-predicate to satisfy caller. null is not allowed.
return new BooleanStaticAssertionPredicate((CriteriaBuilderImpl) cb, true);
}
};
}
public static <X, T> Specification asc(final SingularAttribute<X, T> attribute) {
return new Specification<X>() {
@Override
public Predicate toPredicate(Root<X> root, CriteriaQuery<?> query, CriteriaBuilder cb) {
query.orderBy(cb.asc(root.get(attribute)));
// return always-true-predicate to satisfy caller. null is not allowed.
return new BooleanStaticAssertionPredicate((CriteriaBuilderImpl) cb, true);
}
};
}
public static <X, T> Specification desc(final SingularAttribute<X, T> attribute) {
return new Specification<X>() {
@Override
public Predicate toPredicate(Root<X> root, CriteriaQuery<?> query, CriteriaBuilder cb) {
query.orderBy(cb.desc(root.get(attribute)));
// return always-true-predicate to satisfy caller. null is not allowed.
return new BooleanStaticAssertionPredicate((CriteriaBuilderImpl) cb, true);
}
};
}
/**
* @param joinAttribute
* @param attribute
* @param asc
* @param <X>
* @param <Y>
* @param <T>
* @return
* @deprecated Should not be used in queries that fetch list-associations cause this call will force distinct=false
* on the query.
*/
@Deprecated
public static <X, Y, T> Specification orderByJoin(final SingularAttribute<X, Y> joinAttribute, final SingularAttribute<Y, T> attribute, final boolean asc) {
return new Specification<X>() {
@Override
public Predicate toPredicate(final Root<X> root, final CriteriaQuery<?> query, final CriteriaBuilder cb) {
Join<X, Y> join;
join = root.join(joinAttribute);
Order order;
if (asc) {
order = cb.asc(join.get(attribute));
} else {
order = cb.desc(join.get(attribute));
}
query.orderBy(order);
// must fetch entity of ordered column
root.fetch(joinAttribute, JoinType.LEFT);
// query must not be distinct as h2 otherwise complains
if (query.isDistinct()) {
throw new IllegalStateException("Cannot use order by on joined entity when query is distinct");
}
query.distinct(false);
return new BooleanStaticAssertionPredicate((CriteriaBuilderImpl) cb, true);
}
};
}
public static Specification not(Specification a) {
return Specifications.not(a);
}
}