1. введение
При моделировании реальной системы или процесса хорошим вариантом являются репозитории в стиле доменного проектирования (DDD). Именно для этой цели мы можем использовать Spring Data JPA в качестве уровня абстракции доступа к данным.
Если вы новичок в этой концепции, ознакомьтесь с этим вводным руководством, которое поможет вам войти в курс дела.
В этом уроке мы сосредоточимся на концепции создания пользовательских, а также составных репозиториев, которые создаются с использованием небольших репозиториев, называемых фрагментами.
2. Зависимости Maven
Возможность создания составных репозиториев доступна начиная с весны 5.
Давайте добавим необходимую зависимость для Spring Data JPA:
org.springframework.data spring-data-jpa 2.2.2.RELEASE
Нам также нужно будет настроить источник данных, чтобы наш уровень доступа к данным работал. Это хорошая идея-создать базу данных в памяти, такую как H2, для разработки и быстрого тестирования.
3. Предыстория
3.1. Спящий режим как реализация JPA
Spring Data JPA по умолчанию использует Hibernate в качестве реализации JPA. Мы можем легко спутать одно с другим или сравнить их, но они служат разным целям.
Spring Data JPA-это уровень абстракции доступа к данным, ниже которого мы можем использовать любую реализацию. Мы могли бы, например, отключить режим гибернации в пользу EclipseLink .
3.2. Репозитории по умолчанию
Во многих случаях нам не нужно было бы самим писать какие-либо запросы.
Вместо этого нам нужно только создать интерфейсы, которые, в свою очередь, расширяют общие интерфейсы хранилища данных Spring:
public interface LocationRepository extends JpaRepository{ }
И это само по себе позволило бы нам выполнять общие операции – CRUD, подкачка и сортировка – на объекте Location , который имеет первичный ключ типа Long .
Кроме того, Spring Data JPA оснащен механизмом построения запросов, который обеспечивает возможность генерировать запросы от нашего имени, используя соглашения об именах методов:
public interface StoreRepository extends JpaRepository{ List findStoreByLocationId(Long locationId); }
3.3. Пользовательские репозитории
При необходимости мы можем обогатить наш репозиторий моделей , написав интерфейс фрагмента и реализовав желаемую функциональность. Затем это может быть введено в наш собственный репозиторий JPA.
Например, здесь мы обогащаем наш Репозиторий типов элементов путем расширения репозитория фрагментов:
public interface ItemTypeRepository extends JpaRepository, CustomItemTypeRepository { }
Здесь CustomItemTypeRepository это другой интерфейс:
public interface CustomItemTypeRepository { void deleteCustomById(ItemType entity); }
Его реализация может быть хранилищем любого типа, а не только JPA:
public class CustomItemTypeRepositoryImpl implements CustomItemTypeRepository { @Autowired private EntityManager entityManager; @Override public void deleteCustomById(ItemType itemType) { entityManager.remove(itemType); } }
Нам просто нужно убедиться, что у него есть постфикс Impl . Однако мы можем установить пользовательский постфикс, используя следующую конфигурацию XML:
или с помощью этой аннотации:
@EnableJpaRepositories( basePackages = "com.baeldung.repository", repositoryImplementationPostfix = "CustomImpl")
4. Создание Репозиториев С Использованием Нескольких Фрагментов
До нескольких выпусков назад мы могли расширять наши интерфейсы репозитория только с помощью одной пользовательской реализации. Это было ограничение, из-за которого нам пришлось бы объединить все связанные функции в один объект.
Излишне говорить, что для более крупных проектов со сложными моделями предметной области это приводит к раздутым классам.
Теперь, с весной 5, у нас есть возможность обогатить наш репозиторий JPA несколькими репозиториями фрагментов . Опять же, остается требование, чтобы у нас были эти фрагменты в виде пар интерфейс-реализация.
Чтобы продемонстрировать это, давайте создадим два фрагмента:
public interface CustomItemTypeRepository { void deleteCustom(ItemType entity); void findThenDelete(Long id); } public interface CustomItemRepository { Item findItemById(Long id); void deleteCustom(Item entity); void findThenDelete(Long id); }
Конечно, нам нужно было бы написать их реализации. Но вместо того, чтобы подключать эти пользовательские репозитории – со связанными функциями – в их собственные репозитории JPA, мы можем расширить функциональность одного репозитория JPA:
public interface ItemTypeRepository extends JpaRepository, CustomItemTypeRepository, CustomItemRepository { }
Теперь у нас будет вся связанная функциональность в одном репозитории.
5. Работа с двусмысленностью
Поскольку мы наследуем от нескольких репозиториев, у нас могут возникнуть проблемы с определением того, какая из наших реализаций будет использоваться в случае столкновения. Например, в нашем примере оба хранилища фрагментов имеют метод find Then Delete () с одинаковой сигнатурой.
В этом сценарии порядок объявления интерфейсов используется для устранения неоднозначности . Следовательно, в нашем случае метод внутри Репозитория пользовательских типов элементов будет использоваться с момента его объявления первым.
Мы можем проверить это с помощью этого тестового случая:
@Test public void givenItemAndItemTypeWhenDeleteThenItemTypeDeleted() { OptionalitemType = composedRepository.findById(1L); assertTrue(itemType.isPresent()); Item item = composedRepository.findItemById(2L); assertNotNull(item); composedRepository.findThenDelete(1L); Optional sameItemType = composedRepository.findById(1L); assertFalse(sameItemType.isPresent()); Item sameItem = composedRepository.findItemById(2L); assertNotNull(sameItem); }
6. Заключение
В этой статье мы рассмотрели различные способы, с помощью которых мы можем использовать репозитории Spring Data JPA. Мы видели, что Spring упрощает выполнение операций с базами данных над объектами нашего домена без написания большого количества кода или даже SQL-запросов.
Эта поддержка значительно настраивается за счет использования составных репозиториев.
Фрагменты кода из этой статьи доступны в качестве проекта Maven здесь, на GitHub .