JPAのCriteriaBuilderのconjunctionとdisjunctionについて

SpringのSpecificationなどで、動的なJPQLをCriteriaBuilderで、Predicateを組み立てるときに使う conjunctiondisjunction 、毎回どっちがどっちか忘れるのでメモ。

conjunction

AND条件の起点。SQLに解釈されると 1 = 1 になる。

(root, query, cb) -> {
    var predicate = cb.conjunction();
    predicate = cb.and(
            predicate,
            cb.equal(root.get(EntityClass_.id), entityId)
    );
}

disjunction

OR条件の起点。SQLに解釈されると 0 = 1 になる。

(root, query, cb) -> {
    var predicate = cb.disjunction();
    predicate = cb.or(
            predicate,
            cb.like(root.get(EntityClass_.name), "%" + entityName + "%")
    );
}

振り返り

入力された値に応じて検索条件を組み立てる場合、CriteriaBuilderのメソッドに渡すPredicateが null だと、実行時にNullPointerExceptionが発生するので、未入力なら conjunctiondisjunction を使うようにすると安全。

また、PredicateのListやStreamを用意して、Stream#reduceを使うと、 cb.andcb.or の呼び出しを減らせる。

// AND条件の場合
(root, query, cb) -> {
    var predicates = new ArrayList<Predicate>();
    predicates.add(...);
    return predicates.stream().reduce(cb.conjunction(), cb::and);
}

// OR条件の場合
(root, query, cb) -> {
    return Stream.of(
        ...
    )
    .reduce(cb.disjunction(), cb::or);
}