此版本仍在开发中,尚未被视为稳定版本。对于最新的稳定版本,请使用 Spring Data Neo4j 7.4.4!spring-doc.cadn.net.cn

Spring 数据扩展

本节记录了一组 Spring Data 扩展,这些扩展支持在各种上下文中使用 Spring Data。 目前,大多数集成都针对 Spring MVC。spring-doc.cadn.net.cn

Querydsl 扩展

Querydsl 是一个框架,支持通过其 Fluent API 构建静态类型的类似 SQL 的查询。spring-doc.cadn.net.cn

Querydsl 维护已经放慢到社区在 OpenFeign 下分叉项目的程度,github.com/OpenFeign/querydsl (groupId)io.github.openfeign.querydsl). Spring Data 尽最大努力支持 fork。

几个 Spring Data 模块通过以下方式提供与 Querydsl 的集成QuerydslPredicateExecutor,如下例所示:spring-doc.cadn.net.cn

QuerydslPredicateExecutor 接口
public interface QuerydslPredicateExecutor<T> {

  Optional<T> findById(Predicate predicate);  (1)

  Iterable<T> findAll(Predicate predicate);   (2)

  long count(Predicate predicate);            (3)

  boolean exists(Predicate predicate);        (4)

  // … more functionality omitted.
}
1 查找并返回与Predicate.
2 查找并返回与Predicate.
3 返回与Predicate.
4 返回是否与Predicate存在。

要使用 Querydsl 支持,请扩展QuerydslPredicateExecutor在您的存储库界面上,如下例所示:spring-doc.cadn.net.cn

存储库上的 Querydsl 集成
interface UserRepository extends CrudRepository<User, Long>, QuerydslPredicateExecutor<User> {
}

前面的示例允许您使用 Querydsl 编写类型安全的查询Predicate实例,如下例所示:spring-doc.cadn.net.cn

Predicate predicate = user.firstname.equalsIgnoreCase("dave")
	.and(user.lastname.startsWithIgnoreCase("mathews"));

userRepository.findAll(predicate);

Web 支持

支持存储库编程模型的 Spring Data 模块附带了各种 Web 支持。 与 Web 相关的组件要求 Spring MVC JAR 位于 Classpath 上。 其中一些甚至提供与 Spring HATEOAS 的集成。 通常,集成支持是通过使用@EnableSpringDataWebSupport注解,如下例所示:spring-doc.cadn.net.cn

启用 Spring Data Web 支持
@Configuration
@EnableWebMvc
@EnableSpringDataWebSupport
class WebConfiguration {}
<bean class="org.springframework.data.web.config.SpringDataWebConfiguration" />

<!-- If you use Spring HATEOAS, register this one *instead* of the former -->
<bean class="org.springframework.data.web.config.HateoasAwareSpringDataWebConfiguration" />

@EnableSpringDataWebSupportannotation 注册了一些组件。 我们将在本节后面讨论这些内容。 它还在 Classpath 上检测 Spring HATEOAS,并为其注册集成组件(如果存在)。spring-doc.cadn.net.cn

基本 Web 支持

在 XML 中启用 Spring Data Web 支持

上一节中所示的配置注册了一些基本组件:spring-doc.cadn.net.cn

使用DomainClassConverter

DomainClassConverter类允许你直接在 Spring MVC 控制器方法签名中使用域类型,这样你就不需要通过存储库手动查找实例,如下例所示:spring-doc.cadn.net.cn

在方法签名中使用域类型的 Spring MVC 控制器
@Controller
@RequestMapping("/users")
class UserController {

  @RequestMapping("/{id}")
  String showUserForm(@PathVariable("id") User user, Model model) {

    model.addAttribute("user", user);
    return "userForm";
  }
}

该方法接收一个User实例,无需进一步查找。 可以通过让 Spring MVC 将 path 变量转换为idtype 的 domain 类,最终通过调用findById(…)在为域类型注册的存储库实例上。spring-doc.cadn.net.cn

目前,存储库必须实现CrudRepository才有资格被发现进行转化。

用于 Pageable 和 Sort 的 HandlerMethodArgumentResolvers

