что такое jpa repository

Spring Data JPA: что такое хорошо, и что такое плохо

Крошка-сын к отцу пришел
И спросила кроха
— Что такое хорошо
и что такое плохо

Владимир Маяковский

Эта статья о Spring Data JPA, а именно в подводных граблях, встретившихся на моём пути, ну и конечно же немного о производительности.

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

select t.* from t where t.id in (. )

Один из наиболее распространённых запросов — это запрос вида «выбери все записи, у которых ключ попадает в переданное множество». Уверен, почти все из вас писали или видели что-то вроде

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

Недостаток заключается в использовании слишком «узкого» интерфейса для передачи ключей. «Ну и?» — скажете вы. «Ну список, ну набор, я не вижу здесь проблемы». Однако, если мы посмотрим на методы корневого интерфейса, принимающие множество значений, то везде увидим Iterable :

«Ну и что? А я хочу список. Чем он хуже?»
Ни чем не хуже, только будьте готовы к появлению на вышестоящем уровне вашего приложения подобного кода:

Этот код не делает ничего, кроме перезаворачивания коллекций. Может получиться так, что аргументом метода будет список, а репозиторный метод принимает набор (или наоборот), и перезаворачивать придётся просто для прохода компиляции. Разумеется это не станет проблемой на фоне накладных расходов на сам запрос, речь скорее о ненужных телодвижениях.

Поэтому хорошей практикой является использование Iterable :

Лишний код: неповторяющиеся ключи

В продолжение прошлого раздела хочу обратить внимание на распространённое заблуждение:

Другие проявления этого же заблуждения:

На первый взгляд, ничего необычного, верно?

который всегда вернёт одно и тоже безотносительно наличия повторов в аргументе. Поэтому обеспечивать уникальность ключей не нужно. Есть один особый случай — «Оракл», где попадание >1000 ключей в in приводит к ошибке. Но если вы пытаетесь уменьшить количество ключей исключением повторов, то стоит скорее задуматься о причине их возникновения. Скорее всего ошибка где-то уровнем выше.

Итого, в хорошем коде используйте Iterable :

Самопись

Внимательно посмотрите на этот код и найдите здесь три недостатка и одну возможную ошибку:

Гарри Поттер и составной ключ

Взгляните на два примера и выберите предпочтительный для вас:

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

В логе запросов (вы ведёте его, не так ли?) увидим вот это:

Теперь второй пример

Журнал запросов выглядит иначе:

Вот и вся разница: в первом случае всегда получаем 1 запрос, во втором — n запросов.
Причина этого поведения кроется в SimpleJpaRepository::findAllById :

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

Лишний CrudRepository::save

Часто в коде встречается такой антипаттерн:

In the copyValues method call, the hydrated state is copied again, so a new array is redundantly created, therefore wasting CPU cycles. If the entity has child associations and the merge operation is also cascaded from parent to child entities, the overhead is even greater because each child entity will propagate a MergeEvent and the cycle continues.

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

Конечно, постоянно держать это в голове при разработке и вычитке чужого кода неудобно, поэтому нам хотелось бы внести изменения на уровне каркаса, чтобы метод JpaRepository::save утратил свои вредные свойства. Возможно ли это?

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

Теперь положим, что к счёту привязан его владелец:

Существует метод, позволяющий открепить пользователя от счёта и передать последний новому пользователю:

Что произойдёт теперь? Проверка em.contains(entity) вернёт истину, а значит em.merge(entity) не будет вызван. Если ключ сущности User создаётся на основе последовательности (один из наиболее частых случаев), то он не будет создан вплоть до завершения транзакции (или ручного вызова Session::flush ) т. е. пользователь будет пребывать в состоянии DETACHED, а его родительская сущность (счёт) — в состоянии PERSISTENT. В некоторых случаях это может сломать логику приложения, что и произошло:

«Слепой» CrudRepository::findById

Продолжаем рассматривать всё ту же модель данных:

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

С версией 2.* указанный стрелкой антипаттерн не так бросается в глаза — чётче его видно на старых версиях:

Первым запросом мы достаём пользователя по ключу. Дальше получаем из базы ключ для новорожденного счёта и вставляем его в таблицу. И единственное, что мы берём от пользователя — это ключ, который у нас и так есть в виде аргумента метода. С другой стороны, BankAccount содержит поле «пользователь» и оставить его пустым мы не можем (как порядочные люди мы выставили ограничение в схеме). Опытные разработчики наверняка уже видят способ и рыбку съесть, и на лошадке покататься и пользователя получить, и запрос не делать:

