此版本仍在开发中,尚不被认为是稳定的。对于最新的稳定版本,请使用 Spring Data Relational 3.5.2! |
Spring 数据扩展
本节记录了一组 Spring Data 扩展,这些扩展可以在各种上下文中使用 Spring Data。 目前,大部分集成都是针对 Spring MVC。
Querydsl 扩展
Querydsl 是一个框架,它可以通过其流畅的 API 构建静态类型的类 SQL 查询。
Querydsl 维护已经放慢到社区在 github.com/OpenFeign/querydsl (groupIdio.github.openfeign.querydsl ).
Spring Data 尽最大努力支持分叉。 |
一些 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);
网络支持
支持存储库编程模型的 Spring Data 模块附带了各种 Web 支持。
与 Web 相关的组件要求 Spring MVC JAR 位于类路径上。
其中一些甚至提供与 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
注释注册一些组件。
我们将在本节后面讨论这些。
它还会检测类路径上的 Spring HATEOAS,并为其注册集成组件(如果存在)。
基本网络支持
上一节中显示的配置注册了一些基本组件:
-
一个使用
DomainClassConverter
类让 Spring MVC 从请求参数或路径变量解析存储库管理的域类的实例。 -
HandlerMethodArgumentResolver
让 Spring MVC 解析的实现Pageable
和Sort
请求参数中的实例。 -
Jackson 模块,用于反序列化类型,例如
Point
和Distance
,或存储特定的,具体取决于所使用的 Spring Data Module。
使用DomainClassConverter
类
这DomainClassConverter
class 允许您直接在 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 将路径变量转换为id
类型,最终通过调用findById(…)
在为域类型注册的存储库实例上。
目前,存储库必须实现CrudRepository 有资格被发现进行转换。 |
HandlerMethodArgumentResolvers 用于 Pageable 和 Sort
上一节中显示的配置代码段还注册了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-indexed,默认为 0。 |
|
要检索的页面的大小。默认为 20。 |
|
应按格式排序的属性 |
要自定义此行为,请注册一个实现PageableHandlerMethodArgumentResolverCustomizer
接口或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
参数。
为Page
Spring MVC 控制器通常会尝试最终向客户端呈现 Spring Data 页面的表示。
虽然人们可以简单地返回Page
实例,让 Jackson 按原样呈现它们,我们强烈建议不要将其作为底层实现类PageImpl
是一种域类型。
这意味着我们可能出于不相关的原因想要或必须更改其 API,并且此类更改可能会以中断性的方式更改生成的 JSON 表示形式。
在 Spring Data 3.1 中,我们开始通过发布描述问题的警告日志来暗示问题。 我们最终仍然建议利用与 Spring HATEOAS 的集成来呈现完全稳定且支持超媒体的页面,从而轻松允许客户端导航它们。 但是从版本 3.3 开始,Spring Data 提供了一种页面渲染机制,该机制使用方便,但不需要包含 Spring HATEOAS。
使用 Spring Data'PagedModel
从本质上讲,该支持包括 Spring HATEOAS 的简化版本PagedModel
(位于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
对象会得到一个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 HATEOS 支持,例如 HAL。
这些都可以通过使用它来激活@EnableHypermediaSupport 注解。 在 Spring HATEOAS 参考文档中查找更多信息。 |
汇编程序生成了正确的 URI,并选取了默认配置以将参数解析为Pageable
用于即将到来的请求。这意味着,如果您更改该配置,链接会自动遵守更改。默认情况下,汇编器指向调用它的控制器方法,但您可以通过传递自定义Link
用作构建分页链接的基础,这会重载PagedResourcesAssembler.toModel(…)
方法。
Spring Data Jackson 模块
核心模块和一些特定于商店的模块附带了一组用于类型的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
s 可供Jackson使用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 处理程序方法参数或使用ParameterizedTypeReference
在RestTemplate
.
前面的方法声明将尝试查找firstname
在给定文档中的任何位置。
这lastname
XML 查找在传入文档的顶层执行。
该 JSON 变体尝试顶级lastname
首先,但也尝试了lastname
嵌套在user
子文档,如果前者不返回值。
这样,可以轻松缓解源文档结构中的更改,而无需客户端调用公开的方法(通常是基于类的有效负载绑定的缺点)。
支持嵌套投影,如投影中所述。
如果该方法返回复杂的非接口类型,则 JacksonObjectMapper
用于映射最终值。
对于 Spring MVC,必要的转换器会在@EnableSpringDataWebSupport
处于活动状态,并且所需的依赖项在类路径上可用。
适用于RestTemplate
,注册一个ProjectingJackson2HttpMessageConverter
(JSON) 或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"))
该功能会自动启用,同时@EnableSpringDataWebSupport ,当在类路径上找到 Querydsl 时。 |
添加一个@QuerydslPredicate
to 方法签名提供了一个即用型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
在简单属性上,如eq
. -
Object
在集合上,类似属性为contains
. -
Collection
在简单属性上,如in
.
您可以通过bindings
属性@QuerydslPredicate
或通过使用 Java 8default 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 在存储库界面上定义的自动选择和快捷方式@QuerydslPredicate(bindings=…) . |
3 | 定义username 属性是一个简单的contains 捆绑。 |
4 | 定义默认绑定String 属性不区分大小写contains 火柴。 |
5 | 排除password 属性来自Predicate 分辨率。 |
您可以注册一个QuerydslBinderCustomizerDefaults bean 在应用存储库中的特定绑定之前保留默认的 Querydsl 绑定,或者@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 中提供的存储库命名空间的填充器元素来填充存储库。
要将上述数据填充到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>
前面的声明会导致data.json
要由 Jackson 读取和反序列化的文件ObjectMapper
.
JSON 对象被解组到的类型是通过检查_class
属性。
基础结构最终会选择适当的存储库来处理已反序列化的对象。
要改用 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>