DATAREST-450 - Migrate wiki content to reference docs.

Scrubbed old format. Lightened up paging section and focus more on "next"/"prev" hypermedia controls and their benefit. Moved several sections under one top level section called "Customizing Spring Data REST". Cleaned up index.adoc to match the style of Spring Data JPA. Removed JSONP section.
Remove embedded entity operations, based on old spring-data-rest-compact media type. Utilized relative leveloffset for each include. Corrected some ALPS mistakes.

Original pull request: #162.
This commit is contained in:
Greg Turnquist
2015-02-17 10:26:08 -06:00
committed by Oliver Gierke
parent 8b3b7bac98
commit ebbe77da5b
17 changed files with 727 additions and 172 deletions

1
CONTRIBUTING.adoc Normal file
View File

@@ -0,0 +1 @@
You find the contribution guidelines for Spring Data projects https://github.com/spring-projects/spring-data-build/blob/master/CONTRIBUTING.md[here].

31
README.adoc Normal file
View File

@@ -0,0 +1,31 @@
= Spring Data REST
The goal of the project is to provide a flexible and configurable mechanism for writing simple services that can be exposed over HTTP.
The first exporter implemented is a JPA Repository exporter. This takes your JPA repositories and front-ends them with HTTP, allowing you full CRUD capability over your entities, to include managing associations.
=== Installation
Installation instructions are in the docs:
* http://docs.spring.io/spring-data/rest/docs/current/reference/html/[http://docs.spring.io/spring-data/rest/docs/current/reference/html/]
=== License
The Spring Data REST is http://www.apache.org/licenses/LICENSE-2.0.html[Apache 2.0 licensed].
== Contributing to Spring Data Rest
Here are some ways for you to get involved in the community:
* Get involved with the Spring community on Stackoverflow. Please help out on the http://stackoverflow.com/questions/tagged/spring-data-rest[spring-data-rest] tag by responding to questions and joining the debate.
* Create https://jira.spring.io/browse/DATAREST[JIRA] tickets for bugs and new features and comment and vote on the ones that you are interested in.
* Github is for social coding: if you want to write code, we encourage contributions through pull requests from http://help.github.com/forking/[forks of this repository]. If you want to contribute code this way, please reference a JIRA ticket as well covering the specific issue you are addressing.
* Watch for upcoming articles on Spring by http://spring.io/blog/[subscribing] to spring.io.
Before we accept a non-trivial patch or pull request we will need you to sign the https://support.springsource.com/spring_committer_signup[contributor's agreement]. Signing the contributor's agreement does not grant anyone commit rights to the main repository, but it does mean that we can accept your contributions, and you will get an author credit if we do. Active contributors might be asked to join the core team, and given the ability to merge pull requests.
=== Note
We disabled the github issues since we want to use https://jira.spring.io/browse/DATAREST[JIRA] as the only issue tracker.
All open existing issues have been automatically imported into JIRA, so nothing was lost :)

View File

@@ -0,0 +1,42 @@
[[customizing-sdr.adding-sdr-to-spring-mvc-app]]
= Adding Spring Data REST to an existing Spring MVC Application
If you have an existing Spring MVC application and you'd like to integrate Spring Data REST, it's actually very easy.
Somewhere in your Spring MVC configuration (most likely where you configure your MVC resources) add a bean reference to the JavaConfig class that is responsible for configuring the `RepositoryRestController`. The class name is `org.springframework.data.rest.webmvc.RepositoryRestMvcConfiguration`.
In Java, this would look like:
[source,java]
----
import org.springframework.context.annotation.Import;
import org.springframework.data.rest.webmvc.RepositoryRestMvcConfiguration;
@Configuration
@Import(RepositoryRestMvConfiguration.class)
public class MyApplicationConfiguration {
}
----
In XML this would look like:
[source,xml]
----
<bean class="org.springframework.data.rest.webmvc.config.RepositoryRestMvcConfiguration"/>
----
When your ApplicationContext comes across this bean definition it will bootstrap the necessary Spring MVC resources to fully-configure the controller for exporting the repositories it finds in that `ApplicationContext` and any parent contexts.
== More on required configuration
There are a couple Spring MVC resources that Spring Data REST depends on that must be configured correctly for it to work inside an existing Spring MVC application. We've tried to isolate those resources from whatever similar resources already exist within your application, but it may be that you want to customize some of the behavior of Spring Data REST by modifying these MVC components.
The most important things that we configure especially for use by Spring Data REST include:
=== RepositoryRestHandlerMapping
We register a custom `HandlerMapping` instance that responds only to the `RepositoryRestController` and only if a path is meant to be handled by Spring Data REST. In order to keep paths that are meant to be handled by your application separate from those handled by Spring Data REST, this custom `HandlerMapping` inspects the URL path and checks to see if a Repository has been exported under that name. If it has, it allows the request to be handled by Spring Data REST. If there is no Repository exported under that name, it returns `null`, which just means "let other `HandlerMapping` instances try to service this request".
The Spring Data REST `HandlerMapping` is configured with `order=(Ordered.LOWEST_PRECEDENCE - 100)` which means it will usually be first in line when it comes time to map a URL path. Your existing application will never get a chance to service a request that is meant for a repository. For example, if you have a repository exported under the name "person", then all requests to your application that start with `/person` will be handled by Spring Data REST and your application will never see that request. If your repository is exported under a different name, however (like "people"), then requests to `/people` will go to Spring Data REST and requests to "/person" will be handled by your application.

View File

@@ -0,0 +1,209 @@
[[customizing-sdr.configuring-the-rest-url-path]]
= Configuring the REST URL path
Configuring the segments of the URL path under which the resources of a JPA repository are exported is simple. You just add an annotation at the class level and/or at the query method level.
By default, the exporter will expose your `CrudRepository` using the name of the domain class. Spring Data REST also applies the https://github.com/atteo/evo-inflector[Evo Inflector] to pluralize this word. So a repository defined as follows:
[source,java]
----
interface PersonRepository extends CrudRepository<Person, Long> {}
----
Will, by default, be exposed under the URL `http://localhost:8080/persons/`
To change how the repository is exported, add a `@RestResource` annotation at the class level:
[source,java]
----
@RestResource(path = "people")
interface PersonRepository extends CrudRepository<Person, Long> {}
----
Now the repository will be accessible under the URL: `http://localhost:8080/people/`
If you have query methods defined, those also default to be exposed by their name:
[source,java]
----
interface PersonRepository extends CrudRepository<Person, Long> {
List<Person> findByName(String name);
}
----
This would be exposed under the URL: `http://localhost:8080/persons/search/findByName`
NOTE: All query method resources are exposed under the resource `search`.
To change the segment of the URL under which this query method is exposed, use the `@RestResource` annotation again:
[source,java]
----
@RestResource(path = "people")
interface PersonRepository extends CrudRepository<Person, Long> {
@RestResource(path = "names")
List<Person> findByName(String name);
}
----
Now this query method will be exposed under the URL: `http://localhost:8080/people/search/names`
== Handling rels
Since these resources are all discoverable, you can also affect how the "rel" attribute is displayed in the links sent out by the exporter.
For instance, in the default configuration, if you issue a request to `http://localhost:8080/persons/search` to find out what query methods are exposed, you'll get back a list of links:
[source,javascript]
----
{
"_links" : {
"findByName" : {
"href" : "http://localhost:8080/persons/search/findByName"
}
}
}
----
To change the rel value, use the `rel` property on the `@RestResource` annotation:
[source,java]
----
@RestResource(path = "people")
interface PersonRepository extends CrudRepository<Person, Long> {
@RestResource(path = "names", rel = "names")
List<Person> findByName(String name);
}
----
This would result in a link value of:
[source,javascript]
----
{
"_links" : {
"names" : {
"href" : "http://localhost:8080/persons/search/names"
}
}
}
----
NOTE: These snippets of JSON assume you are using Spring Data REST's default format of http://stateless.co/hal_specification.html[HAL]. It's possible to turn off HAL, which would cause the output to look different. But your ability to override rel names is totally independent of the rendering format.
[source,java]
----
@RestResource(path = "people", rel = "people")
interface PersonRepository extends CrudRepository<Person, Long> {
@RestResource(path = "names", rel = "names")
List<Person> findByName(String name);
}
----
Altering the rel of a Repository changes the top level name:
[source,javascript]
----
{
"_links" : {
"people" : {
"href" : "http://localhost:8080/people"
},
}
}
----
In the top level fragment above:
* `path = "people"` changed the value in `href` from `/persons` to `/people`
* `rel = "people"` changed the name of that link from `persons` to `people`
When you navigate to the *search* resource of this repository, the finder-method's `@RestResource` annotation has altered the path as shown below:
[source,javascript]
----
{
"_links" : {
"names" : {
"href" : "http://localhost:8080/people/search/names"
}
}
}
----
This collection of annotations in your Repository definition has caused the following changes:
* The Repository-level annotation's `path = "people"` is reflected in the base URI with `/people`
* Being a finder method provides you with `/people/search`
* `path = "names"` creates a URI of `/people/search/names`
* `rel = "names"` changes the name of that link from `findByNames` to `names`
[[customizing-sdr.hiding-repositories]]
== Hiding certain repositories, query methods, or fields
You may not want a certain repository, a query method on a repository, or a field of your entity to be exported at all. Examples include hiding fields like `password` on a `User` object or similar sensitive data. To tell the exporter to not export these items, annotate them with `@RestResource` and set `exported = false`.
For example, to skip exporting a Repository:
[source,java]
----
@RestResource(exported = false)
interface PersonRepository extends CrudRepository<Person, Long> {}
----
To skip exporting a query method:
[source,java]
----
@RestResource(path = "people", rel = "people")
interface PersonRepository extends CrudRepository<Person, Long> {
@RestResource(exported = false)
List<Person> findByName(String name);
}
----
Or to skip exporting a field:
[source,java]
----
@Entity
public class Person {
@Id @GeneratedValue private Long id;
@OneToMany
@RestResource(exported = false)
private Map<String, Profile> profiles;
}
----
WARNING: Projections provide the means to change what is exported and effectively side step these settings. If you create any projections against the same domain object, it's your responsiblity to NOT export the fields.
[[customizing-sdr.hiding-repository-crud-methods]]
== Hiding repository CRUD methods
If you don't want to expose a save or delete method on your `CrudRepository`, you can use the `@RestResource(exported = false)` setting by overriding the method you want to turn off and placing the annotation on the overriden version. For example, to prevent HTTP users from invoking the delete methods of `CrudRepository`, override all of them and add the annotation to the overriden methods.
[source,java]
----
@RestResource(path = "people", rel = "people")
interface PersonRepository extends CrudRepository<Person, Long> {
@Override
@RestResource(exported = false)
void delete(Long id);
@Override
@RestResource(exported = false)
void delete(Person entity);
}
----
WARNING: It is important that you override _both_ delete methods as the exporter currently uses a somewhat naive algorithm for determing which CRUD method to use in the interest of faster runtime performance. It's not currently possible to turn off the version of delete which takes an ID but leave exported the version that takes an entity instance. For the time being, you can either export the delete methods or not. If you want turn them off, then just keep in mind you have to annotate both versions with `exported = false`.

View File