JpaRepository::getOne возвращает обёртку над ключом, имеющую тот же тип, что и живая «сущность». Этот код даёт всего два запроса:

Когда создаваемая сущность содержит множество полей с отношением «многие к одному» / «один к одному» этот приём поможет ускорить сохранение и снизить нагрузку на базу.

Исполнение HQL запросов

Это отдельная и интересная тема :). Доменная модель та же и есть такой запрос:

Рассмотрим «чистый» HQL:

При его исполнении будет создан вот такой SQL запрос:

Проблема здесь не сразу бросается в глаза даже умудрённым жизнью и хорошо понимающим SQL разработчикам: inner join по ключу пользователя исключит из выборки счета с отсутствующим user_id (а по-хорошему вставка таковых должна быть запрещена на уровне схемы), а значит присоединять таблицу user вообще не нужно. Запрос может быть упрощён (и ускорен):

Существует способ легко добиться этого поведения в c помощью HQL:

Этот метод создаёт «облегчённый» запрос.

Аннотация Query против метода

Одна из основных фишек Spring Data — возможность создавать запрос из имени метода, что очень удобно, особенно в сочетании с умным дополнением от IntelliJ IDEA. Запрос, описанный в предыдущем примере может быть легко переписан:

Вроде бы и проще, и короче, и читаемее, а главное — не нужно смотреть сам запрос. Имя метода прочитал — и уже понятно, что он выбирает и как. Но дьявол и здесь в мелочах. Итоговый запрос метода, помеченного @Query мы уже видели. Что же будет во втором случае?

«Какого лешего!?» — воскликнет разработчик. Ведь выше мы уже убедились, что скрипач join не нужен.

Если вы ещё не обновились до версий с исправлением, а присоединение таблицы тормозит запрос здесь и сейчас, то не отчаивайтесь: есть сразу два способа облегчить боль:

хороший способ заключается в добавлении optional = false (если схема позволяет):

Теперь запрос-из-метода станет приятнее:

чего мы и добивались.

Ограничение выборки

Для своих целей нам нужно ограничивать выборку (например, хотим возвращать Optional из метода *RepositoryCustom ):

Указанный код обладает одной неприятной особенностью: в том случае, если запрос вернул пустую выборку будет брошено исключение

В проектах, которые я видел, это решалось двумя основными способами:

И очень редко я видел правильное решение:

EntityManager — часть стандарта JPA, в то время как Session принадлежит Хибернейту и является ИМХО более продвинутым средством, о чём часто забывают.

[Иногда] вредное улучшение

Когда нужно достать одно маленькое поле из «толстой» сущности мы поступаем так:

Запрос позволяет достать одно поле типа boolean без загрузки всей сущности (с добавлением в кэш-первого уровня, проверкой изменений по завершению сессии и прочими расходами). Иногда это не только не улучшает производительность, но и наоборот — создаёт ненужные запросы на пустом месте. Представим код, выполняющий некоторые проверки:

Этот код делает по меньшей мере 2 запроса, хотя второго можно было бы избежать:

Вывод простой: не пренебрегайте кэшем первого уровня, в рамках одной транзакции только первый JpaRepository::findById обращается к базе, т. к. кэш первого уровня включен всегда и привязан к сессии, которая, как правило, привязана к текущей транзакции.

Тесты, на которых можно поиграться (ссылка на репозиторий дана в начале статьи):

Источник

Spring Data на примере JPA

Введение

Spring Data позволяет легче создавать Spring-управляемые приложения которые используют новые способы доступа к данным, например нереляционные базы данных, map-reduce фреймворки, cloud сервисы, а так же уже хорошо улучшенную поддердку реляционных баз данных.

В этой статье будет рассмотрен один из под-проектов Spring Data — JPA

Что может Spring Data — JPA
Для чего вам может понадобиться Spring Data — JPA

Я бы ответил так — если вам нужно быстро в проекте создать Repository слой базируемый на JPA, предназначенный в основном для CRUD операций, и вы не хотите создавать абстрактные дао, интерфейсы их реализации, то Spring Data — JPA это хороший выбор.

С чего начать

Будем считать у вас уже есть maven проект с подключенным Spring, базой данных, настроенным EntityManager-ом.

1. Добавьте артефакт со Spring Data — JPA

