что такое репозиторий java

Шаблоны DAO против репозитория

Поймите разницу между шаблонами DAO и репозиторием на примере Java.

1. Обзор

Часто реализации репозитория и DAO считаются взаимозаменяемыми, особенно в приложениях, ориентированных на данные. Это создает путаницу в их различиях.

В этой статье мы обсудим различия между шаблонами DAO и репозиториями.

2. Шаблон ДАО

Поэтому во многих случаях наши DAO соответствуют таблицам базы данных, позволяя более простой способ отправки/извлечения данных из хранилища, скрывая уродливые запросы.

Давайте рассмотрим простую реализацию шаблона DAO.

2.1. Пользователь

Во-первых, давайте создадим базовый класс User domain:

2.2. UserDao

2.3. UserDaoImpl

3. Шаблон репозитория

Согласно книге Эрика Эванса Domain-Driven Design , репозиторий ” – это механизм для инкапсуляции поведения хранения, поиска и поиска, который эмулирует коллекцию объектов.”

Другими словами, репозиторий также имеет дело с данными и скрывает запросы, аналогичные DAO. Однако он находится на более высоком уровне, ближе к бизнес-логике приложения.

Следовательно, репозиторий может использовать DAO для извлечения данных из базы данных и заполнения объекта домена. Или он может подготовить данные из объекта домена и отправить их в систему хранения, используя DAO для сохранения.

3.1. Информация о пользователе

Во-первых, давайте создадим интерфейс UserRepository :

3.2. UserRepositoryImpl

Здесь мы использовали UserDaoImpl для отправки/извлечения данных из базы данных.

До сих пор мы можем сказать, что реализации DAO и репозитория выглядят очень похожими, потому что класс User является анемичным доменом. Кроме того, репозиторий-это просто еще один слой поверх уровня доступа к данным (DAO).

4. Шаблон Репозитория С Несколькими DAO

Чтобы четко понять последнее утверждение, давайте расширим наш User домен для обработки бизнес-прецедента.

Представьте, что мы хотим подготовить профиль пользователя в социальных сетях, объединив его твиты в Twitter, сообщения в Facebook и многое другое.

4.1. Твит

Во-первых, мы создадим класс Tweet с несколькими свойствами, которые содержат информацию о твите:

4.2. TweetDao и TweetDaoImpl

Здесь мы вызовем API Twitter, чтобы получить все твиты пользователя, используя его электронную почту.

Таким образом, в этом случае DAO предоставляет механизм доступа к данным с использованием сторонних API.

4.3. Расширение домена Пользователя

4.4. UserRepositoryImpl

Здесь UserRepositoryImpl извлекает данные пользователя с помощью UserDaoImpl и твиты пользователя с помощью TweetDaoImpl.

Аналогичным образом, мы можем расширить ваш Пользователь домен, чтобы сохранить список сообщений Facebook.

5. Сравнение двух моделей

Теперь, когда мы рассмотрели нюансы шаблонов DAO и репозитория, давайте обобщим их различия:

Кроме того, если у нас есть анемичный домен, репозиторий будет просто DAO.

Кроме того, шаблон репозитория поощряет доменный дизайн, обеспечивая легкое понимание структуры данных и для нетехнических членов команды|/.

6. Заключение

В этой статье мы исследовали различия между шаблонами DAO и репозиториями.

Во-первых, мы рассмотрели базовую реализацию шаблона DAO. Затем мы увидели аналогичную реализацию с использованием шаблона репозитория.

Наконец, мы рассмотрели репозиторий, использующий несколько DAO, расширяющий возможности домена для решения бизнес-задач.

Таким образом, мы можем сделать вывод, что шаблон репозитория доказывает лучший подход, когда приложение переходит от ориентированного на данные к бизнес-ориентированному.

Источник

maestrow / repository.md

Паттерн Репозиторий стал популярным благодаря DDD (Domain Driven Design). В противоположность к Database Driven Design в DDD разработка начинается с проектирования бизнес логики, принимая во внимание только особенности предметной области и игнорируя все, что связано с особенностями базы данных или других способов хранения данных. Способ хранения бизнес объектов реализуется во вторую очередь.

