備忘録として、いくつかの実装例を記載しておく。
Hibernate ORMのソート(Spring Data JPAを使用する)
Spring Bootを使用していなくても、下記のように依存関係を読み込むことでSpring Data JPAを使用できる。
1 2 3 4 5 | < dependency > < groupId >org.springframework.data</ groupId > < artifactId >spring-data-jpa</ artifactId > < version >${spring-data.version}</ version > </ dependency > |
クエリメソッドを使用する
ルールに沿ったメソッド名を使用することにより、自動的にHibernateへSQLが発行される。
1 2 3 4 5 6 7 8 9 10 11 12 13 | public interface CrudRepository<T, ID> extends Repository<T, ID> { // 登録、または更新 <S extends T> S save(S entity); // プライマリーキーで検索 Optional<T> findById(ID primaryKey); // 全検索 Iterable<T> findAll(); // 全件数 long count(); // 削除 void delete(T entity); // 存在チェック boolean existsById(ID primaryKey); |
例えば、小説データを全検索して、小説のタイトルでソートをかけたい場合、下記のように書く。複数のソート条件を指定する場合は続けて記載する。
1 2 3 4 | @Repository public interface NovelRepository extends JpaRepository<Novel, Long>, JpaSpecificationExecutor<Novel> { List<Novel> findAllByOrderByTitle(); } |
※「findAll」の後に余分な「By」があるのは、ルール的にそこに検索条件が入ることになるため、今回は全検索なので「By」だけが残っている。
条件 APIを使用する
criteria を記述することにより、クエリの where 句を定義している場合、ソートは以下のように定義する。複数のソート条件を指定する場合は引数を追加する。
1 | novelRepository.findAll(spec, JpaSort.by( "title" )) |
1 2 3 | @Repository public interface NovelRepository extends JpaSpecificationExecutor<Novel> { } |
Hibernate Searchのソート
ソートの処理実装前に、エンティティに@KeywordFieldを追記する必要がある。
1 2 3 4 | /** タイトル */ @FullTextField (analyzer = "japanese" ) @KeywordField (name = "titleSort" , sortable = Sortable.YES) private String title; |
検索時に上記で定義した名称をソート条件に指定する。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | import org.hibernate.search.mapper.orm.Search; @Service public class NovelService { private final EntityManager entityManager; @Transactional public List<Novel> findAllIndex() { return Search.session(entityManager) .search(Novel. class ) .where(f -> f.matchAll()) .sort(f -> f.field( "titleSort" )) .fetchAllHits(); } |
ソートの条件を指定していないときは、スコアの高い順にソートされている。大雑把に言えば、スコアが高いほど、より多くの述語にマッチしているか、より良くマッチしていることを意味する。そのため、Hibernate Searchに関しては明示的にソートの条件を指定しない方が、良いのかもしれない。