从 SDN+OGM 迁移到 SDN
过去 SDN+OGM 迁移的已知问题
SDN+OGM 多年来一直有着丰富的历史,我们理解迁移大型应用系统既不有趣也无法带来即时利润。我们在从旧版本 Spring Data Neo4j 迁移到新版本时观察到的主要问题大致按以下顺序排列:
- 跳过多次主要升级
-
虽然 Neo4j-OGM 可以独立使用,但 Spring Data Neo4j 不行。它在很大程度上依赖于 Spring Data,进而依赖于 Spring Framework 本身,这最终会影响应用程序的很大一部分。根据应用程序的结构方式,也就是说,框架的任何部分渗透到业务代码的程度,您需要调整应用程序的程度就越大。如果您在应用程序中拥有多个 Spring Data 模块,或者在与图数据库相同的服务层访问关系数据库,情况会更糟。更新两个对象映射框架并非易事。
- 依赖通过 Spring Data 本身配置的嵌入式数据库
-
SDN+OGM 项目中的嵌入式数据库由 Neo4j-OGM 配置。假设您想从 Neo4j 3.0 升级到 3.5,如果不升级整个应用程序,这是不可能的。为什么会这样?因为您选择将数据库嵌入到您的应用程序中,您就将自己绑定到了配置此嵌入式数据库的模块。要拥有另一个嵌入式数据库版本,您必须升级配置它的模块,因为旧模块不支持新数据库。由于 Spring Data 版本始终与 Neo4j-OGM 相对应,您也必须升级它。然而,Spring Data 依赖于 Spring Framework,然后第一个要点中的论点适用。
- 不确定要包含哪些构建块
-
正确理解这些术语并非易事。我们在此处撰写了 SDN+OGM 设置的构建块。可能所有这些都是偶然添加的,您正在处理大量冲突的依赖项。
| 根据这些观察,我们建议在从 SDN+OGM 切换到 SDN 之前,确保您当前应用程序中仅使用 Bolt 或 HTTP 传输。因此,您的应用程序及其访问层在很大程度上独立于数据库版本。从该状态开始,考虑从 SDN+OGM 迁移到 SDN。 |
准备从 SDN+OGM Lovelace 或 SDN+OGM Moore 迁移到 SDN
| Lovelace 发布列车对应于 SDN 5.1.x 和 OGM 3.1.x,而 Moore 对应于 SDN 5.2.x 和 OGM 3.2.x。 |
首先,您必须确保您的应用程序通过 Bolt 协议在服务器模式下针对 Neo4j 运行,这意味着在三种情况中的两种情况下需要工作
您正在使用嵌入式数据库
您已将 org.neo4j:neo4j-ogm-embedded-driver 和 org.neo4j:neo4j 添加到您的项目中,并通过 OGM 工具启动数据库。这不再受支持,您必须设置一个标准的 Neo4j 服务器(支持独立和集群)。
上述依赖项必须删除。
从嵌入式解决方案迁移可能是最艰难的迁移,因为您还需要设置一个服务器。然而,它本身会给您带来很大价值:未来,您将能够升级数据库本身,而无需考虑您的应用程序框架以及数据访问框架。
您正在使用 HTTP 传输
您已添加 org.neo4j:neo4j-ogm-http-driver 并配置了一个类似 user:password@localhost:7474 的 URL。该依赖项必须替换为 org.neo4j:neo4j-ogm-bolt-driver,并且您需要配置一个类似 bolt://:7687 的 Bolt URL,或者使用新的 neo4j:// 方案,该方案也负责路由。
迁移
一旦您确保您的 SDN+OGM 应用程序通过 Bolt 正常工作,您就可以开始迁移到 SDN。
-
删除所有
org.neo4j:neo4j-ogm-*依赖项 -
不支持通过
org.neo4j.ogm.config.Configurationbean 配置 SDN,相反,所有驱动程序配置都通过我们新的 Java 驱动程序启动器进行。您尤其需要调整 URL 和身份验证的属性,请参阅 新旧属性对比
| 您不能通过 XML 配置 SDN。如果您使用 SDN+OGM 应用程序执行此操作,请确保您了解 Spring 应用程序的注解驱动或函数式配置。如今最简单的选择是 Spring Boot。有了我们的启动器,除了连接 URL 和身份验证之外的所有必要配置都已经为您完成。 |
# Old
spring.data.neo4j.embedded.enabled=false # No longer supported
spring.data.neo4j.uri=bolt://:7687
spring.data.neo4j.username=neo4j
spring.data.neo4j.password=secret
# New
spring.neo4j.uri=bolt://:7687
spring.neo4j.authentication.username=neo4j
spring.neo4j.authentication.password=secret
| 这些新属性将来可能会再次更改,当 SDN 和驱动程序最终完全替换旧设置时。 |
最后,添加新的依赖项,请参阅 Gradle 和 Maven 的入门。
然后您就可以替换注解了
| 旧 | 新 |
|---|---|
|
|
|
|
|
|
|
|
|
|
|
无替换,不再需要 |
|
无替换,不再需要 |
|
使用投影;不再支持任意结果映射 |
| 一些 Neo4j-OGM 注解在 SDN 中尚未有相应的注解,有些永远不会有。我们将随着我们支持更多功能而添加到上述列表中。 |
书签管理
@EnableBookmarkManagement 和 @UseBookmark 以及 org.springframework.data.neo4j.bookmark.BookmarkManager 接口及其唯一的实现 org.springframework.data.neo4j.bookmark.CaffeineBookmarkManager 都已移除且不再需要。
SDN 在所有事务中都使用书签,无需配置。您可以删除 CaffeineBookmarkManager 的 bean 声明以及对 com.github.ben-manes.caffeine:caffeine 的依赖。
如果您绝对需要,可以按照这些说明禁用自动书签管理。
自动创建约束和索引
SDN 5.3 及更早版本提供了 Neo4j-OGM 的“自动索引管理器”。
@Index、@CompositeIndex 和 @Required 已被移除,没有替代。为什么?我们认为创建 schema(即使对于无 schema 数据库)不属于领域建模的一部分。您可以争辩说 SDN 模型就是 schema,但我们会回答说我们更喜欢命令查询分离,这意味着我们宁愿定义单独的读写模型。这些对于编写“无聊”的东西和读取图形状的答案非常方便。
除此之外,其中一些注解及其值与特定的 Neo4j 版本或版本绑定,这使得它们难以维护。
然而,最好的论点是投入生产:虽然所有生成 schema 的工具在开发过程中确实有帮助,尤其是在强制执行严格 schema 的数据库中,但它们在生产中往往不太好:如何处理同时运行的不同版本的应用程序?版本 A 断言由较新版本 B 创建的索引?
我们认为最好提前控制这一点,并建议使用受控的数据库迁移,基于 Liquigraph 或 Neo4j migrations 等工具。后者已在 JHipster 项目内部与 SDN 一起使用。这两个项目的共同点是它们将 schema 的当前版本存储在数据库中,并确保 schema 在更新之前符合预期。
从以前的 Neo4j-OGM 注解迁移会影响 @Index、@CompositeIndex 和 @Required,此处 使用 Neo4j-OGM 自动索引管理器的类中给出了这些示例
import org.neo4j.ogm.annotation.CompositeIndex;
import org.neo4j.ogm.annotation.GeneratedValue;
import org.neo4j.ogm.annotation.Id;
import org.neo4j.ogm.annotation.Index;
import org.neo4j.ogm.annotation.Required;
@CompositeIndex(properties = {"tagline", "released"})
public class Movie {
@Id @GeneratedValue Long id;
@Index(unique = true)
private String title;
private String description;
private String tagline;
@Required
private Integer released;
}
它的注解等同于 Cypher 中的以下模式(截至 Neo4j 4.2)
CREATE CONSTRAINT movies_unique_title ON (m:Movie) ASSERT m.title IS UNIQUE;
CREATE CONSTRAINT movies_released_exists ON (m:Movie) ASSERT EXISTS (m.released);
CREATE INDEX movies_tagline_released_idx FOR (m:Movie) ON (m.tagline, m.released);
使用 @Index 而不带 unique = true 等同于 CREATE INDEX movie_title_index FOR (m:Movie) ON (m.title)。请注意,唯一索引已经暗示了一个索引。