此版本仍在开发中,尚不被认为是稳定的。对于最新的稳定版本,请使用 Spring Data Neo4j 7.5.2! |
Spring Data Neo4j 预测
如上所述,投影有两种类型:接口和基于 DTO 的投影。在 Spring Data Neo4j 中,两种类型的投影都直接影响传输哪些属性和关系通过网络。因此,这两种方法都可以减少数据库的负载,以防您处理包含的节点和实体应用程序中的所有使用场景中可能不需要的许多属性。
对于基于接口和 DTO 的投影,Spring Data Neo4j 将使用存储库的域类型来构建 查询。 将考虑所有可能更改查询的属性的所有注释。域类型是通过存储库声明定义的类型(给定一个声明,例如interface TestRepository extends CrudRepository<TestEntity, Long>
域类型将为TestEntity
).
基于接口的投影将始终是底层域类型的动态代理。定义的访问器的名称在此类接口上(例如getName
)必须解析为属性(此处:name
) 存在于投影实体上。这些属性是否在域类型上具有访问器并不相关,只要它们可以通过以下方式访问通用的 Spring Data 基础设施。后者已经得到保证,因为域类型不会是首先。
当与自定义查询一起使用时,基于 DTO 的投影会更加灵活。虽然标准查询派生自原始域类型,因此只能使用在那里定义的属性和关系,但自定义查询可以添加其他属性。
规则如下:首先,域类型的属性用于填充 DTO。如果 DTO 声明其他属性 - 通过访问器或字段 - Spring Data Neo4j 会在生成的记录中查找匹配的属性。属性必须按名称完全匹配,并且可以是简单类型(如org.springframework.data.neo4j.core.convert.Neo4jSimpleTypes
) 或已知持久实体的集合。支持这些实体的集合,但不支持映射。
多级投影
Spring Data Neo4j 还支持多级投影。
interface ProjectionWithNestedProjection {
String getName();
List<Subprojection1> getLevel1();
interface Subprojection1 {
String getName();
List<Subprojection2> getLevel2();
}
interface Subprojection2 {
String getName();
}
}
即使可以对循环投影进行建模或指向将创建循环的实体,投影逻辑不会遵循这些循环,而只会创建无循环查询。
多级投影受限于它们应该投影的实体。RelationshipProperties
在这种情况下属于实体类别,如果应用预测,则需要得到尊重。
投影的数据作
如果已将投影作为 DTO 提取,则可以修改其值。但是,如果使用基于接口的投影,则不能只更新接口。可以使用的典型模式是在域实体类中提供一个方法,该方法使用接口并使用从接口复制的值创建域实体。这样,就可以更新实体,并使用投影蓝图/掩码再次保留它,如下一节所述。
预测的持久性
类似于通过投影检索数据,它们也可以用作持久性的蓝图。 这Neo4jTemplate
提供了一个流畅的 API 来将这些投影应用于保存作。
您可以保存给定域类的投影
Projection projection = neo4jTemplate.save(DomainClass.class).one(projectionValue);
或者,您可以保存域对象,但仅遵循投影中定义的字段。
Projection projection = neo4jTemplate.saveAs(domainObject, Projection.class);
在这两种情况下,也可用于基于集合的作,只有投影中定义的字段和关系才会更新。
为了防止删除数据(例如删除关系),您应该始终至少加载所有稍后应该保留的数据。 |
完整示例
给定以下实体、投影和相应的存储库:
@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();
}
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
如下所示,其行为将按照列表中的说明进行。
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 | 返回扩展域类型的类的一个或多个实例的方法将仅返回扩展类的实例。相关方法的域类型将是扩展类,该类仍然满足存储库本身的域类型 |
4 | 此方法返回接口投影,因此该方法的返回类型不同与存储库的域类型。接口只能访问域类型中定义的属性。后缀By 需要使 SDN 不查找名为InterfaceProjections 在TestEntity |
5 | 此方法返回 DTO 投影。执行它将导致 SDN 发出警告,如 DTO 定义的那样numberOfRelations 作为附加属性,该属性不在域类型的契约中。带注释的属性aProperty 在TestEntity 将被正确翻译为a_property 在查询中。如上所述,返回类型与存储库的域类型不同。后缀By 需要使 SDN 不查找名为DTOProjections 在TestEntity |
6 | 此方法还返回 DTO 投影。但是,不会发出警告,因为查询包含投影中定义的其他属性的拟合值 |