Мы уже писали пользовательский репозиторий, в котором запросы работают через JdbcTemplate. Также мы рассматривали базовый CrudRepository.
В этой статье мы объединим оба репозитория в единый интерфейс. Это позволит внедрять два репозитория в другие места единым бином, а не двумя отдельными бинами. Благодаря Spring это возможно.
Начнем с написания пользовательского репозитория — то есть репозитория, содержащего наши пользовательские собственноручно написанные методы. Хотя он уже и написан, но теперь будут важны названия.
Custom repository
Пользовательский репозиторий должен реализовывать пользовательский интерфейс. Пусть наш интерфейс называется CustomAnimalRepository:
public interface CustomAnimalRepository { Animal getById(long id); Animal insert(Animal animal); Animal update(Animal animal); List<Animal> getAll(); }
Соглашение о наименовании
Тогда реализация интерфейса должна называться CustomAnimalRepositoryImpl:
@AllArgsConstructor public class CustomAnimalRepositoryImpl implements CustomAnimalRepository { private final NamedParameterJdbcTemplate jdbc; @Override @Transactional public Animal insert(Animal animal) { Map<String, Object> params = new HashMap<>(); params.put("name", animal.getName()); SqlParameterSource paramSource = new MapSqlParameterSource(params); GeneratedKeyHolder holder = new GeneratedKeyHolder(); jdbc.update("insert into animal (name) values (:name)", paramSource, holder); animal.setId(holder.getKey().longValue()); return animal; } //другие методы, смотрите на GitHub }
Базовый репозиторий
Базовый репозиторий (расширяющий CrudRepository и декларирующий Query methods) выглядит так:
public interface AnimalRepository extends PagingAndSortingRepository<Animal, Long> { Animal findByName(String name); //другие декларации }
Объединение репозиториев
Чтобы объединить его с пользовательским, заставим его расширять CustomAnimalRepository:
public interface AnimalRepository extends PagingAndSortingRepository<Animal, Long>, CustomAnimalRepository { Animal findByName(String name); //другие декларации }
Теперь AnimalRepository можно внедрять в бины и использовать методы из обоих репозиториев.
Например, в тесте вызываются методы:
@DataJdbcTest class AnimalRepositoryTest { @Autowired private AnimalRepository dao; // пользовательский метод @Test void givenAnimal_whenInsert_thenReturnsAnimal() { Animal animal = dao.insert(new Animal("mouse")); Assertions.assertEquals("mouse", animal.getName()); } //Query method @Test void givenName_whenFindBy_thenReturnsAnimal() { Animal animal = dao.findByName("cat"); Assertions.assertEquals("cat", animal.getName()); } }
Как Spring выбирает метод интерфейса
Если методы конфликтуют (одинаковые названия и сигнатуры в разных местах), то в Spring в первую очередь выбирает:
- пользовательский метод,
- затем Query Method,
- и только если не найдено метода в предыдущих двух местах, берется метод из CrudRepository.
Под капотом для вызова метода Spring использует Spring’s ProxyFactory API и MethodInterceptor, подробнее на Stackoverflow.
Исходный код
Код примера есть на GitHub.