Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -85,10 +85,19 @@ private Specification<Object> buildSpecification(
if (def.not()) {
spec = new Not<>(spec);
}
if (databind.getField().isAnnotationPresent(And.class)) {
var and = databind.getField().isAnnotationPresent(And.class);
var or = databind.getField().isAnnotationPresent(Or.class);
if (and && or) {
throw new IllegalArgumentException(
"@And and @Or are mutually exclusive, but both are present on "
+ databind.getTarget().getClass().getName()
+ "."
+ databind.getField().getName());
}
if (and) {
return new tw.com.softleader.data.jpa.spec.domain.And<>(spec);
}
if (databind.getField().isAnnotationPresent(Or.class)) {
if (or) {
return new tw.com.softleader.data.jpa.spec.domain.Or<>(spec);
}
return spec;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,13 @@
*/
package tw.com.softleader.data.jpa.spec.domain;

import static java.util.Comparator.comparing;

import jakarta.persistence.criteria.CriteriaBuilder;
import jakarta.persistence.criteria.CriteriaQuery;
import jakarta.persistence.criteria.Predicate;
import jakarta.persistence.criteria.Root;
import java.util.Collection;
import java.util.List;
import java.util.StringJoiner;
import lombok.NonNull;
import lombok.RequiredArgsConstructor;
Expand All @@ -36,17 +38,28 @@
@RequiredArgsConstructor
abstract class CompoundSpecification<T> implements Specification<T> {

@NonNull protected final transient Collection<Specification<T>> specs;
@NonNull protected final transient List<Specification<T>> specs;

/**
* Fold 的結果會相依於 element 的順序, 因此在 Fold 前會先把所有覆寫了預設運算子的 element 穩定排序到最後, 如此一來第一個 element
* 就必定是使用預設運算子的, 也就不會有 Wrapper 被忽略的問題; 換句話說, 無論欄位的宣告順序為何, 都會得到相同的組合結果
*/
@Override
public Predicate toPredicate(
@NonNull Root<T> root, CriteriaQuery<?> query, @NonNull CriteriaBuilder builder) {
return specs.stream()
.sorted(comparing(this::overridesOperator))
.reduce(this::combine)
.map(spec -> spec.toPredicate(root, query, builder))
.orElse(null);
}

/**
* @param element 要檢查的元素
* @return 該元素是否覆寫了本 Compound 的預設運算子
*/
protected abstract boolean overridesOperator(Specification<T> element);

/**
* @param result 到目前 Combine 的結果
* @param element 下一個元素
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
*/
package tw.com.softleader.data.jpa.spec.domain;

import java.util.Collection;
import java.util.List;
import lombok.NonNull;
import org.springframework.data.jpa.domain.Specification;

Expand All @@ -29,13 +29,18 @@
*/
public class Conjunction<T> extends CompoundSpecification<T> {

public Conjunction(@NonNull Collection<Specification<T>> specs) {
public Conjunction(@NonNull List<Specification<T>> specs) {
super(specs);
}

@Override
protected boolean overridesOperator(Specification<T> element) {
return element instanceof Or;
}

@Override
protected Specification<T> combine(Specification<T> result, Specification<T> element) {
if (element instanceof Or) {
if (overridesOperator(element)) {
return result.or(element);
}
return result.and(element);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
*/
package tw.com.softleader.data.jpa.spec.domain;

import java.util.Collection;
import java.util.List;
import lombok.NonNull;
import org.springframework.data.jpa.domain.Specification;

Expand All @@ -29,13 +29,18 @@
*/
public class Disjunction<T> extends CompoundSpecification<T> {

public Disjunction(@NonNull Collection<Specification<T>> specs) {
public Disjunction(@NonNull List<Specification<T>> specs) {
super(specs);
}

@Override
protected boolean overridesOperator(Specification<T> element) {
return element instanceof And;
}

@Override
protected Specification<T> combine(Specification<T> result, Specification<T> element) {
if (element instanceof And) {
if (overridesOperator(element)) {
return result.and(element);
}
return result.or(element);
Expand Down
Loading