对于最新的稳定版本,请使用 Spring Data Relational 3.4.0! |
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 | 返回是否与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 的集成。
通常,集成支持是通过使用@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" />
这@EnableSpringDataWebSupport
annotation 注册了一些组件。
我们将在本节后面讨论这些内容。
它还在 Classpath 上检测 Spring HATEOAS,并为其注册集成组件(如果存在)。
基本 Web 支持
上一节中所示的配置注册了一些基本组件:
-
一个使用
DomainClassConverter
类让 Spring MVC 从请求参数或路径变量中解析存储库管理的域类的实例。 -
HandlerMethodArgumentResolver
实现来让 Spring MVC 解析Pageable
和Sort
实例。 -
Jackson Modules 来解序/序列化类型,例如
Point
和Distance
,或者存储特定的,具体取决于所使用的 Spring Data Module。
使用DomainClassConverter
类
这DomainClassConverter
类允许你直接在 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 变量转换为id
type 的 domain 类,最终通过调用findById(…)
在为域类型注册的存储库实例上。
目前,存储库必须实现CrudRepository 才有资格被发现进行转化。 |
用于 Pageable 和 Sort 的 HandlerMethodArgumentResolvers
上一节中所示的配置代码段还注册了一个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。 |
|
应按格式排序的属性 |
要自定义此行为,请注册一个实现PageableHandlerMethodArgumentResolverCustomizer
interface 或SortHandlerMethodArgumentResolverCustomizer
接口。
其customize()
方法,让您更改设置,如下例所示:
@Bean SortHandlerMethodArgumentResolverCustomizer sortCustomizer() {
return s -> s.setPropertyDelimiter("<-->");
}
如果设置现有MethodArgumentResolver
不足以达到您的目的,请扩展SpringDataWebConfiguration
或启用了 HATEOAS 的等效项,覆盖pageableResolver()
或sortResolver()
方法,并导入自定义配置文件,而不是使用@Enable
注解。
如果您需要多个Pageable
或Sort
要从请求中解析的实例(例如,对于多个表),您可以使用 Spring 的@Qualifier
注解来区分彼此。
然后,请求参数必须以${qualifier}_
.
以下示例显示了生成的方法签名:
String showUsers(Model model,
@Qualifier("thing1") Pageable first,
@Qualifier("thing2") Pageable second) { … }
您必须填充thing1_page
,thing2_page
等。
默认的Pageable
传递给方法的PageRequest.of(0, 20)
,但您可以使用@PageableDefault
注解Pageable
参数。
创建 JSON 表示形式Page
Spring MVC 控制器通常会尝试最终将 Spring Data 页面的表示呈现给 Client 端。
虽然一个人可以简单地返回Page
实例,让 Jackson 按原样呈现它们,我们强烈建议不要将此作为底层实现类PageImpl
是域类型。
这意味着我们可能出于不相关的原因想要或必须更改其 API,并且此类更改可能会以破坏性方式更改生成的 JSON 表示。
在 Spring Data 3.1 中,我们开始通过发布描述问题的警告日志来暗示问题。 我们最终仍然建议利用与 Spring HATEOAS 的集成来呈现完全稳定且支持超媒体的页面,使客户端能够轻松导航它们。 但是从版本 3.3 开始, Spring Data 提供了一种页面渲染机制,该机制易于使用,但不需要包含 Spring HATEOAS。
使用 Spring Data'PagedModel
从本质上讲,该支持包括 Spring HATEOAS 的简化版本PagedModel
(Spring Data 位于org.springframework.data.web
包)。
它可以用来包装Page
实例,并产生一个简化的表示,它反映了 Spring HATEOAS 建立的结构,但省略了导航链接。
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);
}
}
超媒体支持Page
和Slice
Spring HATEOAS 附带了一个表示模型类 (PagedModel
/SlicedModel
),它允许丰富Page
或Slice
实例替换为必要的Page
/Slice
元数据以及链接,以便客户轻松浏览页面。
一个Page
更改为PagedModel
由 Spring HATEOAS 的实现完成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
object 获取PageMetadata
实例,并且它填充了来自Page
和底层Pageable
. -
这
PagedModel
可能会得到prev
和next
附加的链接,具体取决于页面的状态。 这些链接指向方法映射到的 URI。 添加到该方法的分页参数与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。
这些可以通过使用其@EnableHypermediaSupport 注解。
在 Spring HATEOAS 参考文档中查找更多信息。 |
汇编器生成了正确的 URI,并选取了默认配置以将参数解析为Pageable
对于即将到来的请求。
这意味着,如果您更改该配置,链接将自动遵循更改。
默认情况下,汇编器指向调用它的控制器方法,但你可以通过传递自定义Link
用作构建分页链接的基础,这会重载PagedResourcesAssembler.toModel(…)
方法。
Spring Data Jackson 模块
核心模块和一些特定于 store 的模块附带了一组用于类型的 Jackson 模块,例如org.springframework.data.geo.Distance
和org.springframework.data.geo.Point
,由 Spring Data 域使用。
启用 Web 支持后,将导入这些模块,并且com.fasterxml.jackson.databind.ObjectMapper
可用。
初始化期间SpringDataJacksonModules
,就像SpringDataJacksonConfiguration
,被基础设施拾取,以便声明的com.fasterxml.jackson.databind.Module
的 cookie 可用于 JacksonObjectMapper
.
以下域类型的数据绑定混合由通用基础设施注册。
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 处理程序方法参数,也可以使用ParameterizedTypeReference
在RestTemplate
.
前面的方法声明将尝试查找firstname
在给定文档中的任意位置。
这lastname
XML 查找在传入文档的顶层执行。
该 JSON 的变体会尝试顶级lastname
首先但也尝试lastname
嵌套在user
sub-document(如果前者不返回值)。
这样,可以很容易地缓解源文档结构的更改,而无需让 Client 端调用公开的方法(通常是基于类的有效负载绑定的缺点)。
支持嵌套投影,如 投影 中所述。
如果该方法返回一个复杂的非接口类型,则返回 JacksonObjectMapper
用于映射最终值。
对于 Spring MVC,一旦@EnableSpringDataWebSupport
处于活动状态,并且所需的依赖项在 Classpath 上可用。
用于RestTemplate
,注册一个ProjectingJackson2HttpMessageConverter
(JSON) 或XmlBeamHttpMessageConverter
手动地。
有关更多信息,请参阅规范 Spring Data Examples 存储库中的 Web 投影示例。
Querydsl Web 支持
对于那些具有 QueryDSL 集成的存储区,您可以从Request
query 字符串。
请考虑以下查询字符串:
?firstname=Dave&lastname=Matthews
鉴于User
对象,您可以使用QuerydslPredicateArgumentResolver
如下:
QUser.user.firstname.eq("Dave").and(QUser.user.lastname.eq("Matthews"))
该功能会自动启用,同时@EnableSpringDataWebSupport 时,在类路径上找到 Querydsl。 |
添加@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
.
您可以通过bindings
属性@QuerydslPredicate
或使用 Java 8default methods
并添加QuerydslBinderCustomizer
方法添加到 repository 接口中,如下所示:
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 定义的存储库界面会自动选取和快捷方式@QuerydslPredicate(bindings=…) . |
3 | 定义username property 设置为简单的contains 捆绑。 |
4 | 定义 的默认绑定String properties 设置为不区分大小写contains 火柴。 |
5 | 排除password property fromPredicate 分辨率。 |
您可以注册一个QuerydslBinderCustomizerDefaults 在应用存储库中的特定绑定之前保存默认 Querydsl 绑定的 bean,或者@QuerydslPredicate . |
存储库填充器
如果你使用 Spring JDBC 模块,你可能熟悉对填充DataSource
使用 SQL 脚本。
类似的抽象在存储库级别可用,尽管它不使用 SQL 作为数据定义语言,因为它必须与存储无关。
因此,填充器支持 XML(通过 Spring 的 OXM 抽象)和 JSON(通过 Jackson)来定义用于填充存储库的数据。
假设您有一个名为data.json
包含以下内容:
[ { "_class" : "com.acme.Person",
"firstname" : "Dave",
"lastname" : "Matthews" },
{ "_class" : "com.acme.Person",
"firstname" : "Carter",
"lastname" : "Beauford" } ]
您可以使用 Spring Data Commons 中提供的存储库命名空间的 populator 元素来填充存储库。
要将上述数据填充到PersonRepository
中,声明类似于以下内容的 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>
前面的声明会导致data.json
文件,以便由 Jackson 读取和反序列化ObjectMapper
.
JSON 对象解组到的类型是通过检查_class
JSON 文档的属性。
基础结构最终会选择适当的存储库来处理已反序列化的对象。
要改用 XML 来定义存储库应填充的数据,您可以使用unmarshaller-populator
元素。
您可以将其配置为使用 Spring OXM 中可用的 XML 编组器选项之一。有关详细信息,请参阅 Spring 参考文档。
以下示例显示了如何使用 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>