Применение данного паттерна не предполагает создание только одного объекта репозитория во всем приложении. Хорошей практикой считается создание отдельных репозиториев для каждого бизнес-объекта или контекста, например: OrdersRepository, UsersRepository, AdminRepository.

Generic Repository это антипаттерн

Возможно у вас возник вопрос: Зачем использовать репозиторий, если я использую ORM?

Действительно, ORM позволяет:

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

Преимущества паттерна Репозиторий

Репозиторий и DAL (Data Access Layer, Persistence Layer)

Источник: Серия статей by Mike Mogosanu из его блога https://blog.sapiensworks.com:

На момент написания этой заметки статьи Mike размещались в категории Repository, затем он переделал свой блог и эти статьи попали в категорию Best Practces, что охватывает и другие вопросы, выходящие за рамки темы Паттерн Репозиторий. Поэтому привожу здесь ссылку на версию статей 2014 года.

Источник

Spring Data JPA

В статье опишу использование Spring Data.

Spring Data — дополнительный удобный механизм для взаимодействия с сущностями базы данных, организации их в репозитории, извлечение данных, изменение, в каких то случаях для этого будет достаточно объявить интерфейс и метод в нем, без имплементации.

1. Spring Repository

Основное понятие в Spring Data — это репозиторий. Это несколько интерфейсов которые используют JPA Entity для взаимодействия с ней. Так например интерфейс
public interface CrudRepository extends Repository
обеспечивает основные операции по поиску, сохранения, удалению данных (CRUD операции)

Есть и другие абстракции, например PagingAndSortingRepository.

Т.е. если того перечня что предоставляет интерфейс достаточно для взаимодействия с сущностью, то можно прямо расширить базовый интерфейс для своей сущности, дополнить его своими методами запросов и выполнять операции. Сейчас я покажу коротко те шаги что нужны для самого простого случая (не отвлекаясь пока на конфигурации, ORM, базу данных).

1. Создаем сущность

2. Наследоваться от одного из интерфейсов Spring Data, например от CrudRepository

3. Использовать в клиенте (сервисе) новый интерфейс для операций с данными

Здесь я воспользовался готовым методом findById. Т.е. вот так легко и быстро, без имплементации, получим готовый перечень операций из CrudRepository:

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

2. Методы запросов из имени метода

Запросы к сущности можно строить прямо из имени метода. Для этого используется механизм префиксов find…By, read…By, query…By, count…By, и get…By, далее от префикса метода начинает разбор остальной части. Вводное предложение может содержать дополнительные выражения, например, Distinct. Далее первый By действует как разделитель, чтобы указать начало фактических критериев. Можно определить условия для свойств сущностей и объединить их с помощью And и Or. Примеры

В документации определен весь перечень, и правила написания метода. В качестве результата могут быть сущность T, Optional, List, Stream. В среде разработки, например в Idea, есть подсказка для написания методов запросов.

что такое репозиторий java. Смотреть фото что такое репозиторий java. Смотреть картинку что такое репозиторий java. Картинка про что такое репозиторий java. Фото что такое репозиторий java
Достаточно только определить подобным образом метод, без имплементации и Spring подготовит запрос к сущности.

3. Конфигурация и настройка

Весь проект доступен на github
github DemoSpringData

Здесь лишь коснусь некоторых особенностей.

В context.xml определенны бины transactionManager, dataSource и entityManagerFactory. Важно указать в нем также

путь где определены репозитории.

EntityManagerFactory настроен на работу с Hibernate ORM, а он в свою очередь с БД Oracle XE, тут возможны и другие варианты, в context.xml все это видно. В pom файле есть все зависимости.

4. Специальная обработка параметров

В методах запросов, в их параметрах можно использовать специальные параметры Pageable, Sort, а также ограничения Top и First.

5. Пользовательские реализации для репозитория

Предположим что в репозиторие нужен метод, который не получается описать именем метода, тогда можно реализовать с помощью своего интерфейса и класса его имплементирующего. В примере ниже добавлю в репозиторий метод получения сотрудников с максимальной оплатой труда.

Имплементирую интерфейс. С помощью HQL (SQL) получаю сотрудников с максимальной оплатой, возможны и другие реализации.

А также расширяю Crud Repository Employees еще и CustomizedEmployees.