@@ -0,0 +1,72 @@
[[customizing-sdr.custom-jackson-deserialization]]
= Adding custom (de)serializers to Jackson's ObjectMapper
Sometimes the behavior of the Spring Data REST's `ObjectMapper`, which has been specially configured to use intelligent serializers that can turn domain objects into links and back again, may not handle your domain model correctly. There are so many ways one can structure your data that you may find your own domain model isn't being translated to JSON correctly. It's also sometimes not practical in these cases to try and support a complex domain model in a generic way. Sometimes, depending on the complexity, it's not even possible to offer a generic solution.
So to accommodate the largest percentage of the use cases, Spring Data REST tries very hard to render your object graph correctly. It will try and serialize unmanaged beans as normal POJOs and it will try and create links to managed beans where that's necessary. But if your domain model doesn't easily lend itself to reading or writing plain JSON, you may want to configure Jackson's ObjectMapper with your own custom type mappings and (de)serializers.
== Abstract class registration
One key configuration point you might need to hook into is when you're using an abstract class (or an interface) in your domain model. Jackson won't know by default what implementation to create for an interface. Take the following example:
[source,java]
----
@Entity
public class MyEntity {
@OneToMany
private List<MyInterface> interfaces;
}
----
In a default configuration, Jackson has no idea what class to instantiate when POSTing new data to the exporter. This is something you'll need to tell Jackson either through an annotation, or, more cleanly, by registering a type mapping using a http://wiki.fasterxml.com/JacksonFeatureModules[Module].
Any `Module` bean declared within the scope of your `ApplicationContext` will be picked up by the exporter and registered with its `ObjectMapper`. To add this special abstract class type mapping, create a `Module` bean and in the `setupModule` method, add an appropriate `TypeResolver`:
[source,java]
----
public class MyCustomModule extends SimpleModule {
private MyCustomModule() {
super("MyCustomModule", new Version(1, 0, 0, "SNAPSHOT"));
}
@Override
public void setupModule(SetupContext context) {
context.addAbstractTypeResolver(
new SimpleAbstractTypeResolver().addMapping(MyInterface.class,
MyInterfaceImpl.class));
}
}
----
Once you have access to the `SetupContext` object in your `Module`, you can do all sorts of cool things to configure Jackon's JSON mapping. You can read more about how http://wiki.fasterxml.com/JacksonFeatureModules[Module`s work on Jackson's wiki].
== Adding custom serializers for domain types
If you want to (de)serialize a domain type in a special way, you can register your own implementations with Jackson's `ObjectMapper` and the Spring Data REST exporter will transparently handle those domain objects correctly.
To add serializers, from your `setupModule` method implementation, do something like the following:
[source,java]
----
public class MyCustomModule extends SimpleModule {
@Override
public void setupModule(SetupContext context) {
SimpleSerializers serializers = new SimpleSerializers();
SimpleDeserializers deserializers = new SimpleDeserializers();
serializers.addSerializer(MyEntity.class, new MyEntitySerializer());
deserializers.addDeserializer(MyEntity.class, new MyEntityDeserializer());
context.addSerializers(serializers);
context.addDeserializers(deserializers);
}
}
----
Now Spring Data REST will correctly handle your domain objects in case they are too complex for the 80% generic use case that Spring Data REST tries to cover.

View File

