|
|
大方的金针菇 · java 我现在有个list ...· 1 年前 · |
|
|
谦逊的电梯 · MySQL 8.0 ...· 1 年前 · |
|
|
魁梧的汤圆 · 第 7 部分 - 验证、工具和故障排除 - ...· 2 年前 · |
|
|
暴躁的香烟 · matlab msgbox ...· 2 年前 · |
| 1. Working with Spring Data Repositories | ||
|---|---|---|
| Prev | Part I. Reference | |
|
Important |
|---|---|
|
Spring Data repository documentation and your module This chapter explains the core concepts and interfaces of Spring Data repositories. The information in this chapter is pulled from the Spring Data Commons module. It uses the configuration and code samples for the Java Persistence API (JPA) module. Adapt the XML namespace declaration and the types to be extended to the equivalents of the particular module that you are using. ??? covers XML configuration which is supported across all Spring Data modules supporting the repository API, ??? covers the query method method keywords supported by the repository abstraction in general. For detailed information on the specific features of your module, consult the chapter on that module of this document. |
Usually we will have persistence technology specific sub-interfaces
to include additional technology specific methods. We will now ship
implementations for a variety of Spring Data modules that implement
CrudRepository
.
On top of the
CrudRepository
there is
a
PagingAndSortingRepository
abstraction
that adds additional methods to ease paginated access to entities:
Example 1.2. PagingAndSortingRepository
public interface PagingAndSortingRepository<T, ID extends Serializable> extends CrudRepository<T, ID> { Iterable<T> findAll(Sort sort); Page<T> findAll(Pageable pageable); }
Accessing the second page of
User
by a page
size of 20 you could simply do something like this:
PagingAndSortingRepository<User, Long> repository = // … get access to a bean Page<User> users = repository.findAll(new PageRequest(1, 20));
public interface PersonRepository extends Repository<User, Long> { … }
Declare query methods on the interface.
List<Person> findByLastname(String lastname);
Set up Spring to create proxy instances for those interfaces.
<?xml version="1.0" encoding="UTF-8"?> <beans:beans xmlns:beans="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://www.springframework.org/schema/data/jpa" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/data/jpa http://www.springframework.org/schema/data/jpa/spring-jpa.xsd"> <repositories base-package="com.acme.repositories" /> </beans>
|
Note |
|---|---|
|
The JPA namespace is used in this example. If you are using
the repository abstraction for any other store, you need to change
this to the appropriate namespace declaration of your store module
which should be exchanging
|
Get the repository instance injected and use it.
public class SomeClient { @Autowired private PersonRepository repository; public void doSomething() { List<Person> persons = repository.findByLastname("Matthews"); }
The sections that follow explain each step.
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”
.
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 expressions are usually property traversals combined
with operators that can be concatenated. You can combine
property expressions with
AND
and
OR
.
You also get support for operators such as
Between
,
LessThan
,
GreaterThan
,
Like
for the
property expressions. The supported operators can vary by
datastore, so consult the appropriate part of your reference
documentation.
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”
.
List<Person> findByAddressZipCode(ZipCode zipCode);
List<Person> findByAddress_ZipCode(ZipCode zipCode);
<?xml version="1.0" encoding="UTF-8"?> <beans:beans xmlns:beans="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://www.springframework.org/schema/data/jpa" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/data/jpa http://www.springframework.org/schema/data/jpa/spring-jpa.xsd"> <repositories base-package="com.acme.repositories" /> </beans:beans>
By default the infrastructure picks up every interface
extending the persistence technology-specific
Repository
subinterface located under
the configured base package and creates a bean instance for it.
However, you might want more fine-grained control over which
interfaces bean instances get created for. To do this you use
<include-filter />
and
<exclude-filter
/>
elements inside
<repositories />
.
The semantics are exactly equivalent to the elements in Spring's
context namespace. For details, see
Spring reference documentation
on these
elements.
For example, to exclude certain interfaces from instantiation as repository, you could use the following configuration:
The repository infrastructure can also be triggered using a
store-specific
@Enable${store}Repositories
annotation
on a JavaConfig class. For an introduction into Java-based
configuration of the Spring container, see the reference
documentation.
[1]
A sample configuration to enable Spring Data repositories looks something like this.
|
Note |
|---|---|
|
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
|
Example 1.10. Implementation of custom repository functionality
class UserRepositoryImpl implements UserRepositoryCustom { public void someCustomMethod(User user) { // Your custom implementation }
|
Note |
|---|---|
|
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
public interface UserRepository extends CrudRepository<User, Long>, UserRepositoryCustom { // Declare query methods here }
Let your standard repository interface extend the custom one. Doing so makes CRUD and custom functionality available to clients.
The first configuration example will try to look up a class
com.acme.repository.UserRepositoryImpl
to act
as custom repository implementation, where the second example will try
to lookup
com.acme.repository.UserRepositoryFooBar
.
Now your individual repository interfaces will extend this
intermediate interface instead of the
Repository
interface to include the
functionality declared.
The default behavior of the Spring
<repositories
/>
namespace is to provide an implementation for all
interfaces that fall under the
base-package
. This means
that if left in its current state, an implementation instance of
MyRepository
will be created by
Spring. This is of course not desired as it is just supposed to act
as an intermediary between
Repository
and the actual repository interfaces you want to define for each
entity. To exclude an interface that extends
Repository
from being instantiated as
a repository instance, you can either annotate it with
@NoRepositoryBean
or move it outside
of the configured
base-package
.
|
Note |
|---|---|
|
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 Section 1.4.3, “Legacy web support” . Also note that the JavaConfig support introduced in Spring Data Commons 1.6 requires Spring 3.2 due to some issues with JavaConfig and overridden methods in Spring 3.1. |
[2]
In general, the integration support is enabled by using the
@EnableSpringDataWebSupport
annotation in
your JavaConfig configuration class.
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.
Alternatively, if you are using XML configuration, register either SpringDataWebSupport or HateoasAwareSpringDataWebSupport as Spring beans:
Example 1.19. Enabling Spring Data web support in XML
<bean class="org.springframework.data.web.config.SpringDataWebConfiguration" /> <!-- If you're using Spring HATEOAS as well register this one *instead* of the former --> <bean class="org.springframework.data.web.config.HateoasAwareSpringDataWebConfiguration" />
The configuration setup shown above will register a few basic components:
This method signature will cause Spring MVC try to derive a
Pageable
instance from the request
parameters using the following default configuration:
Table 1.1. 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
Pageable
s or
Sort
s 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) { … }
you have to populate
foo_page
and
bar_page
etc.
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.
Assume you have a file
data.json
with the
following content:
You can easily populate your repositories by using the populator
elements of the repository namespace provided in Spring Data Commons. To
populate the preceding data to your
PersonRepository
, do the
following:
Example 1.24. Declaring a Jackson repository populator
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:repository="http://www.springframework.org/schema/data/repository" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/data/repository http://www.springframework.org/schema/data/repository/spring-repository.xsd"> <repository:jackson-populator location="classpath:data.json" /> </beans>
This declaration causes the
data.json
file
being read, deserialized by a Jackson
ObjectMapper
. The type to which the JSON object will be unmarshalled to will
be determined by inspecting the
_class
attribute of the
JSON document. The infrastructure will eventually select the appropriate
repository to handle the object just deserialized.
To rather use XML to define the data the repositories shall be
populated with, you can use the
unmarshaller-populator
element. You configure it to use one of the XML marshaller options
Spring OXM provides you with. See the
Spring reference
documentation
for details.
@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"; }
<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>
@Controller @RequestMapping("/users") public class UserController { @RequestMapping("/{id}") public String showUserForm(@PathVariable("id") User user, Model model) { model.addAttribute("user", user); return "userForm"; }
<mvc:annotation-driven conversion-service="conversionService" /> <bean class="org.springframework.data.repository.support.DomainClassConverter"> <constructor-arg ref="conversionService" /> </bean>
class WebConfiguration extends WebMvcConfigurationSupport { // Other configuration omitted @Bean public DomainClassConverter<?> domainClassConverter() { return new DomainClassConverter<FormattingConversionService>(mvcConversionService()); }
@Controller @RequestMapping("/users") public class UserController { // DI code omitted @RequestMapping public String showUsers(Model model, HttpServletRequest request) { int page = Integer.parseInt(request.getParameter("page")); int pageSize = Integer.parseInt(request.getParameter("pageSize")); Pageable pageable = new PageRequest(page, pageSize); model.addAttribute("users", userService.getUsers(pageable)); return "users"; }
@Configuration public class WebConfig extends WebMvcConfigurationSupport { @Override public void configureMessageConverters(List<HttpMessageConverter<?>> converters) { converters.add(new PageableHandlerArgumentResolver()); }
If you're stuck with XML configuration you can register the resolver as follows:
<bean class="….web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter"> <property name="customArgumentResolvers"> <bean class="org.springframework.data.web.PageableHandlerArgumentResolver" /> </list> </property> </bean>
@Controller @RequestMapping("/users") public class UserController { @RequestMapping public String showUsers(Model model, Pageable pageable) { model.addAttribute("users", userRepository.findAll(pageable)); return "users"; }
In case you need multiple
Pageable
s 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) { … }
you have to populate
foo_page
and
bar_page
and the related subproperties.