Skip to content

Instantly share code, notes, and snippets.

@josergdev
Last active October 6, 2024 21:54
Show Gist options
  • Save josergdev/c327bb9569a6c891e7d4e21c01a2f719 to your computer and use it in GitHub Desktop.
Save josergdev/c327bb9569a6c891e7d4e21c01a2f719 to your computer and use it in GitHub Desktop.

Revisions

  1. josergdev revised this gist Oct 6, 2024. 2 changed files with 112 additions and 50 deletions.
    50 changes: 0 additions & 50 deletions CriteriaBuilderExtension.java
    Original file line number Diff line number Diff line change
    @@ -1,50 +0,0 @@
    package dev.joserg.jpa;

    import java.util.List;

    import jakarta.persistence.criteria.CriteriaBuilder;
    import jakarta.persistence.criteria.Expression;
    import jakarta.persistence.criteria.Predicate;

    public class CriteriaBuilderExtension {

    private final CriteriaBuilder criteriaBuilder;

    public CriteriaBuilderExtension(CriteriaBuilder criteriaBuilder) {
    this.criteriaBuilder = criteriaBuilder;
    }
    public Predicate tupleIn(final List<? extends Expression<?>> tupleExpression, final List<List<Object>> tupleValues) {
    final var leftIn = this.criteriaBuilder.function("", Object.class, tupleExpression.toArray(new Expression[0]));
    final var rightIn = tupleValues.stream()
    .map(tupleValue -> {
    if (tupleExpression.size() != tupleValue.size()) {
    throw new IllegalArgumentException("Tuple expression and tuple value must have the same size");
    }
    final var values = tupleValue.stream().map(this.criteriaBuilder::literal).toList();
    return this.criteriaBuilder.function("", Object.class, values.toArray(new Expression[0]));
    })
    .toArray();
    try {
    return leftIn.in(rightIn);
    } catch (final Exception e) {
    log.warn("Build predicate with multiple columns IN clause failed. Using simulated feature");
    return this.simulatedTupleIn(tupleExpression, tupleValues);
    }
    }

    public Predicate simulatedTupleIn(final List<? extends Expression<?>> tupleExpression, final List<List<Object>> tupleValues) {
    var outerPredicate = this.criteriaBuilder.disjunction();
    for (final var tupleValue : tupleValues) {
    if (tupleExpression.size() != tupleValue.size()) {
    throw new IllegalArgumentException("Tuple expression and tuple value must have the same size");
    }
    var innerPredicate = this.criteriaBuilder.conjunction();
    for (int i = 0; i < tupleExpression.size(); i++) {
    final var equalPredicate = this.criteriaBuilder.equal(tupleExpression.get(i), tupleValue.get(i));
    innerPredicate = this.criteriaBuilder.and(innerPredicate, equalPredicate);
    }
    outerPredicate = this.criteriaBuilder.or(outerPredicate, innerPredicate);
    }
    return outerPredicate;
    }
    }
    112 changes: 112 additions & 0 deletions CriteriaBuilderTupleIn.java
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,112 @@
    package dev.joserg.jpa;

    import java.util.ArrayList;
    import java.util.List;
    import java.util.function.Function;

    import jakarta.persistence.criteria.CriteriaBuilder;
    import jakarta.persistence.criteria.Expression;
    import jakarta.persistence.criteria.Predicate;
    import lombok.extern.slf4j.Slf4j;

    @Slf4j
    public class CriteriaBuilderTupleIn {

    private final CriteriaBuilder criteriaBuilder;

    public CriteriaBuilderTupleIn(CriteriaBuilder criteriaBuilder) {
    this.criteriaBuilder = criteriaBuilder;
    }

    public <T> Predicate tupleIn(final List<T> tupables, final Function<T, In> tupleizer) {
    return this.tupleIn(tupables.stream().map(tupable -> InTupable.of(tupable, tupleizer)).toList());
    }

    public Predicate tupleIn(final List<? extends InTupable> inTupables) {
    final var tuples = inTupables.stream().map(InTupable::inTupleize).toList();
    return tuples.stream().findFirst()
    .map(tuple -> this.tupleIn(tuple.getExpressions(), tuples.stream().map(In::getValues).toList()))
    .orElseGet(this.criteriaBuilder::disjunction);
    }

    public Predicate tupleIn(final List<? extends Expression<?>> tupleExpression, final List<List<Object>> tupleValues) {
    final var leftIn = this.criteriaBuilder.function("", Object.class, tupleExpression.toArray(Expression[]::new));
    final var rightIn = tupleValues.stream()
    .map(tupleValue -> {
    if (tupleExpression.size() != tupleValue.size()) {
    throw new IllegalArgumentException("Tuple expression and tuple value must have the same size");
    }
    final var values = tupleValue.stream().map(this.criteriaBuilder::literal).toArray(Expression[]::new);
    return this.criteriaBuilder.function("", Object.class, values);
    })
    .toArray(Expression[]::new);
    try {
    return leftIn.in(rightIn);
    } catch (final Exception e) {
    log.warn("Build predicate with multiple columns IN clause failed. Using simulated feature");
    return this.simulatedTupleIn(tupleExpression, tupleValues);
    }
    }

    public <T> Predicate simulatedTupleIn(final List<T> tupables, final Function<T, In> tupleizer) {
    return this.simulatedTupleIn(tupables.stream().map(tupable -> InTupable.of(tupable, tupleizer)).toList());
    }

    public Predicate simulatedTupleIn(final List<? extends InTupable> inTupables) {
    final var tuples = inTupables.stream().map(InTupable::inTupleize).toList();
    return tuples.stream().findFirst()
    .map(tuple -> this.tupleIn(tuple.getExpressions(), tuples.stream().map(In::getValues).toList()))
    .orElseGet(this.criteriaBuilder::disjunction);
    }

    public Predicate simulatedTupleIn(final List<? extends Expression<?>> tupleExpression, final List<List<Object>> tupleValues) {
    var outerPredicate = this.criteriaBuilder.disjunction();
    for (final var tupleValue : tupleValues) {
    if (tupleExpression.size() != tupleValue.size()) {
    throw new IllegalArgumentException("Tuple expression and tuple value must have the same size");
    }
    var innerPredicate = this.criteriaBuilder.conjunction();
    for (int i = 0; i < tupleExpression.size(); i++) {
    final var equalPredicate = this.criteriaBuilder.equal(tupleExpression.get(i), tupleValue.get(i));
    innerPredicate = this.criteriaBuilder.and(innerPredicate, equalPredicate);
    }
    outerPredicate = this.criteriaBuilder.or(outerPredicate, innerPredicate);
    }
    return outerPredicate;
    }

    public interface InTupable {
    static <T> InTupable of(final T tupable, final Function<T, In> tupleizer) {
    return () -> tupleizer.apply(tupable);
    }

    In inTupleize();
    }

    public static class In {

    private final List<Expression<?>> expressions;

    private final List<Object> values;

    public In() {
    this.expressions = new ArrayList<>();
    this.values = new ArrayList<>();
    }

    public <T> In element(Expression<T> expression, T value) {
    this.expressions.add(expression);
    this.values.add(value);
    return this;
    }

    public List<Expression<?>> getExpressions() {
    return this.expressions;
    }

    public List<Object> getValues() {
    return this.values;
    }
    }

    }
  2. josergdev revised this gist Oct 6, 2024. 1 changed file with 9 additions and 9 deletions.
    18 changes: 9 additions & 9 deletions CriteriaBuilderExtension.java
    Original file line number Diff line number Diff line change
    @@ -13,12 +13,11 @@ public class CriteriaBuilderExtension {
    public CriteriaBuilderExtension(CriteriaBuilder criteriaBuilder) {
    this.criteriaBuilder = criteriaBuilder;
    }

    public Predicate tuplesIn(final List<? extends Expression<?>> tuplePath, final List<List<Object>> tupleValues) {
    final var leftIn = this.criteriaBuilder.function("", Object.class, tuplePath.toArray(new Expression[0]));
    public Predicate tupleIn(final List<? extends Expression<?>> tupleExpression, final List<List<Object>> tupleValues) {
    final var leftIn = this.criteriaBuilder.function("", Object.class, tupleExpression.toArray(new Expression[0]));
    final var rightIn = tupleValues.stream()
    .map(tupleValue -> {
    if (tuplePath.size() != tupleValue.size()) {
    if (tupleExpression.size() != tupleValue.size()) {
    throw new IllegalArgumentException("Tuple expression and tuple value must have the same size");
    }
    final var values = tupleValue.stream().map(this.criteriaBuilder::literal).toList();
    @@ -28,19 +27,20 @@ public Predicate tuplesIn(final List<? extends Expression<?>> tuplePath, final L
    try {
    return leftIn.in(rightIn);
    } catch (final Exception e) {
    return this.simulatedTuplesIn(tuplePath, tupleValues);
    log.warn("Build predicate with multiple columns IN clause failed. Using simulated feature");
    return this.simulatedTupleIn(tupleExpression, tupleValues);
    }
    }

    public Predicate simulatedTuplesIn(final List<? extends Expression<?>> tuplePath, final List<List<Object>> tupleValues) {
    public Predicate simulatedTupleIn(final List<? extends Expression<?>> tupleExpression, final List<List<Object>> tupleValues) {
    var outerPredicate = this.criteriaBuilder.disjunction();
    for (final var tupleValue : tupleValues) {
    if (tuplePath.size() != tupleValue.size()) {
    if (tupleExpression.size() != tupleValue.size()) {
    throw new IllegalArgumentException("Tuple expression and tuple value must have the same size");
    }
    var innerPredicate = this.criteriaBuilder.conjunction();
    for (int i = 0; i < tuplePath.size(); i++) {
    final var equalPredicate = this.criteriaBuilder.equal(tuplePath.get(i), tupleValue.get(i));
    for (int i = 0; i < tupleExpression.size(); i++) {
    final var equalPredicate = this.criteriaBuilder.equal(tupleExpression.get(i), tupleValue.get(i));
    innerPredicate = this.criteriaBuilder.and(innerPredicate, equalPredicate);
    }
    outerPredicate = this.criteriaBuilder.or(outerPredicate, innerPredicate);
  3. josergdev revised this gist Oct 6, 2024. 1 changed file with 10 additions and 10 deletions.
    20 changes: 10 additions & 10 deletions CriteriaBuilderExtension.java
    Original file line number Diff line number Diff line change
    @@ -15,17 +15,17 @@ public CriteriaBuilderExtension(CriteriaBuilder criteriaBuilder) {
    }

    public Predicate tuplesIn(final List<? extends Expression<?>> tuplePath, final List<List<Object>> tupleValues) {
    final var leftIn = this.criteriaBuilder.function("", Object.class, tuplePath.toArray(new Expression[0]));
    final var rightIn = tupleValues.stream()
    .map(tupleValue -> {
    if (tuplePath.size() != tupleValue.size()) {
    throw new IllegalArgumentException("Tuple expression and tuple value must have the same size");
    }
    final var values = tupleValue.stream().map(this.criteriaBuilder::literal).toList();
    return this.criteriaBuilder.function("", Object.class, values.toArray(new Expression[0]));
    })
    .toArray();
    try {
    final var leftIn = this.criteriaBuilder.function("", Object.class, tuplePath.toArray(new Expression[0]));
    final var rightIn = tupleValues.stream()
    .map(tupleValue -> {
    if (tuplePath.size() != tupleValue.size()) {
    throw new IllegalArgumentException("Tuple expression and tuple value must have the same size");
    }
    final var values = tupleValue.stream().map(this.criteriaBuilder::literal).toList();
    return this.criteriaBuilder.function("", Object.class, values.toArray(new Expression[0]));
    })
    .toArray();
    return leftIn.in(rightIn);
    } catch (final Exception e) {
    return this.simulatedTuplesIn(tuplePath, tupleValues);
  4. josergdev revised this gist Oct 6, 2024. 1 changed file with 20 additions and 10 deletions.
    30 changes: 20 additions & 10 deletions CriteriaBuilderExtension.java
    Original file line number Diff line number Diff line change
    @@ -14,20 +14,30 @@ public CriteriaBuilderExtension(CriteriaBuilder criteriaBuilder) {
    this.criteriaBuilder = criteriaBuilder;
    }

    public Predicate tuplesIn(final List<? extends Expression<Object>> tuplePath, final List<List<Object>> tupleValues) {
    final var leftIn = this.criteriaBuilder.function("", Object.class, tuplePath.toArray(new Expression[0]));
    final var rightIn = tupleValues.stream()
    .map(tupleValue -> {
    final var values = tupleValue.stream().map(this.criteriaBuilder::literal).toList();
    return this.criteriaBuilder.function("", Object.class, values.toArray(new Expression[0]));
    })
    .toArray();
    return leftIn.in(rightIn);
    public Predicate tuplesIn(final List<? extends Expression<?>> tuplePath, final List<List<Object>> tupleValues) {
    try {
    final var leftIn = this.criteriaBuilder.function("", Object.class, tuplePath.toArray(new Expression[0]));
    final var rightIn = tupleValues.stream()
    .map(tupleValue -> {
    if (tuplePath.size() != tupleValue.size()) {
    throw new IllegalArgumentException("Tuple expression and tuple value must have the same size");
    }
    final var values = tupleValue.stream().map(this.criteriaBuilder::literal).toList();
    return this.criteriaBuilder.function("", Object.class, values.toArray(new Expression[0]));
    })
    .toArray();
    return leftIn.in(rightIn);
    } catch (final Exception e) {
    return this.simulatedTuplesIn(tuplePath, tupleValues);
    }
    }

    public Predicate simulatedTuplesIn(final List<? extends Expression<Object>> tuplePath, final List<List<Object>> tupleValues) {
    public Predicate simulatedTuplesIn(final List<? extends Expression<?>> tuplePath, final List<List<Object>> tupleValues) {
    var outerPredicate = this.criteriaBuilder.disjunction();
    for (final var tupleValue : tupleValues) {
    if (tuplePath.size() != tupleValue.size()) {
    throw new IllegalArgumentException("Tuple expression and tuple value must have the same size");
    }
    var innerPredicate = this.criteriaBuilder.conjunction();
    for (int i = 0; i < tuplePath.size(); i++) {
    final var equalPredicate = this.criteriaBuilder.equal(tuplePath.get(i), tupleValue.get(i));
  5. josergdev revised this gist Oct 6, 2024. 1 changed file with 13 additions and 0 deletions.
    13 changes: 13 additions & 0 deletions CriteriaBuilderExtension.java
    Original file line number Diff line number Diff line change
    @@ -24,4 +24,17 @@ public Predicate tuplesIn(final List<? extends Expression<Object>> tuplePath, fi
    .toArray();
    return leftIn.in(rightIn);
    }

    public Predicate simulatedTuplesIn(final List<? extends Expression<Object>> tuplePath, final List<List<Object>> tupleValues) {
    var outerPredicate = this.criteriaBuilder.disjunction();
    for (final var tupleValue : tupleValues) {
    var innerPredicate = this.criteriaBuilder.conjunction();
    for (int i = 0; i < tuplePath.size(); i++) {
    final var equalPredicate = this.criteriaBuilder.equal(tuplePath.get(i), tupleValue.get(i));
    innerPredicate = this.criteriaBuilder.and(innerPredicate, equalPredicate);
    }
    outerPredicate = this.criteriaBuilder.or(outerPredicate, innerPredicate);
    }
    return outerPredicate;
    }
    }
  6. josergdev created this gist Oct 6, 2024.
    27 changes: 27 additions & 0 deletions CriteriaBuilderExtension.java
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,27 @@
    package dev.joserg.jpa;

    import java.util.List;

    import jakarta.persistence.criteria.CriteriaBuilder;
    import jakarta.persistence.criteria.Expression;
    import jakarta.persistence.criteria.Predicate;

    public class CriteriaBuilderExtension {

    private final CriteriaBuilder criteriaBuilder;

    public CriteriaBuilderExtension(CriteriaBuilder criteriaBuilder) {
    this.criteriaBuilder = criteriaBuilder;
    }

    public Predicate tuplesIn(final List<? extends Expression<Object>> tuplePath, final List<List<Object>> tupleValues) {
    final var leftIn = this.criteriaBuilder.function("", Object.class, tuplePath.toArray(new Expression[0]));
    final var rightIn = tupleValues.stream()
    .map(tupleValue -> {
    final var values = tupleValue.stream().map(this.criteriaBuilder::literal).toList();
    return this.criteriaBuilder.function("", Object.class, values.toArray(new Expression[0]));
    })
    .toArray();
    return leftIn.in(rightIn);
    }
    }