okinawa

勉強メモ

thymeleafで使えるページングのメソッド一覧

PageImplクラスのメソッドが使えるようです。

spring.pleiades.io

こんな感じでメソッド名そのままで使えるんです。 知らなかったなあ。 どういう仕組みなんだろ。

   <p th:text="${page.getSize}"></p>
    <p th:text="${page.getNumber}"></p>
    <p th:text="${page.NumberOfElements}"></p>
    <p th:text="${page.getContent}"></p>
    <p th:text="${page.hasContent}"></p>
    <p th:text="${page.isFirst}"></p>
    <p th:text="${page.nextOrLastPageable}"></p>
    <p th:text="${page.isLast()}"></p>
    <p th:text="${page.nextPageable}"></p>
    <p th:text="${page.getSort}"></p>
    <p th:text="${page.hasNext}"></p>
    <p th:text="${page.hasPrevious}"></p>
    <p th:text="${page.getTotalElements}"></p>
    <p th:text="${page.getTotalPages}"></p>
    <p th:text="${page.getPageable}"></p>
    <p th:text="${page.iterator}"></p>
    <p th:text="${page.empty}"></p>
    <p th:text="${page.totalPages}"></p>

@PageableDefaultでページのソートやサイズ指定など

spring.pleiades.io

@PageableDefaultでページのソート、サイズ指定、開始ページ指定ができるよ。

かんたんだよ。

@Controller
public class AccountController {

    @Autowired
    private Service service;

    @GetMapping("/hello")
    public String getHello(
            @PageableDefault(page = 0, size = 2, direction = Direction.DESC, sort = { "id", "name" }) Pageable pageable,
            Model model) {

        Page<AccountEntity> accountAll = service.findAllAccount(pageable);
        model.addAttribute("page", accountAll);
        model.addAttribute("accountAll", accountAll.getContent());

        return "hello";
    }
}

メソッドーチェーンとは

・決まり

チェーンでつなぐメソッドは自身のオブジェクトの参照を返す必要がある。(return this;)

       // toString()もtrim()もstrip()も returnはthis
        String s = "あいうえお".toString().trim().strip();
        
        // 最後に繋げるメソッドはthisを返さなくても良い
        char c = "あいうえお".toString().trim().strip().charAt(3);
        
        // これと同じ
        String s2 = "あいうえお".toString();
        s2 = s2.trim();
        s2 = s2.strip();

-------------------------------------------
public String toString() {
            return this;
        }

        public String trim() {
            String ret = isLatin1() ? StringLatin1.trim(value)
                                    : StringUTF16.trim(value);
            return ret == null ? this : ret;
        }
        
        public String strip() {
            String ret = isLatin1() ? StringLatin1.strip(value)
                                    : StringUTF16.strip(value);
            return ret == null ? this : ret;
        
        public char charAt(int index) {
            if (isLatin1()) {
                return StringLatin1.charAt(value, index);
            } else {
                return StringUTF16.charAt(value, index);
            }
        }

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;
    }

SpringBootでページング

SpringBootでページング

・概要
Page<T>のTにコンテンツをぶち込むと、コンテンツ量に応じてページ分割してくれる。
TはEntityクラスとかFormクラス。

参考

https://qiita.com/tanibuchi12/items/6c8fedbc19bdb277d6f2

https://qiita.com/KevinFQ/items/ca68a3001bae19f92879

公式

https://docs.spring.io/spring-data/commons/docs/current/api/org/springframework/data/domain/Page.html

非常に詳しい公式

macchinetta.github.io

かっこいいページネーション

https://taikii.net/posts/2020/02/spring-boot-kakko-e-pagination/

検索用:ページネーション pageable page

SpringでJPAメモ

SpringでJPA

JPAはDBとのやりとりを簡単にしてくれる。

クエリメソッドでSQLを自動生成。

SpringBoot + JPA + Thymeleafで簡単なCRUDを作る②~画面と機能作成まで~ https://qiita.com/ozaki25/items/3b348874b6db5ab4f04f

DB側で必要な設定は↓ 参考:@GeneratedValueを使って主キーを生成する方法 https://qiita.com/KevinFQ/items/a6d92ec7b32911e50ffe

JPA その3 - JPQL https://qiita.com/opengl-8080/items/e074330b5f4862d9995f

Spring Data JPA でのクエリー実装方法まとめ(@Query()とかの使い方) https://qiita.com/tag1216/items/55742fdb442e5617f727

【Spring Data JPA】自動実装されるメソッドの命名ルール(クエリメソッドってやつの参考) https://qiita.com/shindo_ryo/items/af7d12be264c2cc4b252#orderby

JPQL基本の書き方

table名→Entityクラス名

カラム名→フィールド名

で書く。

さらに、teble名を変数宣言する(ここの場合AdressEntity a)

@Table(name="address")
public class AddressEntity {
    @Column(name="city_code") 
    private Integer cityCode;
}
---------------------------------
上記に対応したSQLとJPQL
SELECT * FROM address Where city_code = ?; //SQL
SELECT a FROM AddressEntity a Where a.cityName = ?1; //JPQL

Entity

  • @EntityをつけることでDBのテーブルと紐づく
  • @Tableでテーブル名を指定
  • @Idを付けた変数がテーブルのプライマリーキーになる
  • @GeneratedValueをつけると連番が自動で振られるようになる(MySQL側では主キーにオートインクリメントが必須)
  • @Columnでカラム名を指定
import javax.persistence.Column; 
import javax.persistence.Entity; 
import javax.persistence.GeneratedValue; 
import javax.persistence.GenerationType; 
import javax.persistence.Id; 
import javax.persistence.Table; 
import lombok.Data; 
@Data 
@Entity 
@Table(name="player_table") 
-
public class Player { 
    @Id 
    @GeneratedValue(strategy = GenerationType.IDENTITY) 
    @Column(name="id") 
    private Integer id; 
    @Column(name="name") 
    private String name; 
    @Column(name="number") 
        private Integer number; 
    @Column(name="position") 
        private String position; 
}

Repository

中身は空でもOK。独自の処理をしたい時はここに追記。

import org.springframework.data.jpa.repository.JpaRepository; 
import org.springframework.stereotype.Repository; 
@Repository 
public interface PlayerRepository extends JpaRepository<Player, Long> { 

@PersistenceContext
    private EntityManager em;

    /**
     * 参考
     * @return プレイヤー全リスト 
     */
    public List<Player> index() { 
        return em.createQuery("select m from Player m ", Player.class).getResultList(); 
    }
}

Service

findAll/findOne/save/deleteはJPAのメソッド。 基本的なCRUDはこれでOK. SQL書かなくてよいのでグッド。

import java.util.List; 
import org.springframework.beans.factory.annotation.Autowired; 
import org.springframework.stereotype.Service; 
@Service 
public class PlayerService { 
    
    @Autowired 
    private PlayerRepository playerRepository; 
    public List<Player> findAll() { 
        return playerRepository.findAll(); 
    } 
    public Player findOne(Long id) { 
        return playerRepository.findById(id).orElse(null); 
    } 
    public Player save(Player player) { 
        return playerRepository.save(player); 
    } 
    public void delete(Long id) { 
        playerRepository.deleteById(id); 
    } 
}