Мы уже писали пользовательский репозиторий, в котором запросы работают через 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.