2. В ваш applicationContext.xml нужно добавить путь где будут храниться ваши Repository интерфейсы

3. Создать Entity и Repository интерфейс для него

4. Теперь вы можете использовать созданный интерфейс в вашем приложении

Наследовавшись от CrudRepository вы получили возможность вызывать такие методы как:

без необходимости реализовывать их имплементацию.

Работа с запросами, сортировкой, порционной загрузкой

Рассмотрим на примере: вам нужно сделать запрос, который выберет все Test записи, у которых поле «dummy» установленно в false, и записи отсортированны по полю «tries» в порядке ABC.

Для решения такой задачи вы можете выбрать один из нескольких вариантов:

Если с первым способом все предельно просто и это знакомый запрос, то второй способ заключается в том, чтобы составить имя метода, особым способом использую ключевые слова, такие как: «find», «order», имя переменных и тд. Разработчики Spring Data — JPA постарались учесть большинство возможных вариантов, которые могут вам понадобится.

Specification и CriteriaBuilder

Если вам нужно написать действительно сложный запрос для этого вы можете использовать Specification.
Пример в котором в зависимости от «retries» будут выбраны данные с разными значениями «dummy».

Следующий пример покажет как можно использовать созданный Specification для фильтра всех данных.
Расширим ваш интерфейс при помощи JpaSpecificationExecutor

и вызовем метод findAll передав ему созданную Specification

Источник

Spring JPA репозитории в CUBA

что такое jpa repository. Смотреть фото что такое jpa repository. Смотреть картинку что такое jpa repository. Картинка про что такое jpa repository. Фото что такое jpa repository

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

Предпосылки

Разработчики обычно не очень любят менять свои привычки (зачастую, в список привычек входят и фреймворки). Когда я начал работать с CUBA, мне не пришлось учить слишком много всего нового, активно включаться в работу над проектом можно было почти сразу. Но была одна вещь, над которой пришлось посидеть подольше — это была работа с данными.

В Spring есть несколько библиотек, которые можно использовать для работы с БД, одна из наиболее популярных — spring-data-jpa, которая позволяет в большинстве случаев не писать SQL или JPQL. Нужно всего лишь создать специальный интерфейс с методами, которые названы специальным образом и Spring сгенерирует и выполнит за вас всю оставшуюся часть работы по выборке данных из БД и созданию экземпляров объектов-сущностей.

Ниже представлен интерфейс, с методом для подсчета клиентов с заданной фамилией.

Этот интерфейс можно напрямую использовать в Spring сервисах, не создавая никакой имплементации, что сильно ускоряет работу.

В CUBA есть API для работы с данными, который включает в себя различную функциональность вроде частично загружаемых сущностей или хитрую систему безопасности с контролем доступа к атрибутам сущностей и строкам в таблицах БД. Но этот API немного отличается от того, к чему привыкли разработчики в Spring Data или JPA/Hibernate.

Почему же в CUBA нет JPA репозиториев и можно ли их добавить?

Работа с данными в CUBA

В CUBA три основных класса, отвечающих за работу с данными: DataStore, EntityManager и DataManager.

DataStore — высокоуровневая абстракция для любого хранилища данных: БД, файловой системы или облачного хранилища. Этот API позволяет выполнять базовые операции над данными. В большинстве случаев разработчикам нет нужды работать с DataStore напрямую, кроме случаев разработки своего собственного хранилища, или если требуется какой-то очень специальный доступ к данным в хранилище.

EntityManager — копия всем хорошо известного JPA EntityManager. В отличие от стандартной имплементации, в нем есть специальные методы для работы с представлениями CUBA, для «мягкого»(логического) удаления данных, а также для работы с запросами в CUBA. Как и в случае с DataStore, в 90% проектов обычному разработчику не придется иметь дело с EntityManager, кроме случаев, когда нужно выполнять какие-то запросы в обход системы ограничения доступа к данным.

DataManager — основной класс для работы с данными в CUBA. Предоставляет API для манипулирования данными и поддерживает контроль доступа к данным, включая доступ к атрибутам и row-level ограничения. DataManager неявно модифицирует все запросы, которые выполняются в CUBA. Например, он может исключить поля таблицы, к которым у текущего пользователя нет доступа, из оператора select и добавить условия where для исключения строк таблиц из выборки. И это сильно облегчает жизнь разработчикам, потому что не надо думать, как правильно писать запросы с учетом прав доступа, CUBA это делает автоматически на основе данных из служебных таблиц БД.

