diff --git a/src/docbkx/repositories.xml b/src/docbkx/repositories.xml
index b91a467fb..7cacd1650 100644
--- a/src/docbkx/repositories.xml
+++ b/src/docbkx/repositories.xml
@@ -855,280 +855,303 @@ public class MyRepositoryFactoryBean<R extends JpaRepository<T, I>, T,
Spring Data usage in a variety of contexts. Currently most of the
integration is targeted towards Spring MVC.
-
- Domain class web binding for Spring MVC
+
+ Web support
+
+
+ This section contains the documentation for the Spring Data web
+ support as it is implemented as of Spring Data Commons in the 1.6
+ range. As it the newly introduced support changes quite a lot of
+ things we kept the documentation of the former behavior in .
+
+
+ Spring Data modules ships with a variety of web support if the
+ module supports the repository programming model. The web related stuff
+ requires Spring MVC JARs on the classpath, some of them even provide
+ integration with Spring HATEOAS.
+
+
+ Spring HATEOAS - https://github.com/SpringSource/spring-hateoas
+ In general, the integration support is enabled by using the
+ @EnableSpringDataWebSupport annotation in
+ your JavaConfig configuration class.
- Given you are developing a Spring MVC web application you
- typically have to resolve domain class ids from URLs. By default your
- task is to transform that request parameter or URL part into the domain
- class to hand it to layers below then or execute business logic on the
- entities directly. This would look something like this:
-
- @Controller
-@RequestMapping("/users")
-public class UserController {
-
- private final UserRepository userRepository;
-
- @Autowired
- public UserController(UserRepository userRepository) {
- Assert.notNull(repository, "Repository must not be null!");
- userRepository = userRepository;
- }
-
- @RequestMapping("/{id}")
- public String showUserForm(@PathVariable("id") Long id, Model model) {
-
- // Do null check for id
- User user = userRepository.findOne(id);
- // Do null check for user
-
- model.addAttribute("user", user);
- return "user";
- }
-}
-
- First you declare a repository dependency for each controller to
- look up the entity managed by the controller or repository respectively.
- Looking up the entity is boilerplate as well, as it's always a
- findOne(…) call. Fortunately Spring provides
- means to register custom components that allow conversion between a
- String value to an arbitrary type.
-
-
- PropertyEditors
-
- For Spring versions before 3.0 simple Java
- PropertyEditors had to be used. To
- integrate with that, Spring Data offers a
- DomainClassPropertyEditorRegistrar, which looks
- up all Spring Data repositories registered in the
- ApplicationContext and registers a
- custom PropertyEditor for the managed
- domain class.
-
- <bean class="….web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter">
- <property name="webBindingInitializer">
- <bean class="….web.bind.support.ConfigurableWebBindingInitializer">
- <property name="propertyEditorRegistrars">
- <bean class="org.springframework.data.repository.support.DomainClassPropertyEditorRegistrar" />
- </property>
- </bean>
- </property>
-</bean>
+
+ Enabling Spring Data web support
- If you have configured Spring MVC as in the preceding example,
- you can configure your controller as follows, which reduces a lot of
- the clutter and boilerplate.
+ @Configuration
+@EnableWebMvc
+@EnableSpringDataWebSupport
+class WebConfiguration { }
+
- @Controller
-@RequestMapping("/users")
-public class UserController {
+ The @EnableSpringDataWebSupport annotation registers a few
+ components we will discuss in a bit. It will also detect Spring HATEOAS
+ on the classpath and register integration components for it as well if
+ present.
- @RequestMapping("/{id}")
- public String showUserForm(@PathVariable("id") User user, Model model) {
+ Alternatively, if you are using XML configuration, register either
+ SpringDataWebSupport or HateoasAwareSpringDataWebSupport as Spring
+ beans:
- model.addAttribute("user", user);
- return "userForm";
- }
-}
-
+
+ Enabling Spring Data web support in XML
-
- ConversionService
+ <bean class="org.springframework.data.web.config.SpringDataWebConfiguration" />
- In Spring 3.0 and later the
- PropertyEditor support is superseded by
- a new conversion infrastructure that eliminates the drawbacks of
- PropertyEditors and uses a stateless X
- to Y conversion approach. Spring Data now ships with a
- DomainClassConverter that mimics the behavior
- of DomainClassPropertyEditorRegistrar. To
- configure, simply declare a bean instance and pipe the
- ConversionService being used into its
- constructor:
+<!-- If you're using Spring HATEOAS as well register this one *instead* of the former -->
+<bean class="org.springframework.data.web.config.HateoasAwareSpringDataWebConfiguration" />
+
- <mvc:annotation-driven conversion-service="conversionService" />
+
+ Basic web support
-<bean class="org.springframework.data.repository.support.DomainClassConverter">
- <constructor-arg ref="conversionService" />
-</bean>
-
- If you are using JavaConfig, you can simply extend Spring MVC's
- WebMvcConfigurationSupport and hand the
- FormatingConversionService that the
- configuration superclass provides into the
- DomainClassConverter instance you
- create.
+ The configuration setup shown above will register a few basic
+ components:
- class WebConfiguration extends WebMvcConfigurationSupport {
+
+
+ A DomainClassConverter to enable
+ Spring MVC to resolve instances of repository managed domain
+ classes from request parameters or path variables.
+
- // Other configuration omitted
+
+ HandlerMethodArgumentResolver
+ implementations to let Spring MVC resolve
+ Pageable and
+ Sort instances from request
+ parameters.
+
+
- @Bean
- public DomainClassConverter<?> domainClassConverter() {
- return new DomainClassConverter<FormattingConversionService>(mvcConversionService());
- }
-}
-
-
+
+ DomainClassConverter
-
- If you're stuck with XML configuration you can register the
- resolver as follows:
+
+ HandlerMethodArgumentResolvers for Pageable and Sort
- <bean class="….web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter">
- <property name="customArgumentResolvers">
- <list>
- <bean class="org.springframework.data.web.PageableHandlerArgumentResolver" />
- </list>
- </property>
-</bean>
+ The configuration snippet above also registers a
+ PageableHandlerMethodArgumentResolver as well
+ as an instance of
+ SortHandlerMethodArgumentResolver. The
+ registration enables Pageable and
+ Sort being valid controller method
+ arguments
- When using Spring 3.0.x versions use the
- PageableArgumentResolver instead. Once you've
- configured the resolver with Spring MVC it allows you to simplify
- controllers down to something like this:
+
+ Using Pageable as controller method argument
- @Controller
+ @Controller
@RequestMapping("/users")
public class UserController {
+ @Autowired UserRepository repository;
+
@RequestMapping
public String showUsers(Model model, Pageable pageable) {
- model.addAttribute("users", userRepository.findAll(pageable));
+ model.addAttribute("users", repository.findAll(pageable));
return "users";
}
}
+
- The PageableArgumentResolver automatically
- resolves request parameters to build a
- PageRequest instance. By default it expects the
- following structure for the request parameters.
-
-
- Request parameters evaluated by
- PageableArgumentResolver
-
-
-
-
-
-
-
-
- page
-
- Page you want to retrieve.
-
-
-
- page.size
+ This method signature will cause Spring MVC try to derive a
+ Pageable instance from the request
+ parameters using the following default configuration:
+
+
+ Request parameters evaluated for Pageable instances
+
+
+
+
+
+
+
+
+ page
+
+ Page you want to retrieve.
+
+
+
+ size
+
+ Size of the page you want to retrieve.
+
+
+
+ sort
+
+ Properties that should be sorted by in the format
+ property,property(,ASC|DESC). Default sort
+ direction is ascending. Use multiple sort
+ parameters if you want to switch directions, e.g.
+ ?sort=firstname&sort=lastname,asc.
+
+
+
+
+
+ To customize this behavior extend either
+ SpringDataWebConfiguration or the
+ HATEOAS-enabled equivalent and override the
+ pageableResolver() or
+ sortResolver() methods and import your
+ customized configuration file instead of using the
+ @Enable-annotation.
+
+ In case you need multiple
+ Pageables or
+ Sorts to be resolved from the request (for
+ multiple tables, for example) you can use Spring's
+ @Qualifier annotation to distinguish
+ one from another. The request parameters then have to be prefixed
+ with ${qualifier}_. So for a method signature like
+ this:
+
+ public String showUsers(Model model,
+ @Qualifier("foo") Pageable first,
+ @Qualifier("bar") Pageable second) { … }
- Size of the page you want to retrieve.
-
+ you have to populate foo_page and
+ bar_page etc.
-
- page.sort
+ The default Pageable handed
+ into the method is equivalent to a new PageRequest(0,
+ 20) but can be customized using the
+ @PageableDefaults annotation on the
+ Pageable parameter.
+
+
- Property that should be sorted by.
-
+
+ Hypermedia support for Pageables
-
- page.sort.dir
+ Spring HATEOAS ships with a representation model class
+ PagedResources that allows enrichting the content of a Page instance
+ with the necessary Page metadata as well as links to let the clients
+ easily navigate the pages. The conversion of a Page to a
+ PagedResources is done by an implementation of the Spring HATEOAS
+ ResourceAssembler interface, the PagedResourcesAssembler.
- Direction that should be used for sorting.
-
-
-
-
+
+ Using a PagedResourcesAssembler as controller method
+ argument
- In case you need multiple Pageables
- to be resolved from the request (for multiple tables, for example) you
- can use Spring's @Qualifier annotation to
- distinguish one from another. The request parameters then have to be
- prefixed with ${qualifier}_. So for a method signature like
- this:
+ @Controller
+class PersonController {
- public String showUsers(Model model,
- @Qualifier("foo") Pageable first,
- @Qualifier("bar") Pageable second) { … }
+ @Autowired PersonRepository repository;
- you have to populate foo_page and
- bar_page and the related subproperties.
+ @RequestMapping(value = "/persons", method = RequestMethod.GET)
+ HttpEntity<PagedResources<Person>> persons(Pageable pageable,
+ PagedResourcesAssembler assembler) {
-
- Configuring a global default on bean declaration
-
- The PageableArgumentResolver will use a
- PageRequest with the first page and a page size
- of 10 by default. It will use that value if it cannot resolve a
- PageRequest from the request (because of
- missing parameters, for example). You can configure a global default
- on the bean declaration directly. If you might need controller method
- specific defaults for the Pageable,
- annotate the method parameter with
- @PageableDefaults and specify page
- (through pageNumber), page size (through
- value), sort (list of properties to sort
- by), and sortDir (the direction to sort by) as annotation
- attributes:
+ Page<Person> persons = repository.findAll(pageable);
+ return new ResponseEntity<>(assembler.toResources(persons), HttpStatus.OK);
+ }
+}
+
- public String showUsers(Model model,
- @PageableDefaults(pageNumber = 0, value = 30) Pageable pageable) { … }
-
+ Enabling the configuration as shown above allows the
+ PagedResourcesAssembler to be used as
+ controller method argument. Calling
+ toResources(…) on it will cause the
+ following:
+
+
+
+ The content of the Page will
+ become the content of the PagedResources
+ instance.
+
+
+
+ The PagedResources will get a
+ PageMetadata instance attached populated
+ with information form the Page and
+ the underlying PageRequest.
+
+
+
+ The PagedResources gets
+ prev and next links attached depending
+ on the page's state. The links will point to the URI the method
+ invoked is mapped to. The pagination parameters added to the
+ method will match the setup of the
+ PageableHandlerMethodArgumentResolver to
+ make sure the links can be resolved later on.
+
+
+
+ Assume we have 30 Person instances in the
+ database. You can now trigger a request GET
+ http://localhost:8080/persons and you'll see something similar
+ to this:
+
+ { "links" : [ { "rel" : "next",
+ "href" : "http://localhost:8080/persons?page=1&size=20 }
+ ],
+ "content" : [
+ … // 20 Person instances rendered here
+ ],
+ "pageMetadata" : {
+ "size" : 20,
+ "totalElements" : 30,
+ "totalPages" : 2,
+ "number" : 0
+ }
+}You see that the assembler produced the correct URI and also
+ picks up the default configuration present to resolve the parameters
+ into a Pageable for an upcoming
+ request. This means, if you change that configuration, the links will
+ automatically adhere to the change. By default the assembler points to
+ the controller method it was invoked in but that can be customized by
+ handing in a custom Link to be used as base to
+ build the pagination links to overloads of the
+ PagedResourcesAssembler.toResource(…) method.
+
@@ -1218,5 +1241,282 @@ processing.-->The type to which the JSON object will be unmarshalled to will
</beans>
+
+
+ Legacy web support
+
+
+ Domain class web binding for Spring MVC
+
+ Given you are developing a Spring MVC web application you
+ typically have to resolve domain class ids from URLs. By default your
+ task is to transform that request parameter or URL part into the
+ domain class to hand it to layers below then or execute business logic
+ on the entities directly. This would look something like this:
+
+ @Controller
+@RequestMapping("/users")
+public class UserController {
+
+ private final UserRepository userRepository;
+
+ @Autowired
+ public UserController(UserRepository userRepository) {
+ Assert.notNull(repository, "Repository must not be null!");
+ userRepository = userRepository;
+ }
+
+ @RequestMapping("/{id}")
+ public String showUserForm(@PathVariable("id") Long id, Model model) {
+
+ // Do null check for id
+ User user = userRepository.findOne(id);
+ // Do null check for user
+
+ model.addAttribute("user", user);
+ return "user";
+ }
+}
+
+ First you declare a repository dependency for each controller to
+ look up the entity managed by the controller or repository
+ respectively. Looking up the entity is boilerplate as well, as it's
+ always a findOne(…) call. Fortunately Spring
+ provides means to register custom components that allow conversion
+ between a String value to an arbitrary
+ type.
+
+
+ PropertyEditors
+
+ For Spring versions before 3.0 simple Java
+ PropertyEditors had to be used. To
+ integrate with that, Spring Data offers a
+ DomainClassPropertyEditorRegistrar, which
+ looks up all Spring Data repositories registered in the
+ ApplicationContext and registers a
+ custom PropertyEditor for the managed
+ domain class.
+
+ <bean class="….web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter">
+ <property name="webBindingInitializer">
+ <bean class="….web.bind.support.ConfigurableWebBindingInitializer">
+ <property name="propertyEditorRegistrars">
+ <bean class="org.springframework.data.repository.support.DomainClassPropertyEditorRegistrar" />
+ </property>
+ </bean>
+ </property>
+</bean>
+
+ If you have configured Spring MVC as in the preceding example,
+ you can configure your controller as follows, which reduces a lot of
+ the clutter and boilerplate.
+
+ @Controller
+@RequestMapping("/users")
+public class UserController {
+
+ @RequestMapping("/{id}")
+ public String showUserForm(@PathVariable("id") User user, Model model) {
+
+ model.addAttribute("user", user);
+ return "userForm";
+ }
+}
+
+
+
+ ConversionService
+
+ In Spring 3.0 and later the
+ PropertyEditor support is superseded
+ by a new conversion infrastructure that eliminates the drawbacks of
+ PropertyEditors and uses a stateless
+ X to Y conversion approach. Spring Data now ships with a
+ DomainClassConverter that mimics the behavior
+ of DomainClassPropertyEditorRegistrar. To
+ configure, simply declare a bean instance and pipe the
+ ConversionService being used into its
+ constructor:
+
+ <mvc:annotation-driven conversion-service="conversionService" />
+
+<bean class="org.springframework.data.repository.support.DomainClassConverter">
+ <constructor-arg ref="conversionService" />
+</bean>
+
+ If you are using JavaConfig, you can simply extend Spring
+ MVC's WebMvcConfigurationSupport and hand the
+ FormatingConversionService that the
+ configuration superclass provides into the
+ DomainClassConverter instance you
+ create.
+
+ class WebConfiguration extends WebMvcConfigurationSupport {
+
+ // Other configuration omitted
+
+ @Bean
+ public DomainClassConverter<?> domainClassConverter() {
+ return new DomainClassConverter<FormattingConversionService>(mvcConversionService());
+ }
+}
+
+
+
+
+