|
对于最新稳定版本,请使用Spring Data Neo4j 8.0.0! |
Spring 数据扩展
本节记录了一组 Spring Data 扩展,使 Spring Data 能够在多种情境下使用。 目前,大部分集成面向 Spring MVC。
Querydsl 扩展
Querydsl 是一个框架,通过其流畅 API 构建静态类型的类 SQL 查询。
Querydsl 维护放缓到社区已将项目分叉为 OpenFeign at github.com/OpenFeign/querydsl(groupId)io.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 | 找到并返回一个与谓语. |
| 2 | 查找并返回所有与谓语. |
| 3 | 返回匹配 的实体数量谓语. |
| 4 | 返回是否匹配的实体谓语存在。 |
要使用 Querydsl 支持,请扩展QuerydslPredicateExecutor在你的仓库界面上,如下示例所示:
interface UserRepository extends CrudRepository<User, Long>, QuerydslPredicateExecutor<User> {
}
前面的示例允许你通过使用 Querydsl 来编写类型安全查询谓语实例,如下例子所示:
Predicate predicate = user.firstname.equalsIgnoreCase("dave")
.and(user.lastname.startsWithIgnoreCase("mathews"));
userRepository.findAll(predicate);
网页支持
支持 Repository 编程模型的 Spring Data 模块附带多种网页支持。
网页相关的组件要求 Spring MVC JAR 必须在类路径上。
其中一些甚至支持与Spring HATEOAS的集成。
通常,集成支持通过使用@EnableSpringDataWebSupport在你的 JavaConfig 配置类中进行注释,如下示例所示:
-
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,并对其进行积分组件(如存在)注册。
基础网页支持
前一节所示的配置记录了几个基本组成部分:
-
一个使用
域类转换器类让 Spring MVC 通过请求参数或路径变量解析仓库管理域类的实例。 -
HandlerMethodArgumentResolver实现让 Spring MVC 解析可页面和排序请求参数中的实例。 -
用于解序列化类型的Jackson模块,如
点和距离,或根据所用的Spring数据模块存储特定数据。
使用域类转换器类
这域类转换器class 允许你直接在 Spring MVC 控制器方法签名中使用域类型,这样你就不必通过仓库手动查找实例,如下示例所示:
@Controller
@RequestMapping("/users")
class UserController {
@RequestMapping("/{id}")
String showUserForm(@PathVariable("id") User user, Model model) {
model.addAttribute("user", user);
return "userForm";
}
}
该方法接收到用户直接实例,无需进一步查找。
该实例可以通过让 Spring MVC 将路径变量转换为身份证先类型化域类,最终通过调用访问实例findById(...)在为该域名类型注册的仓库实例上。
目前,仓库必须实现原油仓库有资格被认定进行转换。 |
用于分页和排序的HandlerMethodArgumentResolvers
上一节所示的配置片段还记录了PageableHandlerMethodArgumentResolver以及SortHandlerMethodArgumentResolver.
注册后可实现可页面和排序作为有效的控制器方法参数,如下示例所示:
@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 尝试推导可页面通过以下默认配置,从请求参数中实现实例:
|
你想取回的那个页面。0-索引,默认为0。 |
|
你想检索的页面大小。默认是20。 |
|
格式中应按的属性排序 |
要自定义这种行为,注册一个实现PageableHandlerMethodArgumentResolverCustomizer接口或SortHandlerMethodArgumentResolverCustomizer分别是界面。
其自定义()调用方法,允许你更改设置,如下示例所示:
@Bean SortHandlerMethodArgumentResolverCustomizer sortCustomizer() {
return s -> s.setPropertyDelimiter("<-->");
}
如果设置一个已存在的属性方法参数解析器这对你的目的来说还不够,可以延长SpringDataWebConfiguration或使用HATEOAS支持的等效程序,覆盖可分页解析器()或sortResolver()方法,并导入你自定义的配置文件,而不是使用@Enable注解。
如果你需要多个可页面或排序对于请求中需要解析的实例(例如多个表),你可以使用 Spring 的@Qualifier注释以区分彼此。
请求参数必须以${qualifier}_.
下例展示了所得的方法签名:
String showUsers(Model model,
@Qualifier("thing1") Pageable first,
@Qualifier("thing2") Pageable second) { … }
你必须繁衍thing1_page,thing2_page,依此类推。
默认可页面传递到该方法中等价于PageRequest.of(0, 20)但你可以通过使用@PageableDefault关于可页面参数。
创建 的 JSON 表示页
Spring MVC 控制器通常会尝试最终向客户端渲染 Spring Data 页面的表示。
虽然可以直接返回页对于处理方法的实例,允许Jackson直接渲染它们,我们强烈建议不要将此作为底层实现类PageImpl(页面管理)是一个域类型。
这意味着我们可能出于无关的原因想要或必须更改它的API,而这些更改可能会以破坏性的方式改变最终的JSON表示。
在 Spring Data 3.1 版本中,我们开始通过发布一个描述问题的警告日志来暗示这个问题。 我们最终仍建议利用与 Spring HATEOAS 的集成,实现一种完全稳定且支持超媒体的页面渲染方式,方便客户浏览页面。 但从 3.3 版本起,Spring Data 提供了便捷且不要求包含 Spring HATEOAS 的页面渲染机制。
使用 Spring DataPagedModel
其核心支持是简化版的 Spring HATEOAS'PagedModel(Spring Data的那个位于org.springframework.data.webpackage)。
它可以用来包裹页实例和结果是一个简化的表示,反映了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 | 包裹页实例转变为PagedModel. |
这将得到一个 JSON 结构如下:
{
"content" : [
… // Page content rendered here
],
"page" : {
"size" : 20,
"totalElements" : 30,
"totalPages" : 2,
"number" : 0
}
}
注意文档中包含页字段暴露了必要的分页元数据。
全局实现简化页渲染
如果你不想更改所有现有控制器来添加映射步骤以返回PagedModel而不是页你可以启用 的自动翻译PageImpl(页面管理)实例 变换PagedModel通过调整@EnableSpringDataWebSupport如下:
@EnableSpringDataWebSupport(pageSerializationMode = VIA_DTO)
class MyConfiguration { }
这样你的手柄仍然可以返回页实例,它们将自动被渲染为简化表示:
@Controller
class MyController {
private final MyRepository repository;
// Constructor ommitted
@GetMapping("/page")
Page<?> page(Pageable pageable) {
return repository.findAll(pageable);
}
}
超媒体支持页和片
Spring HATEOAS 自带表示模型类别(PagedModel/切片模型)允许丰富 的内容页或片实例中页/片元数据以及链接,方便客户端轻松浏览页面。
对 a 的转换页转给PagedModel是通过Spring HATEOAS的实现实现的RepresentationModelAssembler称为PagedResources汇编器.
同样地片实例可以转换为切片模型使用SlicedResourcesAssembler.
以下示例展示了如何使用PagedResources汇编器作为控制器方法参数,作为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));
}
}
启用该配置,如前例所示,使得PagedResources汇编器可用作控制器方法的参数。
叫toModel(...)对它有以下效果:
-
内容
页成为PagedModel实例。 -
这
PagedModel对象获得页面元数据附加实例,并且填充了来自页以及可页面. -
这
PagedModel可能会得到昨日和下一个附上链接,视页面状态而定。 这些链接指向该方法所映射的URI。 添加到方法中的分页参数与PageableHandlerMethodArgumentResolver以确保这些链接以后能够解决。
假设我们有30个人数据库中的实例。
你现在可以触发请求了 (获取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注解。
更多信息请参见春季HATEOAS参考文献。 |
汇编器生成正确的URI,并采用默认配置将参数解析为可页面为了即将到来的请求。
这意味着,如果你更改了该配置,链接会自动响应。
默认情况下,汇编器指向调用时的控制器方法,但你可以通过传递自定义方法来自定义链接作为构建分页链接的基础,这会使PagedResourcesAssembler.toModel(...)方法。
Spring Data Jackson 模块
核心模块以及一些商店专用模块,会附带一套针对类型的Jackson模块,比如org.springframework.data.geo.Distance和org.springframework.data.geo.Point,被Spring Data域使用。
这些模块在启用网页支持后导入,com.fasterxml.jackson.databind.ObjectMapper已开放。
初始化过程中SpringDataJacksonModules,就像SpringDataJacksonConfiguration被基础设施接收,因此声明com.fasterxml.jackson.databind.Module这些都被提供给Jackson对象映射器.
以下领域类型的数据绑定混合由通用基础设施注册。
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
|
各个模块可能提供额外服务 |
网页数据绑定支持
您可以使用 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 处理程序的参数,或者使用参数化类型引用在Rest模板.
前述方法声明会尝试 寻找名称在给定文档中的任何位置。
这姓氏XML 查找是在进入文档的顶层执行的。
JSON 变体尝试的是顶层姓氏先是,但也尝试了姓氏嵌套在用户如果前者没有返回某个值,则称为子文档。
这样,源文档结构的变化可以很容易地缓解,而无需客户端调用暴露的方法(通常是基于类的有效载荷绑定的缺点)。
嵌套投影支持,详见投影。
如果方法返回的是复杂的非接口类型,则为对象映射器用于映射最终值。
对于Spring MVC,所需的转换器会在@EnableSpringDataWebSupport是活跃的,且所需的依赖关系可在类路径中获得。
用于Rest模板,寄存器A投影Jackson2HttpMessage转换器(JSON) 或XmlBeamHttpMessage转换器手动下载。
更多信息请参见官方 Spring Data Examples 仓库中的网页投影示例。
Querydsl Web 支持
对于那些支持 Querydsl 集成的商店,你可以从包含的属性中推导出查询请求查询字符串。
考虑以下查询字符串:
?firstname=Dave&lastname=Matthews
给定用户在前面例子中,你可以通过以下方式将查询字符串解析为以下值QuerydslPredicateArgumentResolver如下:
QUser.user.firstname.eq("Dave").and(QUser.user.lastname.eq("Matthews"))
该功能会自动启用,同时@EnableSpringDataWebSupport当 Querydsl 出现在类路径上时。 |
添加一个@QuerydslPredicate方法签名提供了即用状态谓语,你可以用QuerydslPredicateExecutor.
类型信息通常通过方法的返回类型解析。
由于这些信息不一定与域名类型匹配,使用以下信息可能是个好主意根属性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 | 解析查询字符串参数以匹配谓语为用户. |
默认绑定如下:
-
对象在简单性质上情 商. -
对象在类性质集合上,作为包含. -
收集在简单性质上在.
你可以通过以下方式自定义这些绑定绑定属性@QuerydslPredicate或者通过使用 Java 8 来实现默认方法并添加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提供针对谓语. |
| 2 | QuerydslBinderCustomizer存储库界面定义的@QuerydslPredicate(bindings=...). |
| 3 | 定义 的绑定用户名性质为简单包含捆绑。 |
| 4 | 定义 的默认绑定字符串性质为大小写不敏感包含火柴。 |
| 5 | 排除密码来自的性质谓语分辨率。 |
你可以注册QuerydslBinderCustomizerDefaultsBEAN 在应用仓库的特定绑定前,先保留默认的 Querydsl 绑定,或者@QuerydslPredicate. |
存储器填充器
如果你使用Spring JDBC模块,你可能熟悉填充数据来源使用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 读取并反串行对象映射器.
JSON对象被解编组的类型通过检查_类JSON文档的属性。
基础设施最终会选择合适的仓库来处理被反序列化的对象。
如果想用XML来定义存储库应填充的数据,你可以使用无分组器-填充器元素。
你可以配置它使用Spring OXM中可用的XML编组器选项之一。
详情请参见春季参考文献。
以下示例展示了如何使用 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>