|
该版本仍在开发中,尚未被视为稳定。对于最新稳定版本,请使用Spring Data Neo4j 8.0.0! |
Spring Data Neo4j 预测
如上所述,投影有两种类型:接口投影和基于DTO的投影。 在 Spring Data Neo4j 中,这两种类型的投影都直接影响哪些属性和关系被转移 通过电报。 因此,如果你处理的是包含的节点和实体,这两种方法都能减轻数据库负担 有很多属性,可能并非所有应用场景都需要。
无论是基于接口还是基于DTO的投影,Spring Data Neo4j都会使用仓库的域类型来构建
查询。所有可能改变查询的属性注释都会被考虑。
域类型是通过仓库声明定义的类型
(给定一个interface TestRepository 扩展了 CrudRepository<TestEntity, Long>域名类型为测试实体).
基于接口的投影始终是底层域类型的动态代理。访问器的名称定义
在这样的接口上(如getName)必须解析为性质(此处:名称)存在于投影实体上的元素。
这些属性是否在域类型上有访问者并不重要,只要它们能通过以下方式访问即可
这是Spring Data的通用基础设施。后者已经被确保了,因为域类型不会是 中持久实体
第一位。
基于DTO的投影在与自定义查询结合时会更灵活一些。而标准查询则从 原始域类型,因此只能使用其中定义的属性和关系,自定义查询 可以添加额外的属性。
规则如下:首先,利用域类型的属性来填充DTO内容。如果DTO声明
通过访问器或字段获取额外属性——Spring Data Neo4j 会在结果记录中查找匹配的属性。
属性必须在名称上完全匹配,并且可以是简单类型(定义如下org.springframework.data.neo4j.core.convert.Neo4jSimpleTypes)
或已知的持续存在实体。这些合集是支持的,但地图不支持。
Spring Data Neo4j 中还内置了一个额外机制,允许在实体定义层面定义加载和持久边界。 更多内容请参见“总计边界”部分。
多层次投影
Spring Data Neo4j 还支持多层次投影。
interface ProjectionWithNestedProjection {
String getName();
List<Subprojection1> getLevel1();
interface Subprojection1 {
String getName();
List<Subprojection2> getLevel2();
}
interface Subprojection2 {
String getName();
}
}
尽管可以建模循环投影或指向将形成循环的实体, 投影逻辑不会遵循这些循环,只会创建无循环的查询。
多层投影受限于它们应投影的实体。关系属性在这种情况下,属于实体类别,如果应用投影,就需要得到尊重。
投影的数据作
如果你已经将投影作为DTO获取,可以修改其值。 但如果你使用基于接口的投影,你不能仅仅更新界面。 一个典型的模式是在你的域实体类中提供一个方法,消耗接口并创建一个包含从接口复制的值的域实体。 这样,你可以更新实体,并像下一节描述的投影蓝图/遮罩一样再次持久化。
投影的持久性
类似于通过投影检索数据,它们也可以作为持久化的蓝图。 这Neo4j模板提供流畅的 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 | 该性质在图中有不同的名称 |
测试实体@Node
class ExtendedTestEntity extends TestEntity {
private String otherAttribute;
}
测试实体interface TestEntityInterfaceProjection {
String getName();
}
测试实体,包括一个额外的属性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 | 该属性不存在于投影实体中 |
一个测试实体如下所示,且其行为将如同商品列表所述。
测试实体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 | 仓库的域类型为测试实体 |
| 2 | 返回一个或多个的方法测试实体只返回实例,因为它与域类型相符 |
| 3 | 返回一个或多个扩展域类型的类实例的方法,只会返回扩展类的实例。该方法的域类型将是扩展类,且仍然满足仓库本身的域类型 |
| 4 | 该方法返回接口投影,因此方法的返回类型不同与仓库的域类型不同。接口只能访问域类型中定义的属性。后缀由是让 SDN 不去寻找接口投影在测试实体 |
| 5 | 该方法返回一个DTO投影。执行该方法会使SDN发出警告,正如DTO定义的关系数量作为附加属性,但该属性不包含在域类型的契约中。注释属性aProperty在测试实体将正确翻译为a_property在查询中。如上所述,返回类型与仓库的域类型不同。后缀由是让 SDN 不去寻找DTOProjections在测试实体 |
| 6 | 该方法还返回一个DTO投影。但不会发出警告,因为查询包含投影中定义的附加属性的拟合值值 |
总边界
通过引入多重投影来反映多层关系可能会很繁琐。为了在实体层面简化这一点,可以添加一个额外的参数总边界并提供1.n类。在这种情况下,参数化实体只会报告其@Idfield back 和 SDN 无法跟踪其关系,也无法获取其他属性。
仍然可以使用基于接口的投影来描述这些实体。这些投影可以更广泛,比如声明的聚合边界,比如包含属性或关系。