Ниже — диаграмма взаимодействия компонентов CUBA, которые участвуют в выборке данных через DataManager.

что такое jpa repository. Смотреть фото что такое jpa repository. Смотреть картинку что такое jpa repository. Картинка про что такое jpa repository. Фото что такое jpa repository

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

Как уже упоминалось, DataManager отфильтрует «логически удаленные» записи, уберет из запроса запрещенные атрибуты, а также откроет и закроет транзакцию автоматически.

Но, когда дело доходит до запросов посложнее, то в CUBA приходится писать JPQL.

Например, если нужно посчитать клиентов с заданной фамилией, как в примере из предыдущего раздела, то нужно написать примерно такой код:

В CUBA API нужно передавать JPQL выражение в виде строки (Criteria API ещё не поддерживается), это читаемый и понятный способ создания запросов, но отладка таких запросов может принести немало веселых минут. Кроме того, строки JPQL не верифицируются ни компилятором, ни Spring Framework во время инициализации контейнера, что приводит к возникновению ошибок только в Runtime.

Сравните это со Spring JPA:

CUBA построена вокруг Spring Framework, так что в приложение, написанное с использованием CUBA, можно подключить библиотеку spring-data-jpa, но есть небольшая проблема — контроль доступа. Имплементация CrudRepository в Spring использует свой EntityManager. Таким образом, все запросы будут выполняться в обход DataManager. Таким образом, чтобы использовать JPA репозитории в CUBA, нужно заменить все вызовы EntityManager на вызовы DataManager и добавить поддержку CUBA представлений.

Кто-то может сказать, что spring-data-jpa — это такой неконтролируемый черный ящик и всегда предпочтительнее писать чистый JPQL или даже SQL. Это вечная проблема баланса между удобством и уровнем абстракции. Каждый выбирает тот способ, который ему больше по душе, но иметь в арсенале дополнительный способ работы с данными никогда не помешает. А тем, кому нужно больше управления, в Spring есть способ определить свой собственный запрос для методов JPA репозиториев.

Реализация

JPA репозитории реализованы в виде CUBA модуля, с использованием библиотеки spring-data-commons. Мы отказались от идеи модификации spring-data-jpa, потому что объем работы был бы сильно больше по сравнению с написанием собственного генератора запросов. Тем более, что spring-data-commons делает большую часть работы. Например, разбор имени метода и связывание имени с классами и свойствами полностью делается в этой библиотеке. Spring-data-commons содержит все необходимые базовые классы для имплементации собственных репозиториев и требуется не так много усилий, чтобы это реализовать. Например, эта библиотека используется в spring-data-mongodb.

В spring-data-commons используется проксирование для динамического создания реализаций интерфейсов. Когда инициализируется контекст CUBA приложения, то все ссылки на интерфейсы заменяются на ссылки на прокси-бины, опубликованные в контексте. При вызове метода интерфейса он перехватывается соответствующим прокси-объектом. Затем этот объект генерирует JPQL запрос по имени метода, подставляет параметры и отдает запрос с параметрами в DataManager на выполнение. Следующая диаграмма отображает упрощенный процесс взаимодействия ключевых компонентов модуля.

что такое jpa repository. Смотреть фото что такое jpa repository. Смотреть картинку что такое jpa repository. Картинка про что такое jpa repository. Фото что такое jpa repository

Использование репозиториев в CUBA

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

Можно использовать XML конфигурацию для того, чтобы «включить» репозитории:

А можно воспользоваться аннотациями:

После того, как поддержка репозиториев активирована, можно их создавать в привычном виде, например:

Для каждого метода можно использовать аннотации:

Пример использования репозитория в сервисе CUBA:

Заключение

CUBA — гибкий фреймворк. Если есть желание что-то в него добавить, то нет необходимости исправлять ядро самостоятельно или ждать новую версию. Я надеюсь, что этот модуль сделает разработку с CUBA более эффективной и быстрой. Первая версия модуля доступна на GitHub, протестировано на CUBA версии 6.10

Источник

Что такое jpa repository

The JPA module of Spring Data contains a custom namespace that allows defining repository beans. It also contains certain features and element attributes that are special to JPA. Generally the JPA repositories can be set up using the repositories element:

Example 2.1. Setting up JPA repositories using the namespace

