Spring Data Neo4j 扩展

Spring Data Neo4j 仓库可用扩展

Spring Data Neo4j 为存储库提供了一些扩展或“混合”功能,可将其添加到存储库中。什么是混合(mixin)?根据维基百科所述,混合是一种编程语言概念,允许程序员将某些代码注入到类中。混合编程是一种软件开发风格,即先在某个类中创建功能单元,然后将其与其他类进行组合(混合)。spring-doc.cadn.net.cn

Java 在语言层面不支持该概念,但我们通过若干接口及运行时环境来模拟它,该运行时环境会添加适当的实现类和拦截器。spring-doc.cadn.net.cn

Mixins 默认添加的是 QueryByExampleExecutorReactiveQueryByExampleExecutor。这些接口在 按示例查询 中有详细说明。spring-doc.cadn.net.cn

提供的额外混合类(mixins)包括:spring-doc.cadn.net.cn

为生成的查询添加动态条件

无论是 QuerydslPredicateExecutor 还是 CypherdslConditionExecutor 都提供了相同的概念:SDN 生成查询,您只需提供“谓词”(Query DSL)或“条件”(Cypher DSL),这些将被添加到查询中。我们推荐使用 Cypher DSL,因为这是 SDN 原生支持的。您甚至可以考虑使用 注解处理器,它会为您生成一个静态元模型。spring-doc.cadn.net.cn

它是如何工作的?如上所述声明您的仓库,并添加以下接口中的一个spring-doc.cadn.net.cn

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 混入
interface PersonRepository extends Neo4jRepository<Person, Long>, (1)
        CypherdslConditionExecutor<Person> {

    (2)

}
1 标准仓库声明
2 Cypher DSL 混入

示例用法展示了 Cypher DSL 条件执行器:spring-doc.cadn.net.cn

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 方法可将这些参数实际传递给正在执行的查询)。spring-doc.cadn.net.cn

使用(动态)Cypher-DSL语句进行实体和投影

添加相应的混合类与使用 条件执行器 并无不同:spring-doc.cadn.net.cn

interface PersonRepository extends Neo4jRepository<Person, Long>, CypherdslStatementExecutor<Person> {

}

请在扩展 ReactiveNeo4jRepository 时使用 ReactiveCypherdslStatementExecutorspring-doc.cadn.net.cn

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.spring-doc.cadn.net.cn

如果查询需要参数,则必须通过 Cypher-DSL 本身进行定义,并由其进行填充,如下所示的列表:spring-doc.cadn.net.cn

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 语句执行器也提供了一个返回分页结果的重载版本。spring-doc.cadn.net.cn