上一节中所示的配置代码段还注册了一个PageableHandlerMethodArgumentResolver以及SortHandlerMethodArgumentResolver. 注册启用PageableSort作为有效的控制器方法参数,如下例所示:spring-doc.cadn.net.cn

使用 Pageable 作为控制器方法参数
@Controller
@RequestMapping("/users")
class UserController {

  private final UserRepository repository;

  UserController(UserRepository repository) {
    this.repository = repository;
  }

  @RequestMapping
  String showUsers(Model model, Pageable pageable) {

    model.addAttribute("users", repository.findAll(pageable));
    return "users";
  }
}

前面的方法签名会导致 Spring MVC 尝试派生一个Pageable实例:spring-doc.cadn.net.cn

表 1.评估的请求参数Pageable实例

pagespring-doc.cadn.net.cn

页面。0 索引,默认为 0。spring-doc.cadn.net.cn

sizespring-doc.cadn.net.cn

要检索的页面的大小。默认值为 20。spring-doc.cadn.net.cn

sortspring-doc.cadn.net.cn

应按格式排序的属性property,property(,ASC|DESC)(,IgnoreCase).默认排序方向为区分大小写的升序。使用多个sort参数(如果要切换方向或区分大小写),例如?sort=firstname&sort=lastname,asc&sort=city,ignorecase.spring-doc.cadn.net.cn

要自定义此行为,请注册一个实现PageableHandlerMethodArgumentResolverCustomizerinterface 或SortHandlerMethodArgumentResolverCustomizer接口。 其customize()方法,让您更改设置,如下例所示:spring-doc.cadn.net.cn

@Bean SortHandlerMethodArgumentResolverCustomizer sortCustomizer() {
    return s -> s.setPropertyDelimiter("<-->");
}

如果设置现有MethodArgumentResolver不足以达到您的目的,请扩展SpringDataWebConfiguration或启用了 HATEOAS 的等效项,覆盖pageableResolver()sortResolver()方法,并导入自定义配置文件,而不是使用@Enable注解。spring-doc.cadn.net.cn

如果您需要多个PageableSort要从请求中解析的实例(例如,对于多个表),您可以使用 Spring 的@Qualifier注解来区分彼此。 然后,请求参数必须以${qualifier}_. 以下示例显示了生成的方法签名:spring-doc.cadn.net.cn

String showUsers(Model model,
      @Qualifier("thing1") Pageable first,
      @Qualifier("thing2") Pageable second) { … }

您必须填充thing1_page,thing2_page等。spring-doc.cadn.net.cn

默认的Pageable传递给方法的PageRequest.of(0, 20),但您可以使用@PageableDefault注解Pageable参数。spring-doc.cadn.net.cn

创建 JSON 表示形式Page

Spring MVC 控制器通常会尝试最终将 Spring Data 页面的表示呈现给 Client 端。 虽然一个人可以简单地返回Page实例,让 Jackson 按原样呈现它们,我们强烈建议不要将此作为底层实现类PageImpl是域类型。 这意味着我们可能出于不相关的原因想要或必须更改其 API,并且此类更改可能会以破坏性方式更改生成的 JSON 表示。spring-doc.cadn.net.cn

在 Spring Data 3.1 中,我们开始通过发布描述问题的警告日志来暗示问题。 我们最终仍然建议利用与 Spring HATEOAS 的集成来呈现完全稳定且支持超媒体的页面,使客户端能够轻松导航它们。 但是从版本 3.3 开始, Spring Data 提供了一种页面渲染机制,该机制易于使用,但不需要包含 Spring HATEOAS。spring-doc.cadn.net.cn

使用 Spring Data'PagedModel

从本质上讲,该支持包括 Spring HATEOAS 的简化版本PagedModel(Spring Data 位于org.springframework.data.web包)。 它可以用来包装Page实例,并产生一个简化的表示,它反映了 Spring HATEOAS 建立的结构,但省略了导航链接。spring-doc.cadn.net.cn

import org.springframework.data.web.PagedModel;

@Controller
class MyController {

  private final MyRepository repository;

  // Constructor ommitted

  @GetMapping("/page")
  PagedModel<?> page(Pageable pageable) {
    return new PagedModel<>(repository.findAll(pageable)); (1)
  }
}
1 Wraps the Page instance into a PagedModel.

This will result in a JSON structure looking like this:spring-doc.cadn.net.cn

{
  "content" : [
     … // Page content rendered here
  ],
  "page" : {
    "size" : 20,
    "totalElements" : 30,
    "totalPages" : 2,
    "number" : 0
  }
}

Note how the document contains a page field exposing the essential pagination metadata.spring-doc.cadn.net.cn

Globally enabling simplified Page rendering

If you don’t want to change all your existing controllers to add the mapping step to return PagedModel instead of Page you can enable the automatic translation of PageImpl instances into PagedModel by tweaking @EnableSpringDataWebSupport as follows:spring-doc.cadn.net.cn

@EnableSpringDataWebSupport(pageSerializationMode = VIA_DTO)
class MyConfiguration { }

This will allow your controller to still return Page instances and they will automatically be rendered into the simplified representation:spring-doc.cadn.net.cn

@Controller
class MyController {

  private final MyRepository repository;

  // Constructor ommitted

  @GetMapping("/page")
  Page<?> page(Pageable pageable) {
    return repository.findAll(pageable);
  }
}

Hypermedia Support for Page and Slice

Spring HATEOAS ships with a representation model class (PagedModel/SlicedModel) that allows enriching the content of a Page or Slice instance with the necessary Page/Slice metadata as well as links to let the clients easily navigate the pages. The conversion of a Page to a PagedModel is done by an implementation of the Spring HATEOAS RepresentationModelAssembler interface, called the PagedResourcesAssembler. Similarly Slice instances can be converted to a SlicedModel using a SlicedResourcesAssembler. The following example shows how to use a PagedResourcesAssembler as a controller method argument, as the SlicedResourcesAssembler works exactly the same:spring-doc.cadn.net.cn

Using a PagedResourcesAssembler as controller method argument
@Controller
class PersonController {

  private final PersonRepository repository;

  // Constructor omitted

  @GetMapping("/people")
  HttpEntity<PagedModel<Person>> people(Pageable pageable,
    PagedResourcesAssembler assembler) {

    Page<Person> people = repository.findAll(pageable);
    return ResponseEntity.ok(assembler.toModel(people));
  }
}

Enabling the configuration, as shown in the preceding example, lets the PagedResourcesAssembler be used as a controller method argument. Calling toModel(…) on it has the following effects:spring-doc.cadn.net.cn

  • The content of the Page becomes the content of the PagedModel instance.spring-doc.cadn.net.cn

  • The PagedModel object gets a PageMetadata instance attached, and it is populated with information from the Page and the underlying Pageable.spring-doc.cadn.net.cn

  • The PagedModel may get prev and next links attached, depending on the page’s state. The links point to the URI to which the method maps. The pagination parameters added to the method match the setup of the PageableHandlerMethodArgumentResolver to make sure the links can be resolved later.spring-doc.cadn.net.cn

Assume we have 30 Person instances in the database. You can now trigger a request (GET localhost:8080/people) and see output similar to the following:spring-doc.cadn.net.cn

{ "links" : [
    { "rel" : "next", "href" : "http://localhost:8080/persons?page=1&size=20" }
  ],
  "content" : [
     … // 20 Person instances rendered here
  ],
  "page" : {
    "size" : 20,
    "totalElements" : 30,
    "totalPages" : 2,
    "number" : 0
  }
}
The JSON envelope format shown here doesn’t follow any formally specified structure and it’s not guaranteed stable and we might change it at any time. It’s highly recommended to enable the rendering as a hypermedia-enabled, official media type, supported by Spring HATEOAS, like HAL. Those can be activated by using its @EnableHypermediaSupport annotation. Find more information in the Spring HATEOAS reference documentation.

The assembler produced the correct URI and also picked up the default configuration to resolve the parameters into a Pageable for an upcoming request. This means that, if you change that configuration, the links automatically adhere to the change. By default, the assembler points to the controller method it was invoked in, but you can customize that by passing a custom Link to be used as base to build the pagination links, which overloads the PagedResourcesAssembler.toModel(…) method.spring-doc.cadn.net.cn

Spring Data Jackson Modules

The core module, and some of the store specific ones, ship with a set of Jackson Modules for types, like org.springframework.data.geo.Distance and org.springframework.data.geo.Point, used by the Spring Data domain.
Those Modules are imported once web support is enabled and
com.fasterxml.jackson.databind.ObjectMapper is available.spring-doc.cadn.net.cn

During initialization SpringDataJacksonModules, like the SpringDataJacksonConfiguration, get picked up by the infrastructure, so that the declared com.fasterxml.jackson.databind.Modules are made available to the Jackson ObjectMapper.spring-doc.cadn.net.cn

Data binding mixins for the following domain types are registered by the common infrastructure.spring-doc.cadn.net.cn

org.springframework.data.geo.Distance
org.springframework.data.geo.Point
org.springframework.data.geo.Box
org.springframework.data.geo.Circle
org.springframework.data.geo.Polygon

The individual module may provide additional SpringDataJacksonModules.
Please refer to the store specific section for more details.
spring-doc.cadn.net.cn

Web Databinding Support

You can use Spring Data projections (described in Projections) to bind incoming request payloads by using either JSONPath expressions (requires Jayway JsonPath) or XPath expressions (requires XmlBeam), as the following example shows:spring-doc.cadn.net.cn

HTTP payload binding using JSONPath or XPath expressions
@ProjectedPayload
public interface UserPayload {

  @XBRead("//firstname")
  @JsonPath("$..firstname")
  String getFirstname();

  @XBRead("/lastname")
  @JsonPath({ "$.lastname", "$.user.lastname" })
  String getLastname();
}

You can use the type shown in the preceding example as a Spring MVC handler method argument or by using ParameterizedTypeReference on one of methods of the RestTemplate. The preceding method declarations would try to find firstname anywhere in the given document. The lastname XML lookup is performed on the top-level of the incoming document. The JSON variant of that tries a top-level lastname first but also tries lastname nested in a user sub-document if the former does not return a value. That way, changes in the structure of the source document can be mitigated easily without having clients calling the exposed methods (usually a drawback of class-based payload binding).spring-doc.cadn.net.cn

Nested projections are supported as described in Projections. If the method returns a complex, non-interface type, a Jackson ObjectMapper is used to map the final value.spring-doc.cadn.net.cn

For Spring MVC, the necessary converters are registered automatically as soon as @EnableSpringDataWebSupport is active and the required dependencies are available on the classpath. For usage with RestTemplate, register a ProjectingJackson2HttpMessageConverter (JSON) or XmlBeamHttpMessageConverter manually.spring-doc.cadn.net.cn

For more information, see the web projection example in the canonical Spring Data Examples repository.spring-doc.cadn.net.cn

Querydsl Web Support

For those stores that have QueryDSL integration, you can derive queries from the attributes contained in a Request query string.spring-doc.cadn.net.cn

Consider the following query string:spring-doc.cadn.net.cn

?firstname=Dave&lastname=Matthews

Given the User object from the previous examples, you can resolve a query string to the following value by using the QuerydslPredicateArgumentResolver, as follows:spring-doc.cadn.net.cn

QUser.user.firstname.eq("Dave").and(QUser.user.lastname.eq("Matthews"))
The feature is automatically enabled, along with @EnableSpringDataWebSupport, when Querydsl is found on the classpath.

Adding a @QuerydslPredicate to the method signature provides a ready-to-use Predicate, which you can run by using the QuerydslPredicateExecutor.spring-doc.cadn.net.cn

Type information is typically resolved from the method’s return type. Since that information does not necessarily match the domain type, it might be a good idea to use the root attribute of QuerydslPredicate.

The following example shows how to use @QuerydslPredicate in a method signature:spring-doc.cadn.net.cn

@Controller
class UserController {

  @Autowired UserRepository repository;

  @RequestMapping(value = "/", method = RequestMethod.GET)
  String index(Model model, @QuerydslPredicate(root = User.class) Predicate predicate,    (1)
          Pageable pageable, @RequestParam MultiValueMap<String, String> parameters) {

    model.addAttribute("users", repository.findAll(predicate, pageable));

    return "index";
  }
}
1 Resolve query string arguments to matching Predicate for User.

The default binding is as follows:spring-doc.cadn.net.cn

You can customize those bindings through the bindings attribute of @QuerydslPredicate or by making use of Java 8 default methods and adding the QuerydslBinderCustomizer method to the repository interface, as follows:spring-doc.cadn.net.cn

interface UserRepository extends CrudRepository<User, String>,
                                 QuerydslPredicateExecutor<User>,                (1)
                                 QuerydslBinderCustomizer<QUser> {               (2)

  @Override
  default void customize(QuerydslBindings bindings, QUser user) {

    bindings.bind(user.username).first((path, value) -> path.contains(value))    (3)
    bindings.bind(String.class)
      .first((StringPath path, String value) -> path.containsIgnoreCase(value)); (4)
    bindings.excluding(user.password);                                           (5)
  }
}
1 QuerydslPredicateExecutor provides access to specific finder methods for Predicate.
2 QuerydslBinderCustomizer defined on the repository interface is automatically picked up and shortcuts @QuerydslPredicate(bindings=…​).
3 Define the binding for the username property to be a simple contains binding.
4 Define the default binding for String properties to be a case-insensitive contains match.
5 Exclude the password property from Predicate resolution.
You can register a QuerydslBinderCustomizerDefaults bean holding default Querydsl bindings before applying specific bindings from the repository or @QuerydslPredicate.

Repository Populators

If you work with the Spring JDBC module, you are probably familiar with the support for populating a DataSource with SQL scripts. A similar abstraction is available on the repositories level, although it does not use SQL as the data definition language because it must be store-independent. Thus, the populators support XML (through Spring’s OXM abstraction) and JSON (through Jackson) to define data with which to populate the repositories.spring-doc.cadn.net.cn

Assume you have a file called data.json with the following content:spring-doc.cadn.net.cn

Data defined in JSON
[ { "_class" : "com.acme.Person",
 "firstname" : "Dave",
  "lastname" : "Matthews" },
  { "_class" : "com.acme.Person",
 "firstname" : "Carter",
  "lastname" : "Beauford" } ]

You can 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, declare a populator similar to the following:spring-doc.cadn.net.cn

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
    https://www.springframework.org/schema/beans/spring-beans.xsd
    http://www.springframework.org/schema/data/repository
    https://www.springframework.org/schema/data/repository/spring-repository.xsd">

  <repository:jackson2-populator locations="classpath:data.json" />

</beans>

The preceding declaration causes the data.json file to be read and deserialized by a Jackson ObjectMapper.spring-doc.cadn.net.cn

The type to which the JSON object is unmarshalled is determined by inspecting the _class attribute of the JSON document. The infrastructure eventually selects the appropriate repository to handle the object that was deserialized.spring-doc.cadn.net.cn

To instead use XML to define the data the repositories should be populated with, you can use the unmarshaller-populator element. You configure it to use one of the XML marshaller options available in Spring OXM. See the Spring reference documentation for details. The following example shows how to unmarshall a repository populator with JAXB:spring-doc.cadn.net.cn

Declaring an unmarshalling repository populator (using JAXB)
<?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"
  xmlns:oxm="http://www.springframework.org/schema/oxm"
  xsi:schemaLocation="http://www.springframework.org/schema/beans
    https://www.springframework.org/schema/beans/spring-beans.xsd
    http://www.springframework.org/schema/data/repository
    https://www.springframework.org/schema/data/repository/spring-repository.xsd
    http://www.springframework.org/schema/oxm
    https://www.springframework.org/schema/oxm/spring-oxm.xsd">

  <repository:unmarshaller-populator locations="classpath:data.json"
    unmarshaller-ref="unmarshaller" />

  <oxm:jaxb2-marshaller contextPath="com.acme" />

</beans>