Здесь есть одна важная особенность. Класс имплементирующий интерфейс, должен заканчиваться (postfix) на Impl, или в конфигурации надо поставить свой postfix

Проверяем работу этого метода через репозиторий

Другой случай, когда надо изменить поведение уже существующего метода в интерфейсе Spring, например delete в CrudRepository, мне надо что бы вместо удаления из БД, выставлялся признак удаления. Техника точно такая же. Ниже пример:

Теперь если в employeesCrudRepository вызвать delete, то объект будет только помечен как удаленный.

6. Пользовательский Базовый Репозиторий

В предыдущем примере я показал как переопределить delete в Crud репозитории сущности, но если это надо делать для всех сущностей проекта, делать для каждой свой интерфейс как то не очень. тогда в Spring data можно настроить свой базовый репозиторий. Для этого:
Объявляется интерфейс и в нем метод для переопределения (или общий для всех сущностей проекта). Тут я еще для всех своих сущностей ввел свой интерфейс BaseEntity (это не обязательно), для удобства вызова общих методов, его методы совпадают с методами сущности.

В конфигурации надо указать этот базовый репозиторий, он будет общий для всех репозиториев проекта

Теперь Employees Repository (и др.) надо расширять от BaseRepository и уже его использовать в клиенте.

Проверяю работу EmployeesBaseRepository

Теперь также как и ранее, объект будет помечен как удаленный, и это будет выполняться для всех сущностей, которые расширяют интерфейс BaseRepository. В примере был применен метод поиска — Query by Example (QBE), я не буду здесь его описывать, из примера видно что он делает, просто и удобно.

7. Методы запросов — Query

Ранее я писал, что если нужен специфичный метод или его реализация, которую нельзя описать через имя метода, то это можно сделать через некоторый Customized интерфейс ( CustomizedEmployees) и сделать реализацию вычисления. А можно пойти другим путем, через указание запроса (HQL или SQL), как вычислить данную функцию.
Для моего примера c getEmployeesMaxSalary, этот вариант реализации даже проще. Я еще усложню его входным параметром salary. Т.е. достаточно объявить в интерфейсе метод и запрос вычисления.

Упомяну лишь еще, что запросы могут быть и модифицирующие, для этого к ним добавляется еще аннотация @Modifying

Так например в моем гипотетическом примере, когда мне надо для всех сущностей иметь признак “удален», я сделаю базовый интерфейс с методом получения списка объектов с признаком «удален» или «активный»

Далее все репозитории для сущностей можно расширять от него. Интерфейсы которые не являются репозиториями, но находятся в «base-package» папке конфигурации, надо аннотировать @NoRepositoryBean.

Теперь когда будет выполняться запрос, в тело запроса будет подставлено имя сущности T для конкретного репозитория который будет расширять ParentEntityRepository, в данном случае Employees.

Источник

Разбираемся, как работает Spring Data Repository, и создаем свою библиотеку по аналогии

На habr уже была статья о том, как создать библиотеку в стиле Spring Data Repository (рекомендую к чтению), но способы создания объектов довольно сильно отличаются от «классического» подхода, используемого в Spring Boot. В этой статье я постарался быть максимально близким к этому подходу. Такой способ создания бинов (beans) применяется не только в Spring Data, но и, например, в Spring Cloud OpenFeign.

Содержание

Итак, у нас прошли праздники, но мы хотим иметь возможность создавать на лету бины (beans), которые позволили бы нам поздравлять всех, кого мы в них перечислим.

При вызове метода мы хотим получать:

Т.е. мы должны найти все интерфейсы, которые расширяют интерфейс Congratulator или имеют аннотацию @Congratulate

@Enable

Как и любая взрослая библиотека у нас будет аннотация, которая включает наш механизм (как @EnableFeignClients и @EnableJpaRepositories ).

Напишем свою аннотацию

ImportBeanDefinitionRegistrar

Посмотрим, что происходит в ImportBeanDefinitionRegistrar у Spring Cloud Feign:

В Spring Cloud OpenFeign сначала создаются бины конфигурации, затем выполняется поиск кандидатов и для каждого кандидата создается Factory.

В Spring Data подход аналогичный, но так как Spring Data состоит из множества модулей, то основные моменты разнесены по разным классам (см. например org.springframework.data.repository.config.RepositoryBeanDefinitionBuilder#build )

Можно заметить, что сначала создаются Factory, а не сами bean. Это происходит потому, что мы не можем в BeanDefinitionHolder описать, как должен работать наш bean.

Сделаем по аналогии наш класс (полный код класса можно посмотреть здесь)

ResourceLoaderAware и EnvironmentAware используется для получения объектов класса ResourceLoader и Environment соответственно. При создании экземпляра CongratulatorsRegistrar Spring вызовет соответствующие set-методы.

Чтобы найти требуемые нам интерфейсы, используется следующий код:

Что, если мы хотим иметь возможность получать наши beans по имени, например, так

это тоже возможно, так как во время создания Factory в качестве alias было передано имя bean ( AnnotationBeanNameGenerator.INSTANCE.generateBeanName(candidateComponent, registry) )

FactoryBean

Теперь займемся Factory.

Стандартный интерфейс FactoryBean имеет 2 метода, которые нужно имплементировать

Заметим, что есть возможность указать, является ли объект, который будет создаваться, Singleton или нет.

Есть абстрактный класс ( AbstractFactoryBean ), который расширяет интерфейс дополнительной логикой (например, поддержка destroy-методов). Он так же имеет 2 абстрактных метода

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

Сначала создадим обработчик для каждого метода:

Теперь при старте контекста Spring создает бины (beans) на основе наших интерфейсов.

Источник

Абстрактный CRUD от репозитория до контроллера: что ещё можно сделать при помощи Spring + Generics

Совсем недавно на Хабре мелькнула статья коллеги, который описал довольно интересный подход к совмещению Generics и возможностей Spring. Мне она напомнила один подход, который я использую для написания микросервисов, и именно им я решил поделиться с читателями.

что такое репозиторий java. Смотреть фото что такое репозиторий java. Смотреть картинку что такое репозиторий java. Картинка про что такое репозиторий java. Фото что такое репозиторий java

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

Сразу ресурсы.
Ветка, как я не делаю: standart_version.
Подход, о котором рассказывается в статье, в ветке abstract_version.

Я собрал проект через Spring Initializr, добавив фреймворки JPA, Web и H2. Gradle, Spring Boot 2.0.5. Этого будет вполне достаточно.

что такое репозиторий java. Смотреть фото что такое репозиторий java. Смотреть картинку что такое репозиторий java. Картинка про что такое репозиторий java. Фото что такое репозиторий java

Для начала, рассмотрим классический вариант транспорта от контроллера до репозитория и обратно, лишённый всякой дополнительной логики. Если же Вы хотите перейти к сути подхода, проматывайте вниз до абстрактного варианта. Но, всё же, рекомендую прочитать статью целиком.

Классический вариант.

В ресурсах примера представлены несколько сущностей и методов для них, но в статье пусть у нас будет только одна сущность User и только один метод save(), который мы протащим от репозитория через сервис до контроллера. В ресурсах же их 7, а вообще Spring CRUD / JPA Repository позволяют использовать около дюжины методов сохранения / получения / удаления плюс Вы можете пользоваться, к примеру, какими-то своими универсальными. Также, мы не будем отвлекаться на такие нужные вещи, как валидацию, мапинг dto и прочее. Это Вы сможете дописать сами или изучить в других статьях Хабра.

Domain:

Repository:

Service:

Service (имплементация):

Controller:

У нас получился некий набор зависимых классов, которые помогут нам оперировать сущностью User на уровне CRUD. Это в нашем примере по одному методу, в ресурсах их больше. Этот нисколько не абстрактный вариант написания слоёв представлен в ветке standart_version.

Допустим, нам нужно добавить ещё одну сущность, скажем, Car. Мапить на уровне сущностей мы их друг к другу не будем (если есть желание, можете замапить).

Для начала, создаём сущность.

Потом создаём репозиторий.

Потом имплементация сервиса…… Контроллер………

Да, можно просто скопипастить те же методы (они же у нас универсальные) из класса User, потом поменять User на Car, потом проделать то же самое с имплементацией, с контроллером, далее на очереди очередная сущность, а там уже выглядывают ещё и ещё… Обычно устаёшь уже на второй, создание же служебной архитектуры для пары десятков сущностей (копипастинг, замена имени сущности, где-то ошибся, где-то опечатался. ) приводит к мукам, которые вызывает любая монотонная работа. Попробуйте как-нибудь на досуге прописать двадцать сущностей и Вы поймёте, о чём я.

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

Итак, абстракции на основе типовых параметров.

Смысл данного подхода заключается в том, чтобы вынести всю логику в абстракцию, абстракцию привязать к типовым параметрам интерфейса, а в бины инжектить другие бины. И всё. Никакой логики в бинах. Только инжект других бинов. Этот подход предполагает написать архитектуру и логику один раз и не дублировать её при добавлении новых сущностей.

Начнём с краеугольного камня нашей абстракции — абстрактной сущности. Именно с неё начнётся цепочка абстрактных зависимостей, которая послужит каркасом сервиса.

У всех сущностей есть как минимум одно общее поле (обычно больше). Это ID. Вынесем это поле в отдельную абстрактную сущность и унаследуем от неё User и Car.

AbstractEntity:

Не забудьте пометить абстракцию аннотацией @MappedSuperclass — Hibernate тоже должен узнать, что это абстракция.

С Car, соответственно, то же самое.

В каждом слое у нас, помимо бинов, будет один интерфейс с типовыми параметрами и один абстрактный класс с логикой. Кроме репозитория — благодаря специфике Spring Data JPA, здесь всё будет намного проще.

Первое, что нам потребуется в репозитории — общий репозиторий.

CommonRepository:

В этом репозитории мы задаём общие правила для всей цепочки: все сущности, участвующие в ней, будут наследоваться от абстрактной. Далее, для каждой сущности мы должны написать свой репозиторий-интерфейс, в котором обозначим, с какой именно сущностью будет работать эта цепочка репозиторий-сервис-контроллер.

UserRepository:

На этом, благодаря особенностям Spring Data JPA, настройка репозитория заканчивается — всё будет работать и так. Далее следует сервис. Мы должны создать общий интерфейс, абстракцию и бин.

CommonService:

AbstractService:

Здесь мы переопределяем все методы, а также, создаём параметризированный конструктор для будущего репозитория, который мы переопределим в бине. Таким образом, мы уже используем репозиторий, который мы ещё не определили. Мы пока не знаем, какая сущность будет обработана в этой абстракции и какой репозиторий нам потребуется.

UserService:

В бине мы делаем заключительную вещь — явно определяем нужный нам репозиторий, который потом вызовется в конструкторе абстракции. И всё.

При помощи интерфейса и абстракции мы создали магистраль, по которой будем гонять все сущности. В бине же мы подводим к магистрали развязку, по которой будем выводить нужную нам сущность на магистраль.

Контроллер строится по тому же принципу: интерфейс, абстракция, бин.

CommonController:

AbstractController:

UserController:

Это вся структура. Она пишется один раз.

Что дальше?

И вот теперь давайте представим, что у нас появилась новая сущность, которую мы уже унаследовали от AbstractEntity, и нам нужно прописать для неё такую же цепочку. На это у нас уйдёт минута. И никаких копипаст и исправлений.

Возьмём уже унаследованный от AbstractEntity Car.

CarRepository:

CarService:

CarController:

Как мы видим, копирование одинаковой логики состоит в простом добавлении бина. Не надо заново писать логику в каждом бине с изменением параметров и сигнатур. Они написаны один раз и работают в каждом последующем случае.

Заключение

Конечно, в примере описана этакая сферическая ситуация, в которой CRUD для каждой сущности имеет одинаковую логику. Так не бывает — какие-то методы Вам всё равно придётся переопределять в бине или добавлять новые. Но это будет происходить от конкретных потребностей обработки сущности. Хорошо, если процентов 60 от общего количества методов CRUD будет оставаться в абстракции. И это будет хорошим результатом, потому что чем больше мы генерим лишнего кода вручную, тем больше времени мы тратим на монотонную работу и тем выше риск ошибки или опечатки.

Надеюсь, статья была полезна, спасибо за внимание.

Источник

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

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