web-dev-qa-db-ja.com

例によるJPA仕様

スプリングブーツはこちら。複雑なクエリを実装するコンテキストで使用される場合、JpaRepositoriesSpecificationsを囲み、いくつかのアイテムで「木々の森」を見るのに苦労しています。

Specificationの標準的な例は次のとおりです。

_public class PersonSpecification implements Specification<Person> {
    private Person filter;

    public PersonSpecification(Person filter) {
        super();
        this.filter = filter;
    }

    public Predicate toPredicate(Root<Person> root, CriteriaQuery<?> cq,
            CriteriaBuilder cb) {
        Predicate p = cb.disjunction();

        if (filter.getName() != null) {
            p.getExpressions()
                    .add(cb.equal(root.get("name"), filter.getName()));
        }

        if (filter.getSurname() != null && filter.getAge() != null) {
            p.getExpressions().add(
                    cb.and(cb.equal(root.get("surname"), filter.getSurname()),
                            cb.equal(root.get("age"), filter.getAge())));
        }

        return p;
    }
}
_

このtoPredicate(...)メソッドでは、_Root<Person>_とCriteriaQueryは何を表していますか?最も重要なことは、それはsoundseachに1つのSpecification implを作成する必要があるように、各仕様が1つに変換され、たった1つの述語...例えば、「Smeeb」という姓で25歳以上のすべての人を見つけたい場合、_LastnameMatchingSpecification<Person>_と_AgeGreaterThanSpecification<Person>_。誰かがこれを確認または明確化できますか?!

9
smeeb

これも最初は私にとって大変でしたが、今では簡単に動的クエリを作成し、テーブルごとに1つの仕様を作成しています(高度な検索が必要な場合)

これらのオブジェクトを次のように考えてください:

  1. ルートはあなたのテーブルです。
  2. CriteriaQueryはクエリであり、個別のサブクエリ、並べ替えなどの適用に適しています。
  3. CriteriaBuilderは条件であり、where句の作成に適しています

-

常にリストから始めて、最後にニーズに基づいてAND/OR条件でそれらを要約します。

public Predicate toPredicate(Root<Person> root, CriteriaQuery<?> cq, CriteriaBuilder cb) {
    List<Predicate> predicates = new ArrayList<>();

    if(filter.getName() != null) {
        predicates.add(cb.equal(root.get("name"), filter.getName());
    }
    if(filter.getSurname() != null) {
        predicates.add(cb.equal(root.get("surname"), filter.getSurname());
    }
    if(filter.getAge() != null) {
        predicates.add(cb.equal(root.get("age"), filter.getAge());
    }
    if(predicates.isEmpty()){
        predicates.add(cb.equal(root.get("id"), -1);
        /* 
         I like to add this because without it if no criteria is specified then 
         everything is returned. Because that's how queries work without where 
         clauses. However, if my user doesn't provide any criteria I want to 
         say no results found. 
        */
    }

    return query.where(cb.and(predicates.toArray(new Predicate[0])))
                .distinct(true).orderBy(cb.desc(root.get("name")).getRestriction();
}

これで、ユーザーはこれら3つのフィールドの任意の組み合わせをここに渡すことができ、このロジックはクエリを動的に構築してそれらの条件を含めます。

例えばname = Johnおよびsurname = Doeおよびage = 41またはname = Johnおよびage = 41またはname = Johnなど。

最後に、文字列を検索するときは、cb.equalではなくcb.likeを使用することをお勧めします。これにより、%での部分検索がユーザーまたはフロントエンドシステムによって渡されるようになります。

Cb.likeはデフォルトで大文字と小文字を区別しないことに注意してください。次のようなcb.lowerまたはcb.upperと組み合わせて使用​​する必要があります。

 predicates.add(cb.like(cb.lower(root.get("name"), filter.getName().toLowercase());

お役に立てれば !

5
jDub9