|
此版本仍在开发中,目前尚不被视为稳定版本。如需最新稳定版本,请使用 Spring Data Neo4j 8.0.4! |
Spring Data Neo4j 扩展
Spring Data Neo4j 仓库可用扩展
Spring Data Neo4j 为存储库提供了一些扩展或“混合”功能,可将其添加到存储库中。什么是混合(mixin)?根据维基百科所述,混合是一种编程语言概念,允许程序员将某些代码注入到类中。混合编程是一种软件开发风格,即先在某个类中创建功能单元,然后将其与其他类进行组合(混合)。
Java 在语言层面不支持该概念,但我们通过若干接口及运行时环境来模拟它,该运行时环境会添加适当的实现类和拦截器。
Mixins 默认添加的是 QueryByExampleExecutor 和 ReactiveQueryByExampleExecutor。这些接口在 按示例查询 中有详细说明。
提供的额外混合类(mixins)包括:
-
QuerydslPredicateExecutor -
CypherdslConditionExecutor -
CypherdslStatementExecutor -
ReactiveQuerydslPredicateExecutor -
ReactiveCypherdslConditionExecutor -
ReactiveCypherdslStatementExecutor
为生成的查询添加动态条件
无论是 QuerydslPredicateExecutor 还是 CypherdslConditionExecutor 都提供了相同的概念:SDN 生成查询,您只需提供“谓词”(Query DSL)或“条件”(Cypher DSL),这些将被添加到查询中。我们推荐使用 Cypher DSL,因为这是 SDN 原生支持的。您甚至可以考虑使用 注解处理器,它会为您生成一个静态元模型。
它是如何工作的?如上所述声明您的仓库,并添加以下接口中的一个:
interface QueryDSLPersonRepository extends Neo4jRepository<Person, Long>, (1)
QuerydslPredicateExecutor<Person> {
(2)
}
static class DtoPersonProjection {
private final String firstName;
DtoPersonProjection(String firstName) {
this.firstName = firstName;
}
String getFirstName() {
return this.firstName;
}
}
| 1 | 标准仓库声明 |
| 2 | 查询 DSL 混入 |
OR
interface PersonRepository extends Neo4jRepository<Person, Long>, (1)
CypherdslConditionExecutor<Person> {
(2)
}
| 1 | 标准仓库声明 |
| 2 | Cypher DSL 混入 |
示例用法展示了 Cypher DSL 条件执行器:
Node person = Cypher.node("Person").named("person"); (1)
Property firstName = person.property("firstName"); (2)
Property lastName = person.property("lastName");
assertThat(repository.findAll(
this.firstName.eq(Cypher.anonParameter("Helge"))
.or(this.lastName.eq(Cypher.parameter("someName", "B."))), (3)
this.lastName.descending() (4)
)).extracting(Person::getFirstName).containsExactly("Helge", "Bela");
| 1 | 定义一个命名的 Node 对象,将查询目标指向根节点 |
| 2 | 从中推导出一些属性 |
| 3 | 创建一个 or 条件。第一个名字使用匿名参数,姓氏使用命名参数。这是在这些片段中定义参数的方式之一,也是相较于 Query-DSL 混入(mixin)所具有的优势之一,因为 Query-DSL 混入无法实现这一点。字面量可通过 Cypher.literalOf 表达。 |
| 4 | 定义一个来自某个属性的 SortItem |
Query-DSL 混入(mixin)的代码看起来非常相似。使用 Query-DSL 混入的原因包括:API 的熟悉度以及它还能与其他数据存储一起工作。反对使用它的原因有:需要在类路径中引入额外的库;不支持遍历关系;以及前述提到的它不支持在谓词中使用参数(技术上它确实支持,但没有相应的 API 方法可将这些参数实际传递给正在执行的查询)。
使用(动态)Cypher-DSL语句进行实体和投影
添加相应的混合类与使用 条件执行器 并无不同:
interface PersonRepository extends Neo4jRepository<Person, Long>, CypherdslStatementExecutor<Person> {
}
请在扩展 ReactiveNeo4jRepository 时使用 ReactiveCypherdslStatementExecutor。
The CypherdslStatementExecutor comes with several overloads for findOne and findAll. They all take a Cypher-DSL statement respectively an ongoing definition of that as a first parameter and in case of the projecting methods, a type.
如果查询需要参数,则必须通过 Cypher-DSL 本身进行定义,并由其进行填充,如下所示的列表:
static Statement whoHasFirstNameWithAddress(String name) { (1)
Node p = Cypher.node("Person").named("p"); (2)
Node a = Cypher.anyNode("a");
Relationship r = p.relationshipTo(a, "LIVES_AT");
return Cypher.match(r)
.where(p.property("firstName").isEqualTo(Cypher.anonParameter(name))) (3)
.returning(p.getRequiredSymbolicName(), Cypher.collect(r), Cypher.collect(a))
.build();
}
@Test
void fineOneShouldWork(@Autowired PersonRepository repository) {
Optional<Person> result = repository.findOne(whoHasFirstNameWithAddress("Helge")); (4)
assertThat(result).hasValueSatisfying(namesOnly -> {
assertThat(namesOnly.getFirstName()).isEqualTo("Helge");
assertThat(namesOnly.getLastName()).isEqualTo("Schneider");
assertThat(namesOnly.getAddress()).extracting(Person.Address::getCity).isEqualTo("Mülheim an der Ruhr");
});
}
@Test
void fineOneProjectedShouldWork(@Autowired PersonRepository repository) {
Optional<NamesOnly> result = repository.findOne(whoHasFirstNameWithAddress("Helge"), NamesOnly.class (5)
);
assertThat(result).hasValueSatisfying(namesOnly -> {
assertThat(namesOnly.getFirstName()).isEqualTo("Helge");
assertThat(namesOnly.getLastName()).isEqualTo("Schneider");
assertThat(namesOnly.getFullName()).isEqualTo("Helge Schneider");
});
}
| 1 | 动态查询在辅助方法中以类型安全的方式构建 |
| 2 | 我们已经在 此处 看到过这一点,当时我们还定义了一些用于保存模型的变量。 |
| 3 | 我们定义了一个匿名参数,由传递给该方法的实际值 name 填充。 |
| 4 | 辅助方法返回的语句用于查找实体 |
| 5 | 或一个投影。 |
方法 findAll 的工作方式类似。命令式 Cypher-DSL 语句执行器也提供了一个返回分页结果的重载版本。