1. Введение
При моделировании реальной системы или процесса репозитории в стиле доменно-ориентированного проектирования (DDD) являются хорошим вариантом. Именно для этой цели мы можем использовать Spring Data JPA в качестве уровня абстракции доступа к данным.
Если вы новичок в этой концепции, ознакомьтесь с этим вводным руководством , которое поможет вам освоиться.
В этом руководстве мы сосредоточимся на концепции создания пользовательских, а также составных репозиториев, которые создаются с использованием меньших репозиториев, называемых фрагментами.
2. Зависимости Maven
Возможность создания компонуемых репозиториев доступна начиная с Spring 5.
Давайте добавим необходимую зависимость для Spring Data JPA:
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-jpa</artifactId>
<version>2.2.2.RELEASE</version>
</dependency>
Нам также нужно настроить источник данных, чтобы наш уровень доступа к данным работал. Рекомендуется настроить базу данных в памяти, такую как H2, для разработки и быстрого тестирования.
3. Фон
3.1. Hibernate как реализация JPA
Spring Data JPA по умолчанию использует Hibernate в качестве реализации JPA. Мы можем легко спутать одно с другим или сравнить их, но они служат разным целям.
Spring Data JPA — это уровень абстракции доступа к данным, ниже которого мы можем использовать любую реализацию. Мы могли бы, например, заменить Hibernate на EclipseLink .
3.2. Репозитории по умолчанию
Во многих случаях нам не нужно было бы писать какие-либо запросы самостоятельно.
Вместо этого нам нужно только создать интерфейсы, которые, в свою очередь, расширят общие интерфейсы репозитория данных Spring:
public interface LocationRepository extends JpaRepository<Location, Long> {
}
И это само по себе позволило бы нам выполнять стандартные операции — CRUD, пейджинг и сортировку — над объектом Location
, который имеет первичный ключ типа Long
.
Кроме того, Spring Data JPA оснащен механизмом построения запросов, который позволяет генерировать запросы от нашего имени, используя соглашения об именах методов:
public interface StoreRepository extends JpaRepository<Store, Long> {
List<Store> findStoreByLocationId(Long locationId);
}
3.3. Пользовательские репозитории
При необходимости мы можем обогатить наш репозиторий моделей , написав интерфейс фрагмента и реализовав желаемую функциональность. Затем это можно внедрить в наш собственный репозиторий JPA.
Например, здесь мы обогащаем наш ItemTypeRepository
, расширяя репозиторий фрагментов:
public interface ItemTypeRepository
extends JpaRepository<ItemType, Long>, 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:
<repositories base-package="com.foreach.repository" repository-impl-postfix="CustomImpl" />
или с помощью этой аннотации:
@EnableJpaRepositories(
basePackages = "com.foreach.repository", repositoryImplementationPostfix = "CustomImpl")
4. Составление репозиториев с использованием нескольких фрагментов
До нескольких выпусков назад мы могли расширять интерфейсы нашего репозитория только с помощью одной пользовательской реализации. Это было ограничением, из-за которого нам пришлось бы объединить всю связанную функциональность в один объект.
Излишне говорить, что для более крупных проектов со сложными моделями предметной области это приводит к раздуванию классов.
Теперь с Spring 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<ItemType, Long>, CustomItemTypeRepository, CustomItemRepository {
}
Теперь у нас будет вся связанная функциональность в одном репозитории.
5. Работа с двусмысленностью
Поскольку мы наследуем из нескольких репозиториев, у нас могут возникнуть проблемы с определением того, какая из наших реализаций будет использоваться в случае конфликта. Например, в нашем примере оба репозитория фрагментов имеют метод findThenDelete()
с одинаковой сигнатурой.
В этом сценарии порядок объявления интерфейсов используется для устранения неоднозначности . Следовательно, в нашем случае будет использоваться метод внутри CustomItemTypeRepository
, поскольку он объявлен первым.
Мы можем проверить это, используя этот тестовый пример:
@Test
public void givenItemAndItemTypeWhenDeleteThenItemTypeDeleted() {
Optional<ItemType> itemType = composedRepository.findById(1L);
assertTrue(itemType.isPresent());
Item item = composedRepository.findItemById(2L);
assertNotNull(item);
composedRepository.findThenDelete(1L);
Optional<ItemType> sameItemType = composedRepository.findById(1L);
assertFalse(sameItemType.isPresent());
Item sameItem = composedRepository.findItemById(2L);
assertNotNull(sameItem);
}
6. Заключение
В этой статье мы рассмотрели различные способы использования репозиториев Spring Data JPA. Мы увидели, что Spring упрощает выполнение операций с базой данных над объектами предметной области без написания большого количества кода или даже SQL-запросов.
Эта поддержка значительно настраивается за счет использования компонуемых репозиториев.
Фрагменты кода из этой статьи доступны в виде проекта Maven здесь, на GitHub .