Neo4j 与 Java 入门
在本教程中,我们将逐步构建一个 Java 应用程序,以突出展示图数据库能为 Java 开发人员带来的能力与优势。
在直接编写代码之前,让我们先简要了解一下为什么 Java 开发人员可能会选择图数据库。

为什么 Java 开发人员会选择图数据库?
与当今大多数技术一样,图数据库的出现是为了解决业务和技术难题,而这些难题在现有技术中往往难以解决,甚至无法解决。属性图数据模型使用数学上的顶点和边来表示数据域中的实体和关系,其可视化特性使得通过白板绘图即可轻松表达。
作为 Java 开发人员,在构建与不同数据存储(如关系型数据库、NoSQL 或图数据库)交互的应用程序时,有很多不同的选择。那么,为什么开发人员会关心或特别倾向于选择图数据库呢?
首先,图数据库可以帮助解决其他数据库未针对性优化的复杂问题。例如,比较不同库或语言版本(如 Java 17 和 21)之间的变更树,或者回顾某个特定功能(如日期/时间功能)在各版本发布过程中是如何演变的。虽然在其他类型的数据库中也可以回答这些问题,但它们的结构设计在这些场景下并不具备最佳性能——它们旨在以更快的速度解决其他问题。而图数据库的设计初衷就是为了快速有效地回答这类查询。
其次,我们可以让数据域与应用域保持更紧密的一致性,因为图数据库自然地以现实世界中存在的方式表示数据,而 Java 映射数据的方式也与之相似。Java 将数据组织为领域实体,作为该类型对象的一般大纲。领域类由包含该实体详细信息的属性变量(即姓名、日期、标题等属性)和作为该对象类型实例的构造函数组成。关系是指向另一个对象的变量,将实体连接在一起。这种 Java 应用程序与图数据库模型之间的自然对齐,减少了为“让用例适应”技术限制而进行的修改量。
图数据库与 Java 的强强联合,使开发人员能够更轻松地设计、编写、部署和维护技术系统。
创建项目
在了解了相关背景后,让我们通过编写一个应用程序来看看实际效果。
在编写核心 Java 代码之前,需要先进行一些设置。首先是 Neo4j 数据库。对此有几种选择,Neo4j 向公众提供了几个包含不同数据集的演示数据库。本教程将使用 goodreads 演示数据库,您可以通过浏览器访问 https://demo.neo4jlabs.com:7473/browser/ 来访问它。连接数据库所需的凭据稍后会提供。该数据最初来源于 UCSD 书籍图谱项目^,包含有关书籍、作者以及相关的评论和编写这些评论的用户的信息。
另一方面,如果您希望运行自己的实例和数据,最简单的方法是启动 Neo4j Aura 的免费实例,这是一种数据库即服务(DBaaS)。它完全由 Neo4j 在云端托管,因此无需担心管理或维护问题。您只需在 console.neo4j.io 注册一个帐户,并按照提示创建并启动一个实例(注意:请务必下载凭据文件或将凭据保存在您可以随时查阅的地方,因为它们不会再次显示)。
在演示版 goodreads 浏览器中,您可以通过在查询编辑器命令行中运行以下查询来验证数据库中的数据。
CALL db.schema.visualization();

接下来,我们需要设置应用程序项目模板。创建 Java 项目的方法有很多,但本教程将演示一个不使用框架、仅使用 Maven 管理依赖项的示例。请确保您已安装 Maven,导航到您想要在机器上创建项目的目录,然后运行下方所示的命令来创建一个项目。
mvn archetype:generate -DgroupId=com.neo4j.app -DartifactId=neo4j-java-tutorial -DarchetypeArtifactId=maven-archetype-quickstart -DarchetypeVersion=1.5 -DinteractiveMode=false
如果您想进行更多自定义,可以选择调整 group 和 artifactId,但请注意任何更改,因为它们可能会影响后续命令。项目创建完成后,在您喜欢的 IDE 中将其打开(例如,如果您还没有最喜欢的 IDE,VSCode 或 IntelliJ IDEA 都是极佳的选择)。今天演示的所有完整代码都可以在相关的 Github 仓库中找到。
Neo4j Java 驱动程序
要编写的第一段代码是添加 Neo4j Java 驱动程序的依赖项。打开 pom.xml 文件,并在 <dependencies></dependencies> 部分内添加以下代码块。
<dependency>
<groupId>org.neo4j.driver</groupId>
<artifactId>neo4j-java-driver</artifactId>
<version>5.28.5</version>
</dependency>Code language: Java (java)
*注意:* 请使用 5.28.5 或更高版本的 Java 驱动程序,因为在此之前的版本不包含对象映射功能。
接下来,您需要一些属性来连接到 Neo4j 数据库。尽管属性文件的命名和位置有很多选择,但请在 src/main/ 目录下创建一个 resources 文件夹,然后创建一个名为 application.properties 的文件。接着,打开 application.properties 文件,并按下方所示添加属性和值。
NEO4J_URI=neo4j+s://demo.neo4jlabs.com
NEO4J_USERNAME=goodreads
NEO4J_PASSWORD=goodreads
NEO4J_DATABASE=goodreadsCode language: JavaScript (javascript)
现在,您可以开始将数据域编写到应用程序中,从 Book 实体开始。
Java 领域类
在 src/main/java/com/neo4j/app 下创建一个名为 Book.java 的新文件。注意:将类定义更改为 record(而不是 class)。接下来,添加几个字段变量以匹配下方的示例。
import java.util.List;
public record Book(String book_id,
String title,
String isbn,
String isbn13,
String url,
String publisher,
Double average_rating,
List<Author> authors) {
}Code language: Java (java)
该实体被定义为 Java record,这使得对象不可变。在类中,书籍实体的属性被定义为字段,例如 title、publisher 和 authors 列表。
接下来,Author 类尚未定义,因此请在 src/main/java/com/neo4j/app 中创建一个名为 Author.java 的文件并将其定义也更改为 record 来修复此问题。
public record Author(String author_id,
String name,
String average_rating) {
}Code language: Java (java)
该实体看起来与 Book.java 类似,只是包含了与作者相关的字段,例如 name。
下一步是连接到 Neo4j,运行查询,并返回映射到您定义的 Book 和 Author 实体的结果。
连接到 Neo4j
处理连接数据库等实用工具方法的方式有多种,但对于本教程,请在 src/main/java/com/neo4j/app 中创建一个名为 AppUtils.java 的新文件。
这将把连接 Neo4j 的样板代码抽象到一个工具类中,并使主类保持更简洁。接下来,您可以定义一个方法来加载之前在 application.properties 文件中定义的凭据属性,以及一个初始化 Neo4j Java 驱动程序实例的方法。
import java.io.IOException;
import java.io.InputStream;
import org.neo4j.driver.AuthTokens;
import org.neo4j.driver.Driver;
import org.neo4j.driver.GraphDatabase;
public class AppUtils {
public static void loadProperties() {
try (InputStream input = AppUtils.class.getClassLoader().getResourceAsStream("application.properties")) {
if (input == null) {
throw new ApplicationPropertiesNotFound("application.properties file not found");
}
System.getProperties().load(input);
} catch (IOException ex) {
throw new RuntimeException("Error loading application.properties", ex);
}
}
public static Driver initDriver() {
return GraphDatabase.driver(
System.getProperty("NEO4J_URI"),
AuthTokens.basic(
System.getProperty("NEO4J_USERNAME"),
System.getProperty("NEO4J_PASSWORD"))
);
}
public static class ApplicationPropertiesNotFound extends RuntimeException {
public ApplicationPropertiesNotFound(String message) {
super(message);
}
}
}Code language: Java (java)
此类创建了一个从 /resources/application.properties 文件加载应用程序属性的方法。如果找不到文件(ApplicationPropertiesNotFound)或无法加载(捕获 IOException),则会抛出错误。当调用下一个方法(`initDriver()`)时,它将创建一个 Neo4j Java 驱动程序实例,用于向 Neo4j 发送数据并返回数据。
在工具类准备就绪后,下一段代码将位于核心 App.java 类中。
在 Neo4j 中运行查询
在 src/main/java/com/neo4j/app 目录中打开 App.java 类,并在主类中添加下方所示的代码。
public class App {
public static void main(String[] args) {
AppUtils.loadProperties();
try (var driver = AppUtils.initDriver()) {
driver.verifyConnectivity();
//code to query Neo4j + return results
} catch (Exception e) {
e.printStackTrace();
}
}
}Code language: Java (java)
主方法首先需要加载应用程序属性,以便它可以访问环境变量来连接 Neo4j 数据库。接下来的代码块是一个 try catch,它创建一个驱动程序实例来连接到 Neo4j,并验证是否可以成功连接,同时捕获任何错误。
在下一节中,我们将添加用于在 Neo4j 中运行查询并返回结果的代码。
执行查询
要在 Neo4j 中运行查询,您可以使用 .executableQuery().execute() 方法链,并将查询传递给 executableQuery 方法。结果需要映射到 Book.class,这是通过从结果中流式传输记录,将每条记录映射到领域对象,并转换为列表来实现的。
public class App {
public static void main(String[] args) {
AppUtils.loadProperties();
try (var driver = AppUtils.initDriver()) {
driver.verifyConnectivity();
var results = driver.executableQuery("""
MATCH (b:Book)<-[rel:AUTHORED]-(a:Author)
WITH b, collect(a{.*}) as authorList
RETURN b {
.*,
authors: authorList
} AS book
LIMIT 10;
""").execute()
.records()
.stream()
.map(record -> record.get("book").as(Book.class))
.toList();
for (var book : results) {
System.out.println(book);
}
} catch (Exception e) {
e.printStackTrace();
}
}
}Code language: Java (java)
新变量 results 将保存查询结果,因此我们调用 driver.executableQuery() 并传入我们想要运行以从 Neo4j 检索书籍的查询字符串。该查询查找 (Book)<-[AUTHORED]-(Author) 模式,然后将结果作为映射返回,作者嵌套在书籍对象内部,而不是作为单独的节点和关系。
这是因为 Java 驱动程序对象映射不会自动进行映射,所以我们需要以与领域实体相同的格式返回结果。由于作者嵌套在 Book 领域类中,数据库结果也应按该方式格式化。即使书籍和作者实体在数据库中包含更多属性,也只有那些与我们领域类中定义的属性相匹配的内容才会被映射回来。
在 .execute() 运行查询之后,接下来的几行调用方法来检索 Record 映射,然后流式传输这些对象,并将每个记录对象的 book 字段映射到领域 Book.class,最后将其放入一个列表中(.toList())。
下一个代码块中的 for 循环获取列表中的每本书并将其打印到控制台。
后续步骤
如果您想检索图的不同“视图”或部分,可以创建新的领域类(将它们视为实体视图或投影),并将 Neo4j 的结果映射到该对象格式中。您还可以创建带有参数的更动态的查询,将喜欢的框架合并到应用程序中,或者创建一个 REST API。
要继续学习 Neo4j 和 Java,请查看 Neo4j GraphAcademy 上的 使用 Java 操作 Neo4j 课程!GraphAcademy 上还有各种其他免费的在线课程,涵盖 Neo4j 基础、Cypher 和 Spring Data Neo4j 等主题。
资源
- 代码:本教程的仓库
- GraphAcademy 课程:使用 Java 操作 Neo4j
- Neo4j 数据库:Aura:免费实例


