Spring Data JDBC: CrudRepository и Query methods

Продолжим упрощать предыдущий пример работы с базой. Как мы видели, JdbcTemplate позволяет писать непосредственно запрос, оставив за кулисами прочие служебные операции.

В предыдущем примере мы добавили spring-boot-starter-data-jdbc в POM.  Также добавили базу H2, в которой создается и заполняется таблица animal при старте приложения. И с помощью JdbcTemplate написали запросы для поиска, добавления, редактирования сущности Animal.

CrudRepository и PagingAndSortingRepository 

Но в Spring Data существует еще готовый репозиторий CrudRepository, который избавляет нас от составления простых запросов. В нем реализованы CRUD-операции для сущности:

  • сreate
  • read
  • update и
  • delete

Чтобы им воспользоваться, необходимо написать интерфейс, расширяющий CrudRepository или PagingAndSortingRepository:

public interface AnimalRepository extends PagingAndSortingRepository<Animal, Long> {
      
}
PagingAndSortingRepository расширяет CrudRepository и добавляет два метода: возможность выбирать сущность постранично и с сортировкой.

Воспользуемся репозиторием.
Для этого напишем тесты. В жизни тестировать готовый репозиторий не нужно, но поскольку у нас нет интерфейса, методы репозитория можно вызвать в тестах.

CrudRepositoryTest

Метод save() используется для добавления и редактирования сущности.

findById() – для поиска по id.

count() – для подсчета числа элементов в таблице.

Все методы CrudRepository являются @Transactional по умолчанию.

Продемонстрируем их работу:

@DataJdbcTest
public class AnimalRepositoryTest {
    @Autowired
    private AnimalRepository dao;

    @Test
    void givenId_whenFindThenReturnsAnimal() {
        Optional<Animal> optionalAnimal = dao.findById(1l);
        Assertions.assertTrue(!optionalAnimal.isEmpty());
        optionalAnimal.get().setName("cat");
    }

    @Test
    void givenAnimal_whenSaveThenReturnsAnimal() {
        Animal newAnimal=new Animal("mouse");
        Animal animal = dao.save(newAnimal);
        Assertions.assertSame(animal, newAnimal);
        Assertions.assertNotNull(animal.getId());
    }

    @Test
    void givenId_whenUpdateThenReturnsAnimal() {
        Optional<Animal> optionalAnimal = dao.findById(1l);
        optionalAnimal.get().setName("cat1");
        Animal updatedAnimal=dao.save(optionalAnimal.get());
        Assertions.assertEquals("cat1", updatedAnimal.getName());
    }

    @Test
    void whenCount_thenCountIsTwo() {
        long count = dao.count();
        Assertions.assertEquals(2 , count);
    }
}

Учтите, что при запуске тестов либо приложения база заполняется двумя животными (файл data.sql):

insert into animal (name) values ('cat');
insert into animal (name) values ('dog');
@id
Обратите внимание, что идентификатор должен быть аннотирован @id (org.springframework.data.annotation.id):
public class Animal {
    @Id
    private long id;
    private String name;

    //setters/getters/constructors
}
При добавлении в базу нового Animal метод save()  возвращает этот же объект Animal с заполненным сгенерированным id.
Без аннотации @id при сохранении новой записи возникает исключение.
@DataJdbcTest

@DataJdbcTest включает в контекст все бины, унаследованные от CrudRepository. Не унаследованный ни от чего бин @Repository (из предыдущей статьи) не создается, поэтому его приходилось включать в контекст отдельно.

Также @DataJdbcTest откатывает транзакции после каждого теста, приводя базу в первоначальное состояние.

Query Methods

Пока что наш интерфейс AnimalRepository был пустой. Но его можно дополнять и другими методами.

Derived Query Methods

Интересный нам запрос можно создать, просто продекларировав метод с правильным названием, включающим названия нужных полей. Spring Data создает реализацию метода по его названию.

Просто продекларируем в интерфейсе некоторые методы:

Animal findByName(String name);  //1

Animal findFirstByName(String name); //2

List<Animal> findByNameNotContaining(String str); (3)

(1) выбрасывает IncorrectResultSizeDataAccessException при неуникальном результате. И null, если Animal с таким именем не найдено.

(2) возвращает первую найденную запись с таким именем. И тоже null, если Animal с таким именем не найдено.

(3) возвращает список Animal, имя которых не содержит str.

Весь список поддерживаемых ключевых слов есть в таблице в документации.

Добавим тесты:

@Test
void givenName_whenFindBy_thenReturnsAnimal() {
    Animal animal = dao.findByName("cat");
    Assertions.assertEquals("cat", animal.getName());
}

@Test
void givenName_whenFindFirstBy_thenReturnsAnimal() {
    Animal animal = dao.findFirstByName("cat");
    Assertions.assertEquals("cat", animal.getName());
}

@Query

В AnimalRepository можно также добавить sql-запрос с помощью @Query:

@Query("select count(*) from animal")
int animalCount();

Этот метод приведен для примера, т.к. в CrudRepository уже реализован аналогичный count().

Исходный код

Код примера доступен на GitHub.

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *