Spring 数据扩展
本节记录了一组 Spring Data 扩展,这些扩展支持在各种上下文中使用 Spring Data。 目前,大多数集成都针对 Spring MVC。
Querydsl 扩展
Querydsl 是一个框架,支持通过其 Fluent API 构建静态类型的类似 SQL 的查询。
几个 Spring Data 模块通过 ,提供与 Querydsl 的集成,如下例所示: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 | 返回与 exists.Predicate |
要使用 Querydsl 支持,请在存储库界面上扩展,如下例所示:QuerydslPredicateExecutor
interface UserRepository extends CrudRepository<User, Long>, QuerydslPredicateExecutor<User> {
}
前面的示例允许您使用 Querydsl 实例编写类型安全的查询,如下例所示:Predicate
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 的集成。
通常,通过使用 JavaConfig 配置类中的 Comments 来启用集成支持,如下例所示:@EnableSpringDataWebSupport
-
Java
-
XML
@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" />
注释注册了一些组件。
我们将在本节后面讨论这些内容。
它还在 Classpath 上检测 Spring HATEOAS,并为其注册集成组件(如果存在)。@EnableSpringDataWebSupport
基本 Web 支持
上一节中所示的配置注册了一些基本组件:
-
A 使用
DomainClassConverter
类让 Spring MVC 从请求参数或路径变量中解析存储库管理的域类的实例。 -
HandlerMethodArgumentResolver
实现,让 Spring MVC 解析请求参数中的实例。Pageable
Sort
-
Jackson Modules 来解序/序列化像 and 这样的类型,或者存储特定的类型,具体取决于所使用的 Spring Data Module 。
Point
Distance
使用类DomainClassConverter
该类允许你直接在 Spring MVC 控制器方法签名中使用域类型,这样你就不需要通过存储库手动查找实例,如下例所示:DomainClassConverter
@Controller
@RequestMapping("/users")
class UserController {
@RequestMapping("/{id}")
String showUserForm(@PathVariable("id") User user, Model model) {
model.addAttribute("user", user);
return "userForm";
}
}
该方法直接接收实例,无需进一步查找。
通过让 Spring MVC 首先将 path 变量转换为域类的类型,并最终通过调用为域类型注册的存储库实例来访问该实例,可以解析该实例。User
id
findById(…)
目前,必须实施存储库才有资格被发现以进行转换。CrudRepository |
用于 Pageable 和 Sort 的 HandlerMethodArgumentResolvers
上一节中所示的配置代码段还注册了 a 以及 .
注册启用 和 作为有效的控制器方法参数,如下例所示:PageableHandlerMethodArgumentResolver
SortHandlerMethodArgumentResolver
Pageable
Sort
@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
|
页面。0 索引,默认为 0。 |
|
要检索的页面的大小。默认值为 20。 |
|
应按格式排序的属性。默认排序方向为区分大小写的升序。如果要切换方向或区分大小写,请使用多个参数,例如 . |
要自定义此行为,请分别注册实现接口或接口的 Bean。
其方法被调用,允许您更改设置,如下例所示:PageableHandlerMethodArgumentResolverCustomizer
SortHandlerMethodArgumentResolverCustomizer
customize()
@Bean SortHandlerMethodArgumentResolverCustomizer sortCustomizer() {
return s -> s.setPropertyDelimiter("<-->");
}
如果设置 existing 的属性不足以达到您的目的,请扩展 or 已启用 HATEOAS 的等效项,覆盖 or 方法,然后导入自定义配置文件,而不是使用注释。MethodArgumentResolver
SpringDataWebConfiguration
pageableResolver()
sortResolver()
@Enable
如果需要从请求中解析多个实例(例如,对于多个表),则可以使用 Spring 的 Comments 来区分一个实例。
然后,请求参数必须以 .
以下示例显示了生成的方法签名:Pageable
Sort
@Qualifier
${qualifier}_
String showUsers(Model model,
@Qualifier("thing1") Pageable first,
@Qualifier("thing2") Pageable second) { … }
您必须填充 、 等。thing1_page
thing2_page
传递给方法的默认值等效于 a ,但您可以通过对参数使用注释来自定义它。Pageable
PageRequest.of(0, 20)
@PageableDefault
Pageable
创建 JSON 表示形式Page
Spring MVC 控制器通常会尝试最终将 Spring Data 页面的表示呈现给 Client 端。
虽然可以简单地从处理程序方法返回实例,让 Jackson 按原样呈现它们,但我们强烈建议不要这样做,因为底层实现类是一个域类型。
这意味着我们可能出于不相关的原因想要或必须更改其 API,并且此类更改可能会以破坏性方式更改生成的 JSON 表示。Page
PageImpl
在 Spring Data 3.1 中,我们开始通过发布描述问题的警告日志来暗示问题。 我们最终仍然建议利用与 Spring HATEOAS 的集成来呈现完全稳定且支持超媒体的页面,使客户端能够轻松导航它们。 但是从版本 3.3 开始, Spring Data 提供了一种页面渲染机制,该机制易于使用,但不需要包含 Spring HATEOAS。
使用 Spring Data'PagedModel
从本质上讲,该支持由 Spring HATEOAS(位于包中的 Spring Data 版本)的简化版本组成。
它可以用来包装实例并产生一个简化的表示,该表示反映了 Spring HATEOAS 建立的结构,但省略了导航链接。PagedModel
org.springframework.data.web
Page
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 | 将实例包装到 .Page PagedModel |
这将产生如下所示的 JSON 结构:
{
"content" : [
… // Page content rendered here
],
"page" : {
"size" : 20,
"totalElements" : 30,
"totalPages" : 2,
"number" : 0
}
}
请注意该文档如何包含公开基本分页元数据的字段。page
全局启用简化渲染Page
如果您不想更改所有现有控制器以添加要返回的映射步骤,而不是通过调整来启用实例的自动转换,如下所示:PagedModel
Page
PageImpl
PagedModel
@EnableSpringDataWebSupport
@EnableSpringDataWebSupport(pageSerializationMode = VIA_DTO)
class MyConfiguration { }
这将允许你的控制器仍然返回实例,并且它们将自动渲染为简化的表示形式:Page
@Controller
class MyController {
private final MyRepository repository;
// Constructor ommitted
@GetMapping("/page")
Page<?> page(Pageable pageable) {
return repository.findAll(pageable);
}
}
Hypermedia 支持 和Page
Slice
Spring HATEOAS 附带了一个表示模型类(/),它允许使用必要的 / 元数据以及链接来丰富 or 实例的内容,以使客户端轻松导航页面。
a 到 a 的转换是通过 Spring HATEOAS 接口的实现完成的,该接口称为 .
同样,可以使用 .
下面的示例展示了如何使用 a 作为控制器方法参数,因为其工作原理完全相同:PagedModel
SlicedModel
Page
Slice
Page
Slice
Page
PagedModel
RepresentationModelAssembler
PagedResourcesAssembler
Slice
SlicedModel
SlicedResourcesAssembler
PagedResourcesAssembler
SlicedResourcesAssembler
@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));
}
}
启用配置,如前面的示例所示,允许将其用作控制器方法参数。
调用它有以下效果:PagedResourcesAssembler
toModel(…)
-
的内容将成为实例的内容。
Page
PagedModel
-
该对象附加了一个实例,并填充了来自 和 底层 的信息。
PagedModel
PageMetadata
Page
Pageable
-
可能会 get 和附加链接,具体取决于页面的状态。 这些链接指向方法映射到的 URI。 添加到该方法的分页参数与 的设置匹配,以确保以后可以解析链接。
PagedModel
prev
next
PageableHandlerMethodArgumentResolver
假设数据库中有 30 个实例。
您现在可以触发请求 () 并查看类似于以下内容的输出:Person
GET localhost:8080/people
{ "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
}
}
此处显示的 JSON 信封格式不遵循任何正式指定的结构,并且不能保证稳定,我们可能随时更改它。
强烈建议将渲染启用为 Spring HATEOAS 支持的支持超媒体的官方媒体类型,例如 HAL。
这些可以通过使用其 annotation 来激活。
在 Spring HATEOAS 参考文档中查找更多信息。@EnableHypermediaSupport |
汇编器生成了正确的 URI,并选取了默认配置,以将参数解析为即将到来的请求。
这意味着,如果您更改该配置,链接将自动遵循更改。
默认情况下,汇编器指向调用它的控制器方法,但您可以通过传递要用作基础的自定义来构建分页链接来自定义该方法,这会重载该方法。Pageable
Link
PagedResourcesAssembler.toModel(…)
Spring Data Jackson 模块
核心模块和一些特定于 store 的模块附带了一组 Jackson 模块,用于 Spring Data 域使用的类型,如 和 。
这些模块在启用 Web 支持并可用后导入。org.springframework.data.geo.Distance
org.springframework.data.geo.Point
com.fasterxml.jackson.databind.ObjectMapper
在初始化期间,就像 一样,基础设施会拾取,以便声明的 s 可供 Jackson 使用。SpringDataJacksonModules
SpringDataJacksonConfiguration
com.fasterxml.jackson.databind.Module
ObjectMapper
以下域类型的数据绑定混合由通用基础设施注册。
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
单个模块可能会提供额外的 . |
Web 数据绑定支持
您可以使用 Spring Data 投影(在投影中描述)通过使用 JSONPath 表达式(需要 Jayway JsonPath)或 XPath 表达式(需要 XmlBeam)来绑定传入的请求有效负载,如下例所示:
@ProjectedPayload
public interface UserPayload {
@XBRead("//firstname")
@JsonPath("$..firstname")
String getFirstname();
@XBRead("/lastname")
@JsonPath({ "$.lastname", "$.user.lastname" })
String getLastname();
}
您可以将前面示例中所示的类型用作 Spring MVC 处理程序方法参数,或者对 .
前面的方法声明将尝试在给定文档中的任何位置查找。
XML 查找在传入文档的顶层执行。
它的 JSON 变体首先尝试顶级,但如果前者不返回值,则也会尝试嵌套在子文档中。
这样,可以很容易地缓解源文档结构的更改,而无需让 Client 端调用公开的方法(通常是基于类的有效负载绑定的缺点)。ParameterizedTypeReference
RestTemplate
firstname
lastname
lastname
lastname
user
支持嵌套投影,如 投影 中所述。
如果该方法返回复杂的非接口类型,则使用 Jackson 映射最终值。ObjectMapper
对于 Spring MVC,一旦激活,必要的转换器就会自动注册,并且所需的依赖项在 Classpath 上可用。
对于 用法,请注册 (JSON) 或手动。@EnableSpringDataWebSupport
RestTemplate
ProjectingJackson2HttpMessageConverter
XmlBeamHttpMessageConverter
有关更多信息,请参阅规范 Spring Data Examples 存储库中的 Web 投影示例。
Querydsl Web 支持
对于那些具有 QueryDSL 集成的存储,您可以从查询字符串中包含的属性派生查询。Request
请考虑以下查询字符串:
?firstname=Dave&lastname=Matthews
给定前面示例中的对象,您可以使用 ,将查询字符串解析为以下值,如下所示:User
QuerydslPredicateArgumentResolver
QUser.user.firstname.eq("Dave").and(QUser.user.lastname.eq("Matthews"))
当在 Classpath 上找到 Querydsl 时,该功能会自动启用。@EnableSpringDataWebSupport |
将 添加到方法签名中会提供一个现成的 ,您可以使用 .@QuerydslPredicate
Predicate
QuerydslPredicateExecutor
类型信息通常从方法的返回类型中解析。
由于该信息不一定与域类型匹配,因此最好使用 .root QuerydslPredicate |
下面的示例演示如何在方法签名中使用:@QuerydslPredicate
@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 | 将查询字符串参数解析为匹配项 .Predicate User |
默认绑定如下:
-
Object
在 simple properties 上为 。eq
-
Object
on collection 之类的属性如 .contains
-
Collection
在 simple properties 上为 。in
您可以通过 的属性 或通过使用 Java 8 并将方法添加到存储库接口来自定义这些绑定,如下所示:bindings
@QuerydslPredicate
default methods
QuerydslBinderCustomizer
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 提供对 的特定 Finder 方法的访问。Predicate |
2 | QuerydslBinderCustomizer 在存储库界面上定义的 Shortcuts 会自动选取 和 shortcuts 。@QuerydslPredicate(bindings=…) |
3 | 将属性的绑定定义为简单绑定。username contains |
4 | 将属性的默认绑定定义为不区分大小写的匹配项。String contains |
5 | 从解析中排除该属性。password Predicate |
您可以在应用存储库 或 中的特定绑定之前注册一个包含默认 Querydsl 绑定的 Bean。QuerydslBinderCustomizerDefaults @QuerydslPredicate |
存储库填充器
如果你使用 Spring JDBC 模块,你可能熟悉对使用 SQL 脚本填充 a 的支持。
类似的抽象在存储库级别可用,尽管它不使用 SQL 作为数据定义语言,因为它必须与存储无关。
因此,填充器支持 XML(通过 Spring 的 OXM 抽象)和 JSON(通过 Jackson)来定义用于填充存储库的数据。DataSource
假设您有一个调用的文件,其中包含以下内容:data.json
[ { "_class" : "com.acme.Person",
"firstname" : "Dave",
"lastname" : "Matthews" },
{ "_class" : "com.acme.Person",
"firstname" : "Carter",
"lastname" : "Beauford" } ]
您可以使用 Spring Data Commons 中提供的存储库命名空间的 populator 元素来填充存储库。
要将上述数据填充到 您的 ,请声明类似于以下内容的 populator:PersonRepository
<?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>
前面的声明会导致文件被 Jackson 读取和反序列化。data.json
ObjectMapper
JSON 对象解组到的类型是通过检查 JSON 文档的属性来确定的。
基础结构最终会选择适当的存储库来处理已反序列化的对象。_class
要改用 XML 来定义存储库应填充的数据,您可以使用 element.
您可以将其配置为使用 Spring OXM 中可用的 XML 编组器选项之一。有关详细信息,请参阅 Spring 参考文档。
以下示例显示了如何使用 JAXB 取消编组存储库填充器:unmarshaller-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"
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>