@@ -0,0 +1,39 @@
[[customizing-sdr.customizing-json-output]]
= Customizing the JSON output
Sometimes in your application you need to provide links to other resources from a particular entity. For example, a `Customer` response might be enriched with links to a current shopping cart, or links to manage resources related to that entity. Spring Data REST provides integration with https://github.com/SpringSource/spring-hateoas[Spring HATEOAS] and provides an extension hook for users to alter the representation of resources going out to the client.
== The ResourceProcessor interface
Spring HATEOAS defines a `ResourceProcessor<>` interface for processing entities. All beans of type `ResourceProcessor&lt;Resource&lt;T&gt;&gt;` will be automatically picked up by the Spring Data REST exporter and triggered when serializing an entity of type `T`.
For example, to define a processor for a `Person` entity, add a `@Bean` to your `ApplicationContext like the following (which is taken from the Spring Data REST tests):
[source,java]
----
@Bean
public ResourceProcessor<Resource<Person>> personProcessor() {
return new ResourceProcessor<Resource<Person>>() {
@Override
public Resource<Person> process(Resource<Person> resource) {
resource.add(new Link("http://localhost:8080/people", "added-link"));
return resource;
}
};
}
----
IMPORTANT: This example hard codes a link to `http://localhost:8080/people`. If you have a Spring MVC endpoint inside your app that you wish to link to, consider using Spring HATEOAS's https://github.com/spring-projects/spring-hateoas#building-links-pointing-to-methods[`linkTo(...)`] method to avoid managing the URL.
== Adding Links
It's possible to add links to the default representation of an entity by simply calling `resource.add(Link)` like the example above. Any links you add to the `Resource` will be added to the final output.
== Customizing the representation
The Spring Data REST exporter executes any discovered ```ResourceProcessor```s before it creates the output representation. It does this by registering a `Converter<Entity, Resource>` instance with an internal `ConversionService`. This is the component responsible for creating the links to referenced entities (e.g. those objects under the *_links* property in the object's JSON representation). It takes an `@Entity` and iterates over its properties, creating links for those properties that are managed by a `Repository` and copying across any embedded or simple properties.
If your project needs to have output in a different format, however, it's possible to completely replace the default outgoing JSON representation with your own. If you register your own `ConversionService` in the ApplicationContext and register your own `Converter<Person, Resource>`, then you can return a `Resource` implementation of your choosing.

View File

@@ -0,0 +1,9 @@
[[customizing-sdr]]
= Customizing Spring Data REST
There are many options to tailor Spring Data REST. These subsections will show how.
include::configuring-the-rest-url-path.adoc[leveloffset=+1]
include::adding-sdr-to-spring-mvc-app.adoc[leveloffset=+1]
include::customizing-json-output.adoc[leveloffset=+1]
include::custom-jackson-deserialization.adoc[leveloffset=+1]

View File

@@ -1,4 +1,4 @@
[[events-chapter]]
[[events]]
= Events
There are eight different events that the REST exporter emits throughout the process of working with an entity. Those are:
@@ -12,6 +12,7 @@ There are eight different events that the REST exporter emits throughout the pro
* `BeforeDeleteEvent`
* `AfterDeleteEvent`
[[events.application-listener]]
== Writing an ApplicationListener
There is an abstract class you can subclass which listens for these kinds of events and calls the appropriate method based on the event type. You just override the methods for the events you're interested in.
@@ -20,11 +21,13 @@ There is an abstract class you can subclass which listens for these kinds of eve
----
public class BeforeSaveEventListener extends AbstractRepositoryEventListener {
@Override public void onBeforeSave(Object entity) {
@Override
public void onBeforeSave(Object entity) {
... logic to handle inspecting the entity before the Repository saves it
}
@Override public void onAfterDelete(Object entity) {
@Override
public void onAfterDelete(Object entity) {
... send a message that this entity has been deleted
}
}

View File

@@ -0,0 +1,10 @@
[[example-api-usage-with-curl]]
[appendix]
= Using curl to talk to Spring Data REST
This appendix contains a list of guides that demonstrate interacting with a Spring Data REST service via curl:
* https://spring.io/guides/gs/accessing-data-rest/[Accessing JPA Data with REST]
* https://spring.io/guides/gs/accessing-neo4j-data-rest/[Accessing Neo4j Data with REST]
* https://spring.io/guides/gs/accessing-mongodb-data-rest/[Accessing MongoDB Data with REST]
* https://spring.io/guides/gs/accessing-gemfire-data-rest/[Accessing GemFire Data with REST]

View File

@@ -8,16 +8,46 @@ Spring Data REST is itself a Spring MVC application and is designed in such a wa
To install Spring Data REST alongside your application, simply add the required dependencies, include the stock `@Configuration` class `RepositoryRestMvcConfiguration` (or subclass it and perform any required manual configuration), and map some URLs to be managed by Spring Data REST.
[[getting-started.boot]]
== Adding Spring Data REST to a Spring Boot project
The simplest way to get to started is if you are building a Spring Boot application. That's because Spring Data REST has both a starter as well as auto-configuration.
.Spring Boot configuration with Gradle
[source,groovy]
----
dependencies {
...
compile("org.springframework.boot:spring-boot-starter-data-rest")
...
}
----
.Spring Boot configuration with Maven
[source,xml]
----
<dependencies>
...
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-rest</artifactId>
</dependency>
...
</dependencies>
----
NOTE: You don't have to supply the version number if you are using the http://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/#build-tool-plugins-gradle-plugin[Spring Boot Gradle plugin] or the http://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/#build-tool-plugins-maven-plugin[Spring Boot Maven plugin].
[[getting-started.gradle]]
== Adding Spring Data REST to a Gradle project
To add Spring Data REST to a Gradle-based project, add the `spring-data-rest-webmvc` artifact to your compile-time dependencies:
[source,groovy]
[source,groovy,subs="verbatim,attributes"]
----
dependencies {
… other project dependencies
compile "org.springframework.data:spring-data-rest-webmvc:${spring-data-rest-version}"
compile("org.springframework.data:spring-data-rest-webmvc:{version}")
}
----
@@ -26,12 +56,12 @@ dependencies {
To add Spring Data REST to a Maven-based project, add the `spring-data-rest-webmvc` artifact to your compile-time dependencies:
[source,xml]
[source,xml,subs="verbatim,attributes"]
----
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-rest-webmvc</artifactId>
<version>${spring-data-rest-version}</version>
<version>{version}</version>
</dependency>
----
@@ -40,6 +70,8 @@ To add Spring Data REST to a Maven-based project, add the `spring-data-rest-webm
To install Spring Data REST alongside your existing Spring MVC application, you need to include the appropriate MVC configuration. Spring Data REST configuration is defined in a class called `RepositoryRestMvcConfiguration`. You can either import this class into your existing configuration using an `@Import` annotation or you can subclass it and override any of the `configureXXX` methods to add your own configuration to that of Spring Data REST.
IMPORTANT: This step is unnecessary if you are using Spring Boot's auto-configuration. Spring Boot will automatically enable Spring Data REST when you include *spring-boot-starter-data-rest* and either in your list of dependencies, and you your app is flagged with either `@SpringBootApplication` or `@EnableAutoConfiguration`.
In the following example, we'll subclass the standard `RepositoryRestMvcConfiguration` and add some `ResourceMapping` configurations for the `Person` domain object to alter how the JSON will look and how the links to related entities will be handled.
[source,java]
@@ -57,32 +89,17 @@ Make sure you also configure Spring Data repositories for the store you use. For
[[getting-started.bootstrap]]
== Starting the application
As Spring Data REST is built on SpringMVC, you simply stick to the means you use to bootstrap Spring MVC. In a Servlet 3.0 environment this might look something like this:
At this point, you must also configure your key data store.
[source,java]
----
public class RestExporterWebInitializer implements WebApplicationInitializer {
Spring Data REST officially supports:
@Override public void onStartup(ServletContext servletContext) throws ServletException {
// Bootstrap repositories in root application context
AnnotationConfigWebApplicationContext rootCtx = new AnnotationConfigWebApplicationContext();
rootCtx.register(JpaRepositoryConfig.class); // Include JPA entities, Repositories
servletContext.addListener(new ContextLoaderListener(rootCtx));
// Enable Spring Data REST in the DispatcherServlet
AnnotationConfigWebApplicationContext webCtx = new AnnotationConfigWebApplicationContext();
webCtx.register(MyWebConfiguration.class);
DispatcherServlet dispatcherServlet = new DispatcherServlet(webCtx);
ServletRegistration.Dynamic reg = servletContext.addServlet("rest-exporter", dispatcherServlet);
reg.setLoadOnStartup(1);
reg.addMapping("/*");
}
}
----
The equivalent of the above in a standard web.xml will also work identically to this configuration if you are still in a servlet 2.5 environment. When you deploy this application to your servlet container, you should be able to see what repositories are exported by accessing the root of the application.
* https://spring.io/guides/gs/accessing-data-rest/[Spring Data JPA]
* https://spring.io/guides/gs/accessing-mongodb-data-rest/[Spring Data MongoDB]
* https://spring.io/guides/gs/accessing-neo4j-data-rest/[Spring Data Neo4j]
* https://spring.io/guides/gs/accessing-gemfire-data-rest/[Spring Data GemFire]
These linked guides introduce how to add dependencies for the related data store, configure domain objects, and define repositories.
You can run your application as either a Spring Boot app (with links showns above) or configure it as a classic Spring MVC app.
From this point, you can are free to <<customizing-sdr,customize Spring Data REST>> with various options.

View File

@@ -1,21 +1,35 @@
[[spring-data-rest-reference]]
= Spring Data REST Reference Documentation
Jon Brisbin, Oliver Gierke
Jon Brisbin, Oliver Gierke, Greg Turnquist
:revnumber: {version}
:revdate: {localdate}
:toc:
:idprefix:
{version}
:toc-placement!:
(C) 2012-2015 Original authors
NOTE: _Copies of this document may be made for your own use and for distribution to others, provided that you do not charge any fee for such copies and further provided that each copy contains this Copyright Notice, whether distributed in print or electronically._
:leveloffset: 1
include::intro.adoc[]
include::getting-started.adoc[]
include::repository-resources.adoc[]
include::representations.adoc[]
include::projections-excerpts.adoc[]
include::validation.adoc[]
include::events.adoc[]
include::metadata.adoc[]
toc::[]
include::preface.adoc[]
[[reference]]
= Reference Documentation
include::intro.adoc[leveloffset=+1]
include::getting-started.adoc[leveloffset=+1]
include::repository-resources.adoc[leveloffset=+1]
include::paging-and-sorting.adoc[leveloffset=+1]
include::representations.adoc[leveloffset=+1]
include::projections-excerpts.adoc[leveloffset=+1]
include::validation.adoc[leveloffset=+1]
include::events.adoc[leveloffset=+1]
include::metadata.adoc[leveloffset=+1]
include::customizing-sdr.adoc[leveloffset=+1]
[[appendix]]
= Appendix
:numbered!:
include::example-api-usage-with-curl.adoc[leveloffset=+1]

View File

@@ -3,13 +3,11 @@
This section details the various forms of metadata provided by a Spring Data REST-based application.
[[metadata.alps]]
== Application-Level Profile Semantics (ALPS)
[quote, M. Admundsen / L. Richardson / M. Foster, http://tools.ietf.org/html/draft-amundsen-richardson-foster-alps-00]
http://alps.io/[ALPS] is a data format for defining simple descriptions of application-level semantics, similar in complexity to
HTML microformats. An ALPS document can be used as a profile to explain the application semantics of a document with an application-
agnostic media type (such as HTML, HAL, Collection+JSON, Siren, etc.). This increases the reusability of profile documents across
media types.
http://alps.io/[ALPS] is a data format for defining simple descriptions of application-level semantics, similar in complexity to HTML microformats. An ALPS document can be used as a profile to explain the application semantics of a document with an application-agnostic media type (such as HTML, HAL, Collection+JSON, Siren, etc.). This increases the reusability of profile documents across media types.
Spring Data REST provides an ALPS document for every exported repository. It contains information about both the RESTful transitions
as well as the attributes of each repository.
@@ -123,7 +121,8 @@ of the attributes.
<4> The *type* is `UNSAFE` because this operation can alter the state of the system.
<5>
== Hypermedia control types
[[metadata.alps.control-types]]
=== Hypermedia control types
ALPS displays types for each hypermedia control. They include:
@@ -142,7 +141,8 @@ In the representation section up above, bits of data from the application are ma
is a link that involves a safe *GET* to retrive. Hence, it is marked *SAFE*. Hypermedia operations themselves map onto the types as
shown the table.
== ALPS with Projections
[[metadata.alps.projections]]
=== ALPS with Projections
If you define any projections, they are also listed in the ALPS metadata. Assuming we also defined *inlineAddress* and *noAddresses*, they
would appear inside the relevant operations, i.e. *GET* for the whole collection as well *GET* for a single resource. The following shows
@@ -200,11 +200,12 @@ Relationships rendered inside a projection result in inlining the data fields.
With all this information, a client should be able to deduce not only the RESTful transitions avaiable, but also, to some degree, the
data elements needed to interact.
== Adding custom details to your ALPS descriptions
[[metadata.alps.descriptions]]
=== Adding custom details to your ALPS descriptions
It's possible to create custom messages that appear in your ALPS metadata. Just create `rest-messages.properties` like this:
[soruce,proeperties]
[source,properties]
----
rest.description.person=A collection of people
rest.description.person.id=primary key used internally to store a person (not for RESTful usage)

View File

@@ -0,0 +1,130 @@
[[paging-and-sorting]]
= Paging and Sorting
_This documents Spring Data REST's usage of the Spring Data Repository paging and sorting abstractions. To familiarize yourself with those features, please see the Spring Data documentation for the Repository implementation you're using._
== Paging
Rather than return everything from a large result set, Spring Data REST recognizes some URL parameters that will influence the page size and starting page number.
If you extend `PagingAndSortingRepository<T, ID>` and access the list of all entities, you'll get links to the first 20 entities. To set the page size to any other number, add a `size` parameter:
----
http://localhost:8080/people/?size=5
----
This will set the page size to 5.
To use paging in your own query methods, you need to change the method signature to accept an additional `Pageable` parameter and return a `Page` rather than a `List`. For example, the following query method will be exported to `/people/search/nameStartsWith` and will support paging:
[source,java]
----
@RestResource(path = "nameStartsWith", rel = "nameStartsWith")
public Page findByNameStartsWith(@Param("name") String name, Pageable p);
----
The Spring Data REST exporter will recognize the returned `Page` and give you the results in the body of the response, just as it would with a non-paged response, but additional links will be added to the resource to represent the previous and next pages of data.
[[paging-and-sorting.prev-and-next-links]]
=== Previous and Next Links
Each paged response will return links to the previous and next pages of results based on the current page using the IANA defined link relations http://www.w3.org/TR/html5/links.html#link-type-prev[`prev`] and http://www.w3.org/TR/html5/links.html#link-type-next[`next`]. If you are currently at the first page of results, however, no `prev` link will be rendered. The same is true for the last page of results: no `next` link will be rendered.
Look at the following example, where we set the page size to 5:
----
curl localhost:8080/people?size=5
----
[source,javascript]
----
{
"_links" : {
"self" : {
"href" : "http://localhost:8080/persons{&sort,page,size}", <1>
"templated" : true
},
"next" : {
"href" : "http://localhost:8080/persons?page=1&size=5{&sort}", <2>
"templated" : true
}
},
"_embedded" : {
... data ...
},
"page" : { <3>
"size" : 5,
"totalElements" : 50,
"totalPages" : 10,
"number" : 0
}
}
----
At the top, we see `_links`:
<1> This `self` link serves up the whole collection with some options
<2> This `next` link points to the next page, assuming the same page size.
<3> At the bottom is extra data about the page settings, including the size of a page, total elements, total pages, and the page number you are currently viewing.
NOTE: When using tools like *curl* on the command line, if you have a "&" in your statement, wrap the whole URI with quotes.
It's also important to notice that the `self` and `next` URIs are, in fact, URI templates. They accept not only `size`, but also `page`, `sort` as optional flags.
As mentioned, at the bottom of the HAL document, is a collection of details about the page. This extra information makes it very easy for you to configure UI tools like sliders or indicators to reflect overall position the user is in viewing the data. For example, the document above shows we are looking at the first page (with page numbers indexed to 0 being the first).
What happens if we follow the `next` link?
----
$ curl "http://localhost:8080/persons?page=1&size=5"
----
[source,javascript]
----
{
"_links" : {
"self" : {
"href" : "http://localhost:8080/persons{&sort,projection,page,size}",
"templated" : true
},
"next" : {
"href" : "http://localhost:8080/persons?page=2&size=5{&sort,projection}", <1>
"templated" : true
},
"prev" : {
"href" : "http://localhost:8080/persons?page=0&size=5{&sort,projection}", <2>
"templated" : true
}
},
"_embedded" : {
... data ...
},
"page" : {
"size" : 5,
"totalElements" : 50,
"totalPages" : 10,
"number" : 1 <3>
}
}
----
This looks very similar, except for the following differences:
<1> The `next` link now points to yet another page, indicating it's relative perspective to the `self` link.
<2> A `prev` link now appears, giving us a path to the previous page.
<3> The current number is now 1 (indicating the second page).
This feature makes it quite easy to map optional buttons on the screen to these hypermedia controls, hence allowing easy navigational features for the UI experience without having to hard code the URIs. In fact, the user can be empowered to pick from a list of page sizes, dynamically changing the content served, without having to rewrite the `next` and `prev controls at the top or bottom.
[[paging-and-sorting.sorting]]
== Sorting
Spring Data REST recognizes sorting parameters that will use the Repository sorting support.
To have your results sorted on a particular property, add a `sort` URL parameter with the name of the property you want to sort the results on. You can control the direction of the sort by appending a `,` to the the property name plus either `asc` or `desc`. The following would use the `findByNameStartsWith` query method defined on the `PersonRepository` for all `Person` entities with names starting with the letter "K" and add sort data that orders the results on the `name` property in descending order:
----
curl -v "http://localhost:8080/people/search/nameStartsWith?name=K&sort=name,desc"
----
To sort the results by more than one property, keep adding as many `sort=PROPERTY` parameters as you need. They will be added to the `Pageable` in the order they appear in the query string.

View File

@@ -0,0 +1,13 @@
[[preface]]
= Preface
[[Project]]
[preface]
== Project metadata
* Version control - https://github.com/spring-projects/spring-data-rest
* Bugtracker - https://jira.spring.io/browse/DATAREST
* Project page - http://projects.spring.io/spring-data-rest
* Release repository - https://repo.spring.io/libs-release
* Milestone repository - https://repo.spring.io/libs-milestone
* Snapshot repository - https://repo.spring.io/libs-snapshot

View File

@@ -1,9 +1,7 @@
[[projections-excerpts]]
= Projections and Excerpts
Spring Data REST presents a default view of the domain model you are exporting. But sometimes, you may need to
alter the view of that model for various reasons. In this section, you will learn how to define *projections* and
*excerpts* to serve up simplified and reduced views of resources.
Spring Data REST presents a default view of the domain model you are exporting. But sometimes, you may need to alter the view of that model for various reasons. In this section, you will learn how to define projections and excerpts to serve up simplified and reduced views of resources.
== Projections
@@ -14,45 +12,37 @@ Look at the following domain model:
@Entity
public class Person {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private long id;
private String firstName;
private String lastName;
@Id @GeneratedValue
private Long id;
private String firstName, lastName;
@OneToOne
private Address address;
...
}
----
This `Person` has several attributes:
* *id* is the primary key
* *firstName* and *lastName* are data attributes
* *address* is a link to another domain object
* `id` is the primary key
* `firstName` and `lastName` are data attributes
* `address` is a link to another domain object
Now assume we create a corresponding repository as follows:
[source,java]
----
public interface PersonRepository extends CrudRepository<Person, Long> {}
interface PersonRepository extends CrudRepository<Person, Long> {}
----
By default, Spring Data REST will export this domain object including all of its attributes. *firstName* and *lastName* will be exported
as the plain data objects that they are. There are two options regarding the *address* attribute.
One option is to also define a repository for `Address` objects like this:
By default, Spring Data REST will export this domain object including all of its attributes. `firstName` and `lastName` will be exported as the plain data objects that they are. There are two options regarding the `address` attribute. One option is to also define a repository for `Address` objects like this:
[source,java]
----
public interface AddressRepository extends CrudRepository<Address, Long> {}
interface AddressRepository extends CrudRepository<Address, Long> {}
----
In this situation, a `Person` resource will render the *address* attribute as a URI to it's corresponding `Address` resource. If we were
to look up "Frodo" in the system, we could expect to see a HAL document like this:
In this situation, a `Person` resource will render the `address` attribute as a URI to it's corresponding `Address` resource. If we were to look up "Frodo" in the system, we could expect to see a HAL document like this:
[source,javascript]
----
@@ -70,8 +60,7 @@ to look up "Frodo" in the system, we could expect to see a HAL document like thi
}
----
There is another route. If the `Address` domain object does not have it's own repository definition, Spring Data REST will inline the data
fields right inside the `Person` resource.
There is another route. If the `Address` domain object does not have it's own repository definition, Spring Data REST will inline the data fields right inside the `Person` resource.
[source,javascript]
----
@@ -91,34 +80,29 @@ fields right inside the `Person` resource.
}
----
But what if you don't want *address* details at all? Again, by default, Spring Data REST will export all its attributes (except the *id*).
You can offer the consumer of your REST service an alternative by defining one or more *projections*.
But what if you don't want `address` details at all? Again, by default, Spring Data REST will export all its attributes (except the `id`). You can offer the consumer of your REST service an alternative by defining one or more projections.
[source,java]
----
@Projection(name = "noAddresses", types = { Person.class }) <1>
public interface NoAddresses { <2>
interface NoAddresses { <2>
public String getFirstName(); <3>
public String getLastName(); <4>
String getFirstName(); <3>
String getLastName(); <4>
}
----
This projection has the following details:
<1> The `@Projection` annotation flags this as a projection. The *name* atrributes provides
the name of the projection, which you'll see how to use shortly. The *types* attributes targets this projection to only apply to
`Person` objects.
<1> The `@Projection` annotation flags this as a projection. The `name` atrributes provides
the name of the projection, which you'll see how to use shortly. The `types` attributes targets this projection to only apply to `Person` objects.
<2> It's a Java interface making it declarative.
<3> It exports the *firstName*.
<4> It exports the *lastName*.
<3> It exports the `firstName`.
<4> It exports the `lastName`.
The `NoAddresses` projection only has getters for *firstName* and *lastName* meaning that it won't serve up any address information.
Assuming you have a separate repository for `Address` resources, the default view from Spring Data REST is slightly different as
shown below:
The `NoAddresses` projection only has getters for `firstName` and `lastName` meaning that it won't serve up any address information. Assuming you have a separate repository for `Address` resources, the default view from Spring Data REST is slightly different as shown below:
[source,javascript]
----
@@ -137,64 +121,57 @@ shown below:
}
----
<1> There is a new option listed for this resource, *{?projection}*.
<2> The *self* URI is a URI Template.
<1> There is a new option listed for this resource, `{?projection}`.
<2> The `self` URI is a URI Template.
To view apply the projection to the resource, look up `http://localhost:8080/persons/1/projection=noAddresses`.
NOTE: The value supplied to the `projection` query parameter is the same as specified in `@Projection(name = "noAddress")`. It has
nothing to do with the name of the projection's interface.
NOTE: The value supplied to the `projection` query parameter is the same as specified in `@Projection(name = "noAddress")`. It has nothing to do with the name of the projection's interface.
It's possible to have multiple projections.
=== Finding existing projections
Spring Data REST provides https://spring.io/blog/2014/07/14/spring-data-rest-now-comes-with-alps-metadata[ALPS], a micro metadata format.
To view the ALPS metadata, visit `http://localhost:8080/alps`. If you navigate down to the ALPS document for `Person` resources (which
would be `/alps/persons`), you can find many details about `Person` resources.
Projections will be listed along with the details about the *GET* REST transitions, something like this:
Spring Data REST provides hypermedia metadata by exposing <<metadata.alps>> documents, a micro metadata format. To view the ALPS metadata, follow the `profile` link exposed by the root resource. If you navigate down to the ALPS document for `Person` resources (which would be `/alps/persons`), you can find many details about `Person` resources. Projections will be listed along with the details about the `GET` REST transitions, something like this:
[source,javascript]
----
...
"id" : "get-person", <1>
"name" : "person",
"type" : "SAFE",
"rt" : "#person-representation",
"descriptors" : [ {
"name" : "projection", <2>
"doc" : {
"value" : "The projection that shall be applied when rendering the response. Acceptable values available in nested descriptors.",
"format" : "TEXT"
},
"type" : "SEMANTIC",
"descriptors" : [ {
"name" : "noAddresses", <3>
"type" : "SEMANTIC",
"descriptors" : [ {
"name" : "firstName", <4>
"type" : "SEMANTIC"
}, {
{ …
"id" : "get-person", <1>
"name" : "person",
"type" : "SAFE",
"rt" : "#person-representation",
"descriptors" : [ {
"name" : "projection", <2>
"doc" : {
"value" : "The projection that shall be applied when rendering the response. Acceptable values available in nested descriptors.",
"format" : "TEXT"
},
"type" : "SEMANTIC",
"descriptors" : [ {
"name" : "noAddresses", <3>
"type" : "SEMANTIC",
"descriptors" : [ {
"name" : "firstName", <4>
"type" : "SEMANTIC"
}, {
"name" : "lastName", <4>
"type" : "SEMANTIC"
} ]
} ]
} ]
} ]
} ]
},
...
----
<1> This part of the ALPS document shows details about *GET* and `Person` resources.
<2> Further down are the *projection* options.
<3> Further down you can see projection *noAddresses* listed.
<4> The actual attributes served up by this projection include *firstName* and *lastName*.
<1> This part of the ALPS document shows details about `GET` and `Person` resources.
<2> Further down are the `projection` options.
<3> Further down you can see projection `noAddresses` listed.
<4> The actual attributes served up by this projection include `firstName` and `lastName`.
=== Bringing in hidden data
So far, you have seen how projections can be used to reduce the information that is presented to the user. Projections can also bring
in normally unseen data. For example, Spring Data REST will ignore fields or getters that are marked up with `@JsonIgnore` annotations.
Look at the following domain object:
So far, you have seen how projections can be used to reduce the information that is presented to the user. Projections can also bring in normally unseen data. For example, Spring Data REST will ignore fields or getters that are marked up with `@JsonIgnore` annotations. Look at the following domain object:
[source,java]
----
@@ -203,21 +180,17 @@ public class User {
@Id @GeneratedValue
private Long id;
private String name;
@JsonIgnore <1>
private String password;
private String[] roles;
...
----
<1> Jackson's `@JsonIgnore` is used to prevent the *password* field from getting serialized into JSON.
<1> Jackson's `@JsonIgnore` is used to prevent the `password` field from getting serialized into JSON.
This `User` class can be used to store user information as well as integration with Spring Security. If you create a `UserRepository`,
the *password* field would normally have been exported. Not good! In this example, we prevent that from happening by applying Jackson's
`@JsonIgnore` on the *password* field.
This `User` class can be used to store user information as well as integration with Spring Security. If you create a `UserRepository`, the `password` field would normally have been exported. Not good! In this example, we prevent that from happening by applying Jackson's `@JsonIgnore` on the `password` field.
NOTE: Jackson will also not serialize the field into JSON if `@JsonIgnore` is on the field's corresponding getter function.
@@ -226,17 +199,15 @@ However, projections introduce the ability to still serve this field. It's possi
[source,java]
----
@Projection(name = "passwords", types = { User.class }) <2>
public interface PasswordProjection {
public String getPassword();
interface PasswordProjection {
String getPassword();
}
----
If such a projection is created and used, it will side step the `@JsonIgnore` directive placed on `User.password`.
IMPORTANT: This example may seem a bit contrived, but it's possible with a richer domain model and many projections, to accidentally
leak such details. Since Spring Data REST cannot discern the sensitivity of such data, it is up to the developers to avoid such situations.
IMPORTANT: This example may seem a bit contrived, but it's possible with a richer domain model and many projections, to accidentally leak such details. Since Spring Data REST cannot discern the sensitivity of such data, it is up to the developers to avoid such situations.
== Excerpts
@@ -245,7 +216,7 @@ An excerpt is a projection that is applied to a repository automatically. For an
[source,java]
----
@RepositoryRestResource(excerptProjection = NoAddresses.class)
public interface PersonRepository extends CrudRepository<Person, Long> {}
interface PersonRepository extends CrudRepository<Person, Long> {}
----
This directs Spring Data REST to use the `NoAddresses` projection when embedding `Person` resources into collections or related resources.
@@ -256,37 +227,30 @@ In addition to altering the default rendering, excerpts have additional rednerin
== Excerpting commonly accessed data
A common situation with REST services arises when you compose domain objects. For example, a `Person` is stored in one table and their
related `Address` is stored in another. By default, Spring Data REST will serve up the person's *address* as a URI the client must
navigate. But if it's common for consumers to always fetch this extra piece of data, an excerpt projection can go ahead and inline
this extra piece of data, saving you an extra *GET*.
To do so, let's define another excerpt projection:
A common situation with REST services arises when you compose domain objects. For example, a `Person` is stored in one table and their related `Address` is stored in another. By default, Spring Data REST will serve up the person's `address` as a URI the client must navigate. But if it's common for consumers to always fetch this extra piece of data, an excerpt projection can go ahead and inline this extra piece of data, saving you an extra `GET`. To do so, let's define another excerpt projection:
[source,java]
----
@Projection(name = "inlineAddress", types = { Person.class }) <1>
public interface InlineAddress {
interface InlineAddress {
public String getFirstName();
String getFirstName();
public String getLastName();
public Address getAddress(); <2>
String getLastName();
Address getAddress(); <2>
}
----
<1> This projection has been named *inlineAddress*.
<2> This projection adds in `getAddress` which returns the `Address` field. When used inside a projection, it causes the information
to be inlined.
<1> This projection has been named `inlineAddress`.
<2> This projection adds in `getAddress` which returns the `Address` field. When used inside a projection, it causes the information to be inlined.
We can plug it into the `PersonRepository` definition as follows:
[source,java]
----
@RepositoryRestResource(excerptProjection = InlineAddress.class)
public interface PersonRepository extends CrudRepository<Person, Long> {}
interface PersonRepository extends CrudRepository<Person, Long> {}
----
This will cause the HAL document to appear as follows:
@@ -314,8 +278,7 @@ This will cause the HAL document to appear as follows:
This should appear as a mix of what you've seen so far.
<1> The *address* data is inlined directly, so you don't have to navigate to get it.
<1> The `address` data is inlined directly, so you don't have to navigate to get it.
<2> The link to the `Address` resource is still provided, making it still possible to navigate to its own resource.
WARNING: Configuring `@RepositoryRestResource(excerptProjection=...)` for a repository alters the default behavior. This can potentially
case breaking change to consumers of your service if you have already made a release. Use with caution.
WARNING: Configuring `@RepositoryRestResource(excerptProjection=...)` for a repository alters the default behavior. This can potentially case breaking change to consumers of your service if you have already made a release. Use with caution.

View File

@@ -1,10 +1,10 @@
[[representations-chapter]]
[[representations]]
= Domain Object Representations
[[mapping]]
[[representations.mapping]]
== Object Mapping
Spring Data REST returns a representation of a domain object that corresponds to the requested `Accept` type specified in the HTTP request.
Spring Data REST returns a representation of a domain object that corresponds to the requested `Accept` type specified in the HTTP request.
Currently, only JSON representations are supported. Other representation types can be supported in the future by adding an appropriate converter and updating the controller methods with the appropriate content-type.
@@ -29,14 +29,14 @@ public class MyEntity {
In a default configuration, Jackson has no idea what class to instantiate when POSTing new data to the exporter. This is something you'll need to tell Jackson either through an annotation, or, more cleanly, by registering a type mapping using a `Module`.
To add your own Jackson configuration to the `ObjectMapper` used by Spring Data REST, override the `configureJacksonObjectMapper` method. That method will be passed an `ObjectMapper` instance that has a special module to handle serializing and deserializing `PersistentEntity`s. You can register your own modules as well, like in the following example.
To add your own Jackson configuration to the `ObjectMapper` used by Spring Data REST, override the `configureJacksonObjectMapper` method. That method will be passed an `ObjectMapper` instance that has a special module to handle serializing and deserializing `PersistentEntity`s. You can register your own modules as well, like in the following example.
[source,java]
----
@Override
@Override
protected void configureJacksonObjectMapper(ObjectMapper objectMapper) {
objectMapper.registerModule(new SimpleModule("MyCustomModule") {
@Override
@Override
public void setupModule(SetupContext context) {
context.addAbstractTypeResolver(
new SimpleAbstractTypeResolver().addMapping(MyInterface.class,
@@ -55,7 +55,7 @@ If you want to (de)serialize a domain type in a special way, you can register yo
[source,java]
----
@Override
@Override
public void setupModule(SetupContext context) {
SimpleSerializers serializers = new SimpleSerializers();
SimpleDeserializers deserializers = new SimpleDeserializers();
@@ -67,3 +67,4 @@ public void setupModule(SetupContext context) {
context.addDeserializers(deserializers);
}
----

View File

@@ -1,17 +1,17 @@
[[validation-chapter]]
[[validation]]
= Validation
There are two ways to register a `Validator` instance in Spring Data REST: wire it by bean name or register the validator manually. For the majority of cases, the simple bean name prefix style will be sufficient.
There are two ways to register a `Validator` instance in Spring Data REST: wire it by bean name or register the validator manually. For the majority of cases, the simple bean name prefix style will be sufficient.
In order to tell Spring Data REST you want a particular `Validator` assigned to a particular event, you simply prefix the bean name with the event you're interested in. For example, to validate instances of the `Person` class before new ones are saved into the repository, you would declare an instance of a `Validator<Person>` in your `ApplicationContext` with the bean name "beforeCreatePersonValidator". Since the prefix "beforeCreate" matches a known Spring Data REST event, that validator will be wired to the correct event.
In order to tell Spring Data REST you want a particular `Validator` assigned to a particular event, you simply prefix the bean name with the event you're interested in. For example, to validate instances of the `Person` class before new ones are saved into the repository, you would declare an instance of a `Validator<Person>` in your `ApplicationContext` with the bean name "beforeCreatePersonValidator". Since the prefix "beforeCreate" matches a known Spring Data REST event, that validator will be wired to the correct event.
== Assigning Validators manually
If you would rather not use the bean name prefix approach, then you simply need to register an instance of your validator with the bean whose job it is to invoke validators after the correct event. In your configuration that subclasses Spring Data REST's `RepositoryRestMvcConfiguration`, override the `configureValidatingRepositoryEventListener` method and call the `addValidator` method on the `ValidatingRepositoryEventListener`, passing the event you want this validator to be triggered on, and an instance of the validator.
If you would rather not use the bean name prefix approach, then you simply need to register an instance of your validator with the bean whose job it is to invoke validators after the correct event. In your configuration that subclasses Spring Data REST's `RepositoryRestMvcConfiguration`, override the `configureValidatingRepositoryEventListener` method and call the `addValidator` method on the `ValidatingRepositoryEventListener`, passing the event you want this validator to be triggered on, and an instance of the validator.
[source,java]
----
@Override
@Override
protected void configureValidatingRepositoryEventListener(ValidatingRepositoryEventListener v) {
v.addValidator("beforeSave", new BeforeSaveValidator());
}