Using this element looks up Spring Data repositories as described in Section 1.2.3, “Creating repository instances”. Beyond that it activates persistence exception translation for all beans annotated with @Repository to let exceptions being thrown by the JPA persistence providers be converted into Spring’s DataAccessException hierarchy.

Custom namespace attributes

Beyond the default attributes of the repositories element the JPA namespace offers additional attributes to gain more detailed control over the setup of the repositories:

Table 2.1. Custom JPA-specific attributes of the repositories element

Note that we require a PlatformTransactionManager bean named transactionManager to be present if no explicit transaction-manager-ref is defined.

2.1.2 Annotation based configuration

The Spring Data JPA repositories support cannot only be activated through an XML namespace but also using an annotation through JavaConfig.

Example 2.2. Spring Data JPA repositories using JavaConfig

2.2 Persisting entities

2.2.1 Saving entities

Entity state detection strategies

Spring Data JPA offers the following strategies to detect whether an entity is new or not:

Table 2.2. Options for detection whether an entity is new in Spring Data JPA

2.3 Query methods

2.3.1 Query lookup strategies

The JPA module supports defining a query manually as String or have it being derived from the method name.

Declared queries

Although getting a query derived from the method name is quite convenient, one might face the situation in which either the method name parser does not support the keyword one wants to use or the method name would get unnecessarily ugly. So you can either use JPA named queries through a naming convention (see Section 2.3.3, “Using JPA NamedQueries” for more information) or rather annotate your query method with @Query (see Section 2.3.4, “Using @Query” for details).

2.3.2 Query creation

Generally the query creation mechanism for JPA works as described in Section 1.2, “Query methods”. Here’s a short example of what a JPA query method translates into:

Example 2.3. Query creation from method names

We will create a query using the JPA criteria API from this but essentially this translates into the following query:

Table 2.3. Supported keywords inside method names

In and NotIn also take any subclass of Collection as parameter as well as arrays or varargs. For other syntactical versions of the very same logical operator check Appendix B, Repository query keywords.

2.3.3 Using JPA NamedQueries

The examples use simple element and @NamedQuery annotation. The queries for these configuration elements have to be defined in JPA query language. Of course you can use or @NamedNativeQuery too. These elements allow you to define the query in native SQL by losing the database platform independence.

XML named query definition

To use XML configuration simply add the necessary element to the orm.xml JPA configuration file located in META-INF folder of your classpath. Automatic invocation of named queries is enabled by using some defined naming convention. For more details see below.

Example 2.4. XML named query configuration

As you can see the query has a special name which will be used to resolve it at runtime.

Annotation configuration

Annotation configuration has the advantage of not needing another configuration file to be edited, probably lowering maintenance costs. You pay for that benefit by the need to recompile your domain class for every new query declaration.

Example 2.5. Annotation based named query configuration

Declaring interfaces

To allow execution of these named queries all you need to do is to specify the UserRepository as follows:

Example 2.6. Query method declaration in UserRepository

Spring Data will try to resolve a call to these methods to a named query, starting with the simple name of the configured domain class, followed by the method name separated by a dot. So the example here would use the named queries defined above instead of trying to create a query from the method name.

2.3.4 Using @Query

Using named queries to declare queries for entities is a valid approach and works fine for a small number of queries. As the queries themselves are tied to the Java method that executes them you actually can bind them directly using the Spring Data JPA @Query annotation rather than annotating them to the domain class. This will free the domain class from persistence specific information and co-locate the query to the repository interface.

Example 2.7. Declare query at the query method using @Query

Using advanced LIKE expressions

The query execution mechanism for manually defined queries using @Query allow the definition of advanced LIKE expressions inside the query definition.

Example 2.8. Advanced LIKE expressions in @Query

In the just shown sample LIKE delimiter character % is recognized and the query transformed into a valid JPQL query (removing the % ). Upon query execution the parameter handed into the method call gets augmented with the previously recognized LIKE pattern.

Native queries

The @Query annotation allows to execute native queries by setting the nativeQuery flag to true. Note, that we currently don’t support execution of pagination or dynamic sorting for native queries as we’d have to manipulate the actual query declared and we cannot do this reliably for native SQL.

Example 2.9. Declare a native query at the query method using @Query

2.3.5 Using named parameters

By default Spring Data JPA will use position based parameter binding as described in all the samples above. This makes query methods a little error prone to refactoring regarding the parameter position. To solve this issue you can use @Param annotation to give a method parameter a concrete name and bind the name in the query.

