Repository spring что это
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 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) на основе наших интерфейсов.
Repository spring что это
The goal of Spring Data repository abstraction is to significantly reduce the amount of boilerplate code required to implement data access layers for various persistence stores.
Spring Data repository documentation and your module
1.1 Core concepts
The central interface in Spring Data repository abstraction is Repository (probably not that much of a surprise). It takes the the domain class to manage as well as the id type of the domain class as type arguments. This interface acts primarily as a marker interface to capture the types to work with and to help you to discover interfaces that extend this one. The CrudRepository provides sophisticated CRUD functionality for the entity class that is being managed.
Example 1.1. CrudRepository interface
Saves the given entity.
Returns the entity identified by the given id.
Returns all entities.
Returns the number of entities.
Deletes the given entity.
Indicates whether an entity with the given id exists.
On top of the CrudRepository there is a PagingAndSortingRepository abstraction that adds additional methods to ease paginated access to entities:
Example 1.2. PagingAndSortingRepository
Accessing the second page of User by a page size of 20 you could simply do something like this:
1.2 Query methods
Standard CRUD functionality repositories usually have queries on the underlying datastore. With Spring Data, declaring those queries becomes a four-step process:
Declare an interface extending Repository or one of its subinterfaces and type it to the domain class that it will handle.
Declare query methods on the interface.
Set up Spring to create proxy instances for those interfaces.
Get the repository instance injected and use it.
The sections that follow explain each step.
1.2.1 Defining repository interfaces
Fine-tuning repository definition
Example 1.3. Selectively exposing CRUD methods
1.2.2 Defining query methods
The repository proxy has two ways to derive a store-specific query from the method name. It can derive the query from the method name directly, or by using an additionally created query. Available options depend on the actual store. However, there’s got to be an strategy that decides what actual query is created. Let’s have a look at the available options.
Query lookup strategies
The following strategies are available for the repository infrastructure to resolve the query. You can configure the strategy at the namespace through the query-lookup-strategy attribute. Some strategies may not be supported for particular datastores.
CREATE
CREATE attempts to construct a store-specific query from the query method name. The general approach is to remove a given set of well-known prefixes from the method name and parse the rest of the method. Read more about query construction in the section called “Query creation”.
USE_DECLARED_QUERY
USE_DECLARED_QUERY tries to find a declared query and will throw an exception in case it can’t find one. The query can be defined by an annotation somewhere or declared by other means. Consult the documentation of the specific store to find available options for that store. If the repository infrastructure does not find a declared query for the method at bootstrap time, it fails.
CREATE_IF_NOT_FOUND (default)
Query creation
Example 1.4. Query creation from method names
The actual result of parsing the method depends on the persistence store for which you create the query. However, there are some general things to notice.
The method parser supports setting an IgnoreCase flag for individual properties, for example, findByLastnameIgnoreCase(…) ) or for all properties of a type that support ignoring case (usually String s, for example, findByLastnameAndFirstnameAllIgnoreCase(…) ). Whether ignoring cases is supported may vary by store, so consult the relevant sections in the reference documentation for the store-specific query method.
You can apply static ordering by appending an OrderBy clause to the query method that references a property and by providing a sorting direction ( Asc or Desc ). To create a query method that supports dynamic sorting, see the section called “Special parameter handling”.
Property expressions
Property expressions can refer only to a direct property of the managed entity, as shown in the preceding example. At query creation time you already make sure that the parsed property is a property of the managed domain class. However, you can also define constraints by traversing nested properties. Assume Person s have Address es with ZipCode s. In that case a method name of
Although this should work for most cases, it is possible for the algorithm to select the wrong property. Suppose the Person class has an addressZip property as well. The algorithm would match in the first split round already and essentially choose the wrong property and finally fail (as the type of addressZip probably has no code property). To resolve this ambiguity you can use _ inside your method name to manually define traversal points. So our method name would end up like so:
Special parameter handling
To handle parameters to your query you simply define method parameters as already seen in the examples above. Besides that the infrastructure will recognize certain specific types like Pageable and Sort to apply pagination and sorting to your queries dynamically.
Example 1.5. Using Pageable and Sort in query methods
The first method allows you to pass an org.springframework.data.domain.Pageable instance to the query method to dynamically add paging to your statically defined query. Sorting options are handled through the Pageable instance too. If you only need sorting, simply add an org.springframework.data.domain.Sort parameter to your method. As you also can see, simply returning a List is possible as well. In this case the additional metadata required to build the actual Page instance will not be created (which in turn means that the additional count query that would have been necessary not being issued) but rather simply restricts the query to look up only the given range of entities.
To find out how many pages you get for a query entirely you have to trigger an additional count query. By default this query will be derived from the query you actually trigger.
1.2.3 Creating repository instances
In this section you create instances and bean definitions for the repository interfaces defined. The easiest way to do so is by using the Spring namespace that is shipped with each Spring Data module that supports the repository mechanism.
XML configuration
Each Spring Data module includes a repositories element that allows you to simply define a base package that Spring scans for you.
Using filters
For example, to exclude certain interfaces from instantiation as repository, you could use the following configuration:
Example 1.6. Using exclude-filter element
This example excludes all interfaces ending in SomeRepository from being instantiated.
JavaConfig
The repository infrastructure can also be triggered using a store-specific @Enable$
A sample configuration to enable Spring Data repositories looks something like this.
Example 1.7. Sample annotation based repository configuration
The sample uses the JPA-specific annotation, which you would change according to the store module you actually use. The same applies to the definition of the EntityManagerFactory bean. Consult the sections covering the store-specific configuration.
Standalone usage
You can also use the repository infrastructure outside of a Spring container. You still need some Spring libraries in your classpath, but generally you can set up repositories programmatically as well. The Spring Data modules that provide repository support ship a persistence technology-specific RepositoryFactory that you can use as follows.
Example 1.8. Standalone usage of repository factory
1.3 Custom implementations for Spring Data repositories
Often it is necessary to provide a custom implementation for a few repository methods. Spring Data repositories easily allow you to provide custom repository code and integrate it with generic CRUD abstraction and query method functionality.
1.3.1 Adding custom behavior to single repositories
To enrich a repository with custom functionality you first define an interface and an implementation for the custom functionality. Use the repository interface you provided to extend the custom interface.
Example 1.9. Interface for custom repository functionality
Example 1.10. Implementation of custom repository functionality
The implementation itself does not depend on Spring Data and can be a regular Spring bean. So you can use standard dependency injection behavior to inject references to other beans, take part in aspects, and so on.
Example 1.11. Changes to the your basic repository interface
Let your standard repository interface extend the custom one. Doing so makes CRUD and custom functionality available to clients.
Configuration
Example 1.12. Configuration example
Manual wiring
The preceding approach works well if your custom implementation uses annotation-based configuration and autowiring only, as it will be treated as any other Spring bean. If your custom implementation bean needs special wiring, you simply declare the bean and name it after the conventions just described. The infrastructure will then refer to the manually defined bean definition by name instead of creating one itself.
Example 1.13. Manual wiring of custom implementations (I)
1.3.2 Adding custom behavior to all repositories
The preceding approach is not feasible when you want to add a single method to all your repository interfaces.
To add custom behavior to all repositories, you first add an intermediate interface to declare the shared behavior.
Example 1.14. An interface declaring custom shared behavior
Now your individual repository interfaces will extend this intermediate interface instead of the Repository interface to include the functionality declared.
Next, create an implementation of the intermediate interface that extends the persistence technology-specific repository base class. This class will then act as a custom base class for the repository proxies.
Example 1.15. Custom repository base class
Example 1.16. Custom repository factory bean
Finally, either declare beans of the custom factory directly or use the factory-class attribute of the Spring namespace to tell the repository infrastructure to use your custom factory implementation.
Example 1.17. Using the custom factory with the namespace
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, есть подсказка для написания методов запросов.
Достаточно только определить подобным образом метод, без имплементации и 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.
Repository spring что это
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
Variable | Usage | Description |
---|---|---|
entityName | select x from # <#entityName>x | Inserts 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: