okinawa

勉強メモ

JPAでの複数ワード検索(動的クエリ・動的SQL・ページング・照合順序)

Googleみたいにスペース区切りでAND検索したくて作ったやつ。

なおかつページングと照合順序(collate)も入れたかった。

例:「東京 銭湯」

参考

・setPrameterの参考

qiita.com

・ページスライスの参考

hibernate - Implementing Pagination using entity manager in spring - Stack Overflow

注意点

この実装だと半角カタカナの「プ」「ゲ」を全角カタカナ「プ」「ゲ」でLIKE検索するとヒットしない。

utf8_unicode_ciの仕様でそうなってしまう。

それも改善するならこっちもどうぞ↓

dodosu.hatenablog.jp

ポイント

  1. ページスライス

  2. クエリの生成。SOLが全て完成してから生成しないとダメ。

  3. 完成してから.setParameter()。

  4. LIKE句をsetParameter()で渡すとこ。

ソース

    // ここは検索メソッドとページングメソッドを呼び出してるだけ
    public Page<SkillEntity> searchBigSkill(String[] keywordArray, Pageable pageable) {
        List<SkillEntity> skillList = skillRepository.searchBigSkill(keywordArray);
        return commonService.returnPagedList(pageable, skillList);
    }
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Service;

/**
    * ページスライス
    *
    * 受け取ったListをページにスライスする
    *
    * @param <E>
    * @param pageable
    * @param listOfEntities
    * @return 1ページ分にスライスされたListをpageableにして返す
    */
    public <E> Page<E> returnPagedList(Pageable pageable, List<E> listOfEntities) {
        List<E> listToReturn = listOfEntities;
        if (pageable.isPaged()) {

            // 1ページのデータ数
            int pageSize = pageable.getPageSize();

            // 現在のページ番号
            int currentPage = pageable.getPageNumber();

            // 現在のページの最初の要素番号
            int startItem = currentPage * pageSize;

            // ページに続きがあるかの判定
            if (listOfEntities.size() < startItem) {

                //ページに続きがなければ空のリストを返す
                listToReturn = Collections.emptyList();

            } else {

                //現在のページの最後の要素番号
                int toIndex = Math.min(startItem + pageSize, listOfEntities.size());

                // startItemからtoIndexまでのListを作る
                listToReturn = listOfEntities.subList(startItem, toIndex);
            }
        }
        return new PageImpl<>(listToReturn, pageable, listOfEntities.size());
    }
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;

import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.Query;

import org.springframework.stereotype.Repository;

public class Repository {

    @PersistenceContext
    private EntityManager em;

    //複数ワード検索&collate
    public List<SkillEntity> searchBigSkill(String[] keyWords) {

        String additionSQL = "";
        // 追加するSQL。複数keywordの場合はAND句を追加
        for (int i = 1; i < keyWords.length; i++) {
            additionSQL = additionSQL + " AND(bigName collate utf8_unicode_ci LIKE ?" + i + ")";
        }

        // 元となるSQL
        String sql = "SELECT * FROM skill WHERE(bigName collate utf8_unicode_ci LIKE ?0)"
                    + additionSQL
                    + " ORDER BY bigID, normalID, smallID";

        // クエリの生成。
        Query query = em.createNativeQuery(sql, SkillEntity.class);

        // クエリに検索ワードをセット。
        for (int i = 0; i < keyWords.length; i++) {
            query.setParameter(i, "%" + keyWords[i] + "%");
        }

        @SuppressWarnings("unchecked")
        List<SkillEntity> skillList = query.getResultList();
        return skillList;
    }