Запросы в репозиториях
Методов CrudRepository хватает для работы с одной записью по id. Для поиска нужны свои запросы. В Micronaut Data их чаще всего не пишут, а называют: имя метода findByTitleContains обработчик разбирает при компиляции и превращает в SQL с WHERE title LIKE. Ошибка в имени поля обнаружится сразу при сборке, а не у пользователя.
Запросы по имени метода
Дополните репозиторий статей:
// src/main/java/com/example/ArticleRepository.java
package com.example;
import io.micronaut.data.annotation.Query;
import io.micronaut.data.jdbc.annotation.JdbcRepository;
import io.micronaut.data.model.query.builder.sql.Dialect;
import io.micronaut.data.repository.PageableRepository;
import java.util.List;
@JdbcRepository(dialect = Dialect.H2)
public interface ArticleRepository extends PageableRepository<Article, Long> {
List<Article> findByTitleContains(String text);
List<Article> findByTitleContainsOrderByIdDesc(String text);
long countByTitleContains(String text);
@Query("SELECT * FROM article WHERE LENGTH(body) > :minLength")
List<Article> findLong(int minLength);
}Для поиска по строке ?q= в ArticleService появляется метод search(text), который вызывает findByTitleContains. В журнале видно, во что превратилось имя:
SELECT article_.`id`,article_.`title`,article_.`body` FROM `article` article_ WHERE (article_.`title` LIKE CONCAT('%',?,'%'))Из чего складывается имя
| Часть | Пример | SQL |
|---|---|---|
findBy + поле | findByTitle | WHERE title = ? |
Contains | findByTitleContains | LIKE '%…%' |
GreaterThan, LessThan | findByIdGreaterThan | >, < |
And, Or | findByTitleAndBody | AND, OR |
OrderBy + поле + Desc | …OrderByIdDesc | ORDER BY id DESC |
countBy, existsBy, deleteBy | countByTitleContains | COUNT, проверка, DELETE |
Если запрос не выразить именем, напишите SQL в @Query. Параметр :minLength связывается с аргументом метода по имени.
Постраничный вывод
PageableRepository добавляет findAll(Pageable). Контроллер принимает Pageable параметром — Micronaut соберёт его из page, size и sort в адресе:
// src/main/java/com/example/ArticleController.java
@Get("/page")
public Page<Article> page(Pageable pageable) {
return articleService.findPage(pageable);
}В сервисе findPage просто вызывает articleRepository.findAll(pageable). Запрос curl "http://localhost:8080/articles/page?page=0&size=2" при трёх статьях вернёт:
{"content":[{"id":1,"title":"Привет, Micronaut","body":"Первая статья"},{"id":2,"title":"Записи в Java","body":"Вторая статья"}],"pageable":{"size":2,"number":0,"sort":{},"mode":"OFFSET"},"totalSize":3}Номер страницы начинается с нуля, totalSize — сколько записей всего. Сортировка задаётся так: ?page=1&size=2&sort=title.
Комментарии
Комментарий хранит номер своей статьи:
// src/main/java/com/example/Comment.java
@Serdeable
@MappedEntity
public record Comment(
@Id @GeneratedValue @Nullable Long id,
Long articleId,
String author,
String text) {
}// src/main/java/com/example/CommentRepository.java
@JdbcRepository(dialect = Dialect.H2)
public interface CommentRepository extends CrudRepository<Comment, Long> {
List<Comment> findByArticleId(Long articleId);
long countByArticleId(Long articleId);
}Поле articleId хранится в колонке article_id. CommentController с адресом /articles/{articleId}/comments отдаёт findByArticleId(articleId) на GET и сохраняет комментарий на POST. Проверка:
curl -X POST http://localhost:8080/articles/1/comments \
-H "Content-Type: application/json" -d '{"author":"Oleg","text":"Отличная статья"}'
curl http://localhost:8080/articles/1/comments[{"id":1,"articleId":1,"author":"Oleg","text":"Отличная статья"}]Частая ошибка
Опечатка в имени поля ломает сборку, а не работу приложения. Метод findByHeadline в репозитории статей даёт при компиляции:
[ERROR] ArticleRepository.java:[19,19] Unable to implement Repository method: com.example.ArticleRepository.findByHeadline(String headline).
Cannot query entity [Article] on non-existent property: Headline [title, body]В квадратных скобках Micronaut перечисляет поля, которые есть у сущности, — выберите нужное.