Example 2.10. Using named parameters

Note that the method parameters are switched according to the occurrence in the query defined.

2.3.6 Using SpEL expressions

Table 2.4. Supported variables inside SpEL based query templates

VariableUsageDescription
entityNameselect x from # <#entityName>xInserts the entityName of the domain type associated with the given Repository. The entityName is resolved as follows: If the domain type has set the name property on the @Entity annotation then it will be used. Otherwise the simple class-name of the domain type will be used.

The following example demonstrates one use case for the # <#entityName>expression in a query string where you want to define a repository interface with a query method with a manually defined query. In order not to have to state the actual entity name in the query string of a @Query annotation one can use the # <#entityName>Variable.

Another use case for the # <#entityName>expression in a query string is if you want to define a generic repository interface with specialized repository interfaces for a concrete domain type. In order not to have to repeat the definition of custom query methods on the concrete interfaces you can use the entity name expression in the query string of the @Query annotation in the generic repository interface.

2.3.7 Modifying queries

All the sections above describe how to declare queries to access a given entity or collection of entities. Of course you can add custom modifying behaviour by using facilities described in Section 1.3, “Custom implementations for Spring Data repositories”. As this approach is feasible for comprehensive custom functionality, you can achieve the execution of modifying queries that actually only need parameter binding by annotating the query method with @Modifying :

Example 2.13. Declaring manipulating queries

2.3.8 Applying query hints

To apply JPA QueryHint s to the queries declared in your repository interface you can use the QueryHints annotation. It takes an array of JPA QueryHint annotations plus a boolean flag to potentially disable the hints applied to the addtional count query triggered when applying pagination.

Example 2.14. Using QueryHints with a repository method

The just shown declaration would apply the configured QueryHint for that actually query but omit applying it to the count query triggered to calculate the total number of pages.

2.4 Specifications

JPA 2 introduces a criteria API that can be used to build queries programmatically. Writing a criteria you actually define the where-clause of a query for a domain class. Taking another step back these criteria can be regarded as predicate over the entity that is described by the JPA criteria API constraints.

Spring Data JPA takes the concept of a specification from Eric Evans’ book «Domain Driven Design», following the same semantics and providing an API to define such Specification s using the JPA criteria API. To support specifications you can extend your repository interface with the JpaSpecificationExecutor interface:

The additional interface carries methods that allow you to execute Specification s in a variety of ways.

For example, the findAll method will return all entities that match the specification:

The Specification interface is as follows:

Okay, so what is the typical use case? Specification s can easily be used to build an extensible set of predicates on top of an entity that then can be combined and used with JpaRepository without the need to declare a query (method) for every needed combination. Here’s an example:

Example 2.15. Specifications for a Customer

Example 2.16. Using a simple Specification

Okay, why not simply create a query for this kind of data access? You’re right. Using a single Specification does not gain a lot of benefit over a plain query declaration. The power of Specification s really shines when you combine them to create new Specification objects. You can achieve this through the Specifications helper class we provide to build expressions like this:

Example 2.17. Combined Specifications

As you can see, Specifications offers some glue-code methods to chain and combine Specification s. Thus extending your data access layer is just a matter of creating new Specification implementations and combining them with ones already existing.

2.5 Transactionality

Example 2.18. Custom transaction configuration for CRUD

This will cause the findAll() method to be executed with a timeout of 10 seconds and without the readOnly flag.

Another possibility to alter transactional behaviour is using a facade or service implementation that typically covers more than one repository. Its purpose is to define transactional boundaries for non-CRUD operations:

Example 2.19. Using a facade to define transactions for multiple repository calls

This will cause call to addRoleToAllUsers(…) to run inside a transaction (participating in an existing one or create a new one if none already running). The transaction configuration at the repositories will be neglected then as the outer transaction configuration determines the actual one used. Note that you will have to activate or use @EnableTransactionManagement explicitly to get annotation based configuration at facades working. The example above assumes you are using component scanning.

2.5.1 Transactional query methods

To allow your query methods to be transactional simply use @Transactional at the repository interface you define.

Example 2.20. Using @Transactional at query methods

Typically you will want the readOnly flag set to true as most of the query methods will only read data. In contrast to that deleteInactiveUsers() makes use of the @Modifying annotation and overrides the transaction configuration. Thus the method will be executed with readOnly flag set to false.

It’s definitely reasonable to use transactions for read only queries and we can mark them as such by setting the readOnly flag. This will not, however, act as check that you do not trigger a manipulating query (although some databases reject INSERT and UPDATE statements inside a read only transaction). The readOnly flag instead is propagated as hint to the underlying JDBC driver for performance optimizations. Furthermore, Spring will perform some optimizations on the underlying JPA provider. E.g. when used with Hibernate the flush mode is set to NEVER when you configure a transaction as readOnly which causes Hibernate to skip dirty checks (a noticeable improvement on large object trees).

2.6 Locking

To specify the lock mode to be used the @Lock annotation can be used on query methods:

Example 2.21. Defining lock metadata on query methods

Example 2.22. Defining lock metadata on CRUD methods

2.7 Auditing

2.7.1 Basics

Spring Data provides sophisticated support to transparently keep track of who created or changed an entity and the point in time this happened. To benefit from that functionality you have to equip your entity classes with auditing metadata that can be defined either using annotations or by implementing an interface.

Annotation based auditing metadata

Example 2.23. An audited entity

Interface-based auditing metadata

In case you don’t want to use annotations to define auditing metadata you can let your domain class implement the Auditable interface. It exposes setter methods for all of the auditing properties.

There’s also a convenience base class AbstractAuditable which you can extend to avoid the need to manually implement the interface methods. Be aware that this increases the coupling of your domain classes to Spring Data which might be something you want to avoid. Usually the annotation based way of defining auditing metadata is preferred as it is less invasive and more flexible.

AuditorAware

Here’s an example implementation of the interface using Spring Security’s Authentication object:

Example 2.24. Implementation of AuditorAware based on Spring Security

The implementation is accessing the Authentication object provided by Spring Security and looks up the custom UserDetails instance from it that you have created in your UserDetailsService implementation. We’re assuming here that you are exposing the domain user through that UserDetails implementation but you could also look it up from anywhere based on the Authentication found.

2.7.2 General auditing configuration

Spring Data JPA ships with an entity listener that can be used to trigger capturing auditing information. So first you have to register the AuditingEntityListener inside your orm.xml to be used for all entities in your persistence contexts:

Note that the auditing feature requires spring-aspects.jar to be on the classpath.

Example 2.25. Auditing configuration orm.xml

Now activating auditing functionality is just a matter of adding the Spring Data JPA auditing namespace element to your configuration:

Example 2.26. Activating auditing using XML configuration

As of Spring Data JPA 1.5, auditing can be enabled by annotating a configuration class with the @EnableJpaAuditing annotation.

Example 2.27. Activating auditing via Java configuration

2.8 Miscellaneous

2.8.1 Merging persistence units

Spring supports having multiple persistence units out of the box. Sometimes, however, you might want to modularize your application but still make sure that all these modules run inside a single persistence unit at runtime. To do so Spring Data JPA offers a PersistenceUnitManager implementation that automatically merges persistence units based on their name.

Example 2.28. Using MergingPersistenceUnitmanager

2.8.2 Classpath scanning for @Entity classes and JPA mapping files

Example 2.29. Using ClasspathScanningPersistenceUnitPostProcessor

As of Spring 3.1 a package to scan can be configured on the LocalContainerEntityManagerFactoryBean directly to enable classpath scanning for entity classes. See the JavaDoc for details.

2.8.3 CDI integration

Instances of the repository interfaces are usually created by a container, which Spring is the most natural choice when working with Spring Data. There’s sophisticated support to easily set up Spring to create bean instances documented in Section 1.2.3, “Creating repository instances”. As of version 1.1.0 Spring Data JPA ships with a custom CDI extension that allows using the repository abstraction in CDI environments. The extension is part of the JAR so all you need to do to activate it is dropping the Spring Data JPA JAR into your classpath.

You can now set up the infrastructure by implementing a CDI Producer for the EntityManagerFactory and EntityManager :

The necessary setup can vary depending on the JavaEE environment you run in. It might also just be enough to redeclare a EntityManager as CDI bean as follows:

In this example, the container has to be capable of creating JPA EntityManager s itself. All the configuration does is re-exporting the JPA EntityManager as CDI bean.

The Spring Data JPA CDI extension will pick up all EntityManager s availables as CDI beans and create a proxy for a Spring Data repository whenever an bean of a repository type is requested by the container. Thus obtaining an instance of a Spring Data repository is a matter of declaring an @Inject ed property:

Источник

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

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