- 内容提要
- 序
- 前言
- 第一部分 背景知识
- 第 1 章 Spring Data 项目
- 第 2 章 Repository:便利的数据访问层
- 第 3 章 使用 Querydsl 实现类型安全的查询
- 第二部分 关系型数据库
- 第 4 章 JPA Repository
- 第 5 章 借助 Querydsl SQL 实现类型安全的 JDBC 编程
- 第三部分 NoSQL
- 第 6 章 MongoDB: 文档存储
- 第 7 章 Neo4j:图数据库
- 第 8 章 Redis:键/值存储
- 第四部分 快速应用开发
- 第 9 章 使用 Spring Roo 实现持久层
- 第 10 章 REST Repository 导出器
- 第五部分 大数据
- 第 11 章 Spring for Apache Hadoop
- 第 12 章 使用 Hadoop 分析数据
- 第 13 章 使用 Spring Batch 和 Spring Integration 创建大数据管道
- 第六部分 数据网格
- 第 14 章 分布式数据网格:GemFire
- 关于封面
7.6 组合发挥图和 Repository 的威力
一切准备就绪之后,就可以看看 Repository 如何与 Spring Data Neo4j 进行集成,以及怎样以“图”的方式使用它们。
Spring Data Commons Repository(参见第 2 章)能够很容易让持久化访问相关的代码(或者说没有代码)位于同一个地方并且允许我们尽可能少地编写代码来满足特定用例的需求。在 Spring Data Neo4j 中,Repository 来源于 GraphRepository<T>基础接口,它已经包含了一些常用的所需功能:CRUD 操作以及索引和遍历功能。搭建基础的 Repository 需要另一行命名空间配置,如示例 7-16 所示。每个领域类将会绑定一个独立和具体的 Repository 接口,如示例 7-17 所示。
示例 7-16 搭建基础的 Repository 配置
示例 7-17 基础的 Repository 接口声明
Spring Data Neo4j Repository 提供了对 @Query 注解和衍生查找方法的支持,这是通过 Cypher 表达式实现的。为了理解这种映射是如何工作的,需要了解 Cypher 的表示语法,这在下面的边栏“Cypher 查询语言”中进行了讲解。
Cypher 查询语言
Neo4j 自带了简洁和面向对象的 Java API,并且可以使用众多的 JVM(Java 虚拟机)语言以及大量针对 Neo4j 服务器的驱动。但是通常来讲,更好的数据操作声明表达方式是以“是什么(what)”来进行,而不是指明“怎样做(how)”。
这就是开发 Cypher 查询语言( http://neo4j.org/resources/cypher )的原因。它基于图中的模式匹配,模式绑定到特定的节点或关系并允许对结果进行进一步的过滤和分页。Cypher 有数据操作的特性,这样就可以修改图。Cypher 的查询部分支持链式(piped)连接,从而支持更为高级和强大的图操作。
每个 Cypher 查询可以包含以下几个部分。
START
定义标示符(identifier)、绑定节点和关系,这可以通过索引或 ID 进行查找。
MATCH
使用 ASCII-ART 描述在图中要查找的模式。模式要绑定到标示符上并且会定义新的标示符。在查询执行时所找到的子图会产生结果。
WHERE
使用布尔表达式对结果进行过滤,使用点号来访问属性、函数、集合断言和函数(collection predicates and functions)以及算术运算符等。
SKIP LIMIT
使用偏移量和每页的数量对结果进行分页。
RETURN
声明查询要返回什么。如果使用了聚集函数,那么所有非聚集的值将会用作分组值。
ORDER BY
根据属性或其他的表达式进行排序。
ORDER BY user.name ASC, count(*) DESC
UPDATES
Cypher 还有更多的东西。借助于 CREATE [UNIQUE]、SET 以及 DELETE,图可以在运行期进行修改。WITH 和 FOREACH 允许使用更为高级的查询结构。
PARAMETERS
Cypher 可以传递进来参数的 Map,它可以通过键(或位置)进行引用。
start n=node({nodeId}) where n.name=~{0} return n
Cypher 返回的结果在内部是列表式的结构,这一点上就像是 JDBC ResultSet。列名会作为行值的键。
存在针对 Cypher 的 Java DSL,这样就不用借助缺乏语义的字符串来进行查询了,而是可以使用类型安全的 API 来构建 Cypher 查询。可以基于所生成的领域对象,使用 Querydsl(参见第 3 章)构建用于过滤和索引查询的表达式。借助于已有的 JDBC 驱动( https://github.com/rickardoberg/neo4j-jdbc ),Cypher 查询能够很容易地集成到现存的 Java(Spring)应用和其他工具之中。
7.6.1 基本的图 Repository 操作
Repository 所提供的基本操作模仿了 Neo4jTemplate 的操作,只不过要绑定到声明 Repository 的领域类。所以,findOne(...)、save(...)、delete(...) 和 findAll(...) 等方法接收和返回领域类的实例。
Spring Data Neo4j 在图中存储了映射实体的类型(层级)信息。为了实现这一点,它会采用多个策略中的某一个,默认情况是基于索引的存储。这种类型的信息可以用于所有的 Repository 和模板方法,它们会操作某种类型的所有实例,并且会校验请求类型与实际存储的类型。
Repository 的更新方法默认就是事务性的,所以没有必要在它们上面声明事务。但是对于领域用例,比较明智的做法还是要这样做的,因为通常在一个业务事务中会封装不止一个数据库操作(这要使用 Neo4j 所提供的对 JtaTransactionManager 的支持)。
对于索引操作,IndexRepository 中有一些特定的方法,如 findAllByPropertyValue()、findAllByQuery() 和 findAllByRange(),它们会直接映射到 Neo4j 的底层索引基础设施,但是要考虑到 Repository 领域类和既有的索引相关的注解。TraversalRepository 中暴露了类似的方法,它的 findAllByTraversal() 方法允许直接访问 Neo4j 强大的图遍历机制。其他的 Repository 接口提供了空间查询(spatial query)和 Cypher-DSL 集成的方法。
7.6.2 衍生和基于注解的查找方法
除了之前所讨论的基本操作之外,Spring Data Neo4j Repository 也支持借助 Cypher 查询语言自定义查找方法。在基于注解和衍生的查找方法中,额外的 Pageable 和 Sort 方法参数会在查询执行的时候自动考虑进来。它们会被转换成合适的 ORDER BY、SKIP 和 LIMIT 声明。
基于注解的查找方法
查找方法可以直接使用 Cypher,只需添加 @Query 注解并包含查询字符串就可以了,如示例 7-18 所示。方法的参数也会作为参数传递给 Cypher 查询,这可以通过参数的位置也可以通过 @Parameter 注解所声明的名称来完成,所以可以在查询字符串中使用{index}或{name}。
示例 7-18 在 Repository 查询方法上添加 Cypher 查询注解
结果处理
查找方法的返回类型可以是 Iterable<T>,在这种情况下查询的求值是延迟进行的,也可以是其他的接口:Collection<T>、List<T>、Set<T>以及 Page<T>。T 是查询的结果类型,它可以是映射的领域实体(当返回节点或关系时)也可以是原始类型。关于查询结果的映射,这里提供了很简单的基于接口的支持。要映射结果,首先要创建带有 @MapResult 注解的接口。在这个接口中声明检索每列值的方法。对这些方法分别添加上 @ResultColumn("columnName") 注解,如示例 7-19 所示。
示例 7-19 定义 MapResult 并将其用于接口方法中
为了避免针对不同的粒度、结果类型和容器类产生过多的查询方法,Spring Data Neo4j 为结果处理提供了 API。这些 API 涵盖了自动化和编码形式的值转换。结果处理 API 的核心在于将可迭代(iterable)的结果转换为不同的类型,在这里会使用到所配置的或给定的 ResultConverter,这要取决于结果大小的粒度,可能还要与目标容器的类型有关,如示例 7-20 所示。
示例 7-20 结果处理 API
衍生的查找方法
如第 2 章中所述,衍生查找方法(参见 2.2.2 小节“衍生查询”)是真正与众不同的方式。它们会使用目标领域实体的既有映射信息并且会对查找方法的名字进行智能的解析,从而生成能够获取所需信息的查询。
衍生查找方法 - 如 ProductRepository.findByNameAndColorAndTagName (name, color, tagName) - 以 find(By) 或 get(By) 开头,接下来会包含一系列的属性表达式。每个属性表达式指向当前类型的属性名,或者指向其他关联领域实体类型及其一个属性。这些属性必须存在于实体之中。如果不是这样的话,在 ApplicationContext 启动时,Repository 会出现创建错误。
对于所有合法的查找方法,Repository 会通过领域实体的映射信息构建适当的查询。在查询构建的时候,有很多方面会考虑起来,如图类型的展现、索引信息、域类型、关系类型以及方向。这也是进行适当转义的地方。
因此,示例 7-20 将会被转换成的查询如示例 7-21 所示。
示例 7-21 生成的衍生查询
这个示例展现了对已添加索引的属性如何基于索引进行查找以及简单的属性比较。如果方法名要引用其他关联的实体,那么查询构造器会在生成的查询中检查这些实体包含了什么。构造器还将关系的方向和关系类型添加到实体中。如果路径中有更多的属性,要重复相同的行为。
属性对比所支持的关键字包括:
- 算数比较如 GreaterThan、Equals 或 NotEquals;
- 用于检查空值(或不存在)的 IsNull 和 IsNotNull;
- 用于字符串对比的 Contains、StartsWith、EndsWith 和 Like;
- 用于对表达式求反的 Not 前缀;
- 用于匹配正则表达式的 Regexp。
对于很多典型的查询用例,在 Repository 接口中编写并使用衍生查找声明是很容易的。仅对那些关联较多的查询才需要基于注解的查询、遍历描述(Traversal Description)或按需手动遍历。
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论