Last active
August 24, 2024 12:03
-
-
Save josergdev/f67b27d4d8f561f2836ad73c7b01e8cf to your computer and use it in GitHub Desktop.
Spring Data Specification to find data by pairs of attributes
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package dev.joserg.util.jpa; | |
import static org.springframework.data.jpa.domain.Specification.allOf; | |
import static org.springframework.data.jpa.domain.Specification.anyOf; | |
import java.util.List; | |
import java.util.Optional; | |
import jakarta.annotation.Nonnull; | |
import jakarta.persistence.criteria.Root; | |
import jakarta.persistence.metamodel.SingularAttribute; | |
import org.springframework.data.jpa.domain.Specification; | |
public interface GenericSpecification { | |
static <E, T> Specification<E> by(final SingularAttribute<E, T> attribute, @Nonnull final T value) { | |
return (root, query, criteriaBuilder) -> criteriaBuilder.equal(root.get(attribute), value); | |
} | |
static <E, T> Specification<E> byOptional(final SingularAttribute<E, T> attribute, final Optional<T> valueOpt) { | |
return (root, query, criteriaBuilder) -> valueOpt | |
.map(value -> criteriaBuilder.equal(root.get(attribute), value)) | |
.orElseGet(() -> Specification.where(null).toPredicate((Root<Object>) root, query, criteriaBuilder)); | |
} | |
static <E, T> Specification<E> beingNull(final SingularAttribute<E, T> attribute) { | |
return (root, query, criteriaBuilder) -> criteriaBuilder.isNull(root.get(attribute)); | |
} | |
static <E, L, R> Specification<E> byPair( | |
final Pair<SingularAttribute<E, L>, SingularAttribute<E, R>> pairAttribute, | |
final Pair<L, R> pairValue) { | |
return allOf( | |
by(pairAttribute.first(), pairValue.first()), | |
by(pairAttribute.second(), pairValue.second())); | |
} | |
static <E, A1, A2, A3> Specification<E> byTriple( | |
final Triple<SingularAttribute<E, A1>, SingularAttribute<E, A2>, SingularAttribute<E, A3>> tripleAttribute, | |
final Triple<A1, A2, A3> tripleValue) { | |
return allOf( | |
by(tripleAttribute.first(), tripleValue.first()), | |
by(tripleAttribute.second(), tripleValue.second()), | |
by(tripleAttribute.third(), tripleValue.third())); | |
} | |
static <E, A1> Specification<E> byAnySingle( | |
final SingularAttribute<E, A1> attribute, | |
final List<A1> singleValues) { | |
return anyOf(singleValues.stream().map(value -> by(attribute, value)).toList()); | |
} | |
static <E, A1, A2> Specification<E> byAnyPair( | |
final Pair<SingularAttribute<E, A1>, SingularAttribute<E, A2>> pairAttribute, | |
final List<Pair<A1, A2>> pairValues) { | |
return anyOf(pairValues.stream().map(value -> byPair(pairAttribute, value)).toList()); | |
} | |
static <E, A1, A2, A3> Specification<E> byAnyTriple( | |
final Triple<SingularAttribute<E, A1>, SingularAttribute<E, A2>, SingularAttribute<E, A3>> tripleAttribute, | |
final List<Triple<A1, A2, A3>> tripleValues) { | |
return anyOf(tripleValues.stream().map(value -> byTriple(tripleAttribute, value)).toList()); | |
} | |
record Pair<A1, A2>(A1 first, A2 second) { | |
public static <A1, A2> Pair<A1, A2> ofPair(final A1 first, final A2 second) { | |
return new Pair<>(first, second); | |
} | |
} | |
record Triple<A1, A2, A3>(A1 first, A2 second, A3 third) { | |
public static <A1, A2, A3> Triple<A1, A2, A3> ofTriple(final A1 first, final A2 second, final A3 third) { | |
return new Triple<>(first, second, third); | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Given a Jpa Entity Class:
With a generated MetaModel:
You can use ByAnyPairSpecification.byAnyPair(...) as argument of a JpaSpecificaitonExecutor: