此版本仍在开发中,目前尚不被视为稳定版本。如需最新稳定版本,请使用 Spring Data Neo4j 8.0.4spring-doc.cadn.net.cn

Spring Data Neo4j 投影

如上所述,投影分为两种类型:基于接口的投影和基于DTO的投影。在Spring Data Neo4j中,这两种投影都会直接影响哪些属性和关系会通过网络传输。因此,这两种方法都可以在您的应用中处理包含大量属性的节点和实体时,减少数据库的负载,因为这些属性在某些使用场景下可能并不需要。spring-doc.cadn.net.cn

对于基于接口和 DTO 的投影,Spring Data Neo4j 将使用存储库的领域类型来构建查询。所有可能影响查询的属性上的注解都将被考虑在内。领域类型是指通过存储库声明所定义的类型(例如,给定一个声明如 interface TestRepository extends CrudRepository<TestEntity, Long>,则领域类型为 TestEntity)。spring-doc.cadn.net.cn

基于接口的投影始终是底层领域类型动态代理。此类接口上定义的访问器名称(如 getName)必须解析为投影实体中存在的属性(此处: name)。这些属性在领域类型中是否具有访问器并不重要,只要它们能够通过通用的 Spring Data 基础设施进行访问即可。后者已得到保证,因为如果领域类型本身不是持久化实体,则根本不会存在该情况。spring-doc.cadn.net.cn

基于 DTO 的投影在与自定义查询配合使用时更具灵活性。虽然标准查询是根据原始领域类型推导得出的,因此只能使用该类型中定义的属性和关系,但自定义查询可以添加额外的属性。spring-doc.cadn.net.cn

规则如下:首先,使用领域类型属性填充DTO。如果DTO声明了额外的属性(通过访问器或字段),Spring Data Neo4j 会在生成的结果记录中查找匹配的属性。属性必须严格按名称匹配,且可以是简单类型(如org.springframework.data.neo4j.core.convert.Neo4jSimpleTypes中所定义)或已知的持久化实体。支持这些类型的集合,但不支持映射(maps)。spring-doc.cadn.net.cn

Spring Data Neo4j 还内置了一种额外的机制,允许在实体定义级别上定义加载和持久化的边界。
有关此内容的更多信息,请参阅 聚合边界 部分。spring-doc.cadn.net.cn

多级投影

Spring Data Neo4j 也支持多级投影。spring-doc.cadn.net.cn

多级投影示例
interface ProjectionWithNestedProjection {

    String getName();

    List<Subprojection1> getLevel1();

    interface Subprojection1 {
        String getName();
        List<Subprojection2> getLevel2();
    }

    interface Subprojection2 {
        String getName();
    }
}

尽管可以建模循环投影或指向可能造成循环的实体,但投影逻辑不会遵循这些循环,而只会创建无环查询。spring-doc.cadn.net.cn

多级投影与它们应投影的实体相关联。RelationshipProperties 属于此类实体,若应用投影,则需予以尊重。spring-doc.cadn.net.cn

投影的数据操作

如果您已将投影作为 DTO 获取,可以修改其值。但若使用基于接口的投影,则无法直接更新该接口。一种典型的模式是,在您的领域实体类中提供一个方法,该方法接收接口并根据接口中的值创建一个新的领域实体。这样,您便可更新该实体,并再次将其持久化,同时遵循下一节所述的投影蓝图/模板。spring-doc.cadn.net.cn

投影的持久化

类似于通过投影检索数据,它们也可用作持久化的模板。Neo4jTemplate 提供了一个流畅的 API,可用于将这些投影应用于保存操作。spring-doc.cadn.net.cn

您可以为给定的领域类保存一个投影spring-doc.cadn.net.cn

为给定领域类保存投影
Projection projection = neo4jTemplate.save(DomainClass.class).one(projectionValue);

或者,您可以保存一个领域对象,但仅尊重投影中定义的字段。spring-doc.cadn.net.cn

保存具有指定投影蓝图的领域对象
Projection projection = neo4jTemplate.saveAs(domainObject, Projection.class);

在两种情况下,这些也适用于基于集合的操作,但只有在投影中定义的字段和关系才会被更新。spring-doc.cadn.net.cn

为防止数据被删除(例如,关系的移除),您应始终加载至少所有后续将被持久化的数据。

一个完整的示例

给定以下实体、投影及相应的仓库:spring-doc.cadn.net.cn

一个简单的实体
@Node
class TestEntity {
    @Id @GeneratedValue private Long id;

    private String name;

    @Property("a_property") (1)
    private String aProperty;
}
1 此属性在图中具有不同的名称
派生实体,继承自 TestEntity
@Node
class ExtendedTestEntity extends TestEntity {

    private String otherAttribute;
}
接口投影的 TestEntity
interface TestEntityInterfaceProjection {

    String getName();
}
DTO 投影的 TestEntity,包含一个额外的属性
class TestEntityDTOProjection {

    private String name;

    private Long numberOfRelations; (1)

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Long getNumberOfRelations() {
        return numberOfRelations;
    }

    public void setNumberOfRelations(Long numberOfRelations) {
        this.numberOfRelations = numberOfRelations;
    }
}
1 此属性不存在于投影实体上

以下显示了 TestEntity 的存储库,其行为将如列表中所述。spring-doc.cadn.net.cn

A repository for the TestEntity
interface TestRepository extends CrudRepository<TestEntity, Long> { (1)

    List<TestEntity> findAll(); (2)

    List<ExtendedTestEntity> findAllExtendedEntities(); (3)

    List<TestEntityInterfaceProjection> findAllInterfaceProjectionsBy(); (4)

    List<TestEntityDTOProjection> findAllDTOProjectionsBy(); (5)

    @Query("MATCH (t:TestEntity) - [r:RELATED_TO] -> () RETURN t, COUNT(r) AS numberOfRelations") (6)
    List<TestEntityDTOProjection> findAllDTOProjectionsWithCustomQuery();
}
1 仓库的域类型是 TestEntity
2 返回一个或多个 TestEntity 的方法将直接返回其实例,因为这与领域类型相匹配。
3 返回一个或多个扩展了领域类型(domain type)的类的实例的方法,将直接返回扩展类的实例。所讨论方法的领域类型将是扩展类,这仍然满足该仓库(repository)自身的领域类型。
4 此方法返回一个接口投影,因此该方法的返回类型与存储库的领域类型不同。该接口只能访问领域类型中定义的属性。By 后缀用于确保 SDN 不会在 TestEntity 中查找名为 InterfaceProjections 的属性。
5 此方法返回一个 DTO 投影。执行它将导致 SDN 发出警告,因为 DTO 定义了 numberOfRelations 作为额外属性,而该属性不在领域类型契约中。TestEntity 中标注的属性 aProperty 将正确地映射到查询中的 a_property。与上文相同,返回类型与存储库的领域类型不同。后缀 By 是必需的,以确保 SDN 不会在 TestEntity 中查找名为 DTOProjections 的属性。
6 此方法也返回一个 DTO 投影。然而,不会发出警告,因为查询包含适用于投影中定义的额外属性的合适值。
虽然存储库在上面的列表使用具体的返回类型来定义投影,另一种变体是使用动态投影如Spring Data Neo4j与其他Spring Data项目共享的文档各部分所述,动态投影可应用于封闭和开放接口投影以及基于类的DTO投影:

动态投影的关键在于,将所需的投影类型作为查询方法的最后一个参数指定在存储库中,如下所示:<T> Collection<T> findByName(String name, Class<T> type). 这是一个声明,可以添加到<br/>TestRepository上方并允许通过同一方法检索不同的投影,而无需重复可能的@Query在多个方法上使用注解。

聚合边界

通过引入多个投影来反映多层级的关系可能会很繁琐。为了简化这一操作(即使在实体级别上),可以添加一个额外的参数 aggregateBoundary 并提供 1..n 个类。使用此方法,参数化实体将仅返回其 @Id 字段,而 SDN 将不会追踪其关系或获取其他属性。spring-doc.cadn.net.cn

仍然可以为这些实体使用基于接口的投影。这些投影甚至可以更广泛,因为声明的聚合边界,例如包括属性或关系。spring-doc.cadn.net.cn