一、为什么需要图数据库?
关系型数据库通过外键和 JOIN 完全可以表达实体之间的关系,比如"作者-书籍"用一张中间表就能搞定。那为什么还要引入图数据库?
核心差异在于查询的简洁性和多层关系遍历的效率:
- 简单关系(如"某作者写了哪些书"):关系型数据库 JOIN 一下就行,图数据库没有明显优势
- 多层遍历(如"这本书的作者的朋友推荐过什么书"):关系型数据库需要层层 JOIN,SQL 又长又慢;图数据库天然沿着关系遍历,性能差距随层数增大而急剧拉大
- 复杂关系网络(如社交网络、知识图谱、推荐系统):图数据库的原生索引结构就是为关系设计的,查询效率和代码可读性都远优于关系型方案
Neo4j 是目前最流行的原生图数据库,它通过节点(Node)和关系(Relationship)来表达数据,用 Cypher 查询语言来遍历图结构。本文将以"作者-书籍"这个简单场景为例,手把手教你使用 Docker Desktop 安装 Neo4j,并在 Spring Boot 中集成使用。
二、使用 Docker Desktop 安装 Neo4j
2.1 前置条件
确保你的机器上已经安装了 Docker Desktop(支持 Windows / macOS / Linux)。可以从 Docker 官网 下载安装。
2.2 一键启动 Neo4j
打开终端(PowerShell 或 Terminal),执行以下命令:
PowerShell 用户(Windows 推荐),直接复制以下命令:
docker run `
--name neo4j `
-p 7474:7474 -p 7687:7687 `
-v ${HOME}/neo4j/data:/data `
-v ${HOME}/neo4j/logs:/logs `
-v ${HOME}/neo4j/conf:/conf `
-e NEO4J_AUTH=neo4j/neo4j666 `
-e NEO4JLABS_PLUGINS="""[\""apoc\""]""" `
-d neo4j:5.22-community
说明:PowerShell 中使用反引号
`作为行续符(不是\),$HOME会自动解析为你的用户目录(如C:\Users\YourName)。NEO4JLABS_PLUGINS的值需要特殊转义,上面使用了三重引号的方式来正确传递 JSON 数组。
Git Bash / WSL / macOS / Linux 用户,使用标准 Unix 格式:
docker run \
--name neo4j \
-p 7474:7474 -p 7687:7687 \
-v $HOME/neo4j/data:/data \
-v $HOME/neo4j/logs:/logs \
-v $HOME/neo4j/conf:/conf \
-e NEO4J_AUTH=neo4j/neo4j666 \
-e NEO4JLABS_PLUGINS='["apoc"]' \
-d neo4j:5.22-community
2.3 参数详解
| 参数 | 说明 |
|---|---|
--name neo4j |
容器名称,方便后续管理 |
-p 7474:7474 |
映射 Neo4j Browser 的 HTTP 端口 |
-p 7687:7687 |
映射 Bolt 协议端口(Java 应用连接用) |
-v .../data:/data |
数据持久化,容器删除后数据不丢失 |
-v .../logs:/logs |
日志持久化,方便排查问题 |
-v .../conf:/conf |
配置文件持久化,方便自定义调优 |
-e NEO4J_AUTH=neo4j/neo4j666 |
设置初始账号密码(用户名 neo4j,密码 neo4j666) |
-e NEO4JLABS_PLUGINS='["apoc"]' |
自动安装 APOC 插件(提供数百个实用函数) |
-d neo4j:5.22-community |
后台运行,使用社区版 5.22 |
2.4 验证安装
启动后等待约 10 秒,打开浏览器访问:
http://localhost:7474
你会看到 Neo4j Browser 界面,使用上面设置的用户名 neo4j 和密码 neo4j666 登录即可。
可以用以下命令检查容器运行状态:
docker ps
三、理解图数据模型
在 Neo4j 中,数据由两类元素构成:
- 节点(Node):带有标签的实体,比如一个"作者"或一本"书籍"
- 关系(Relationship):节点之间的有向连接,比如作者
WROTE(写了)一本书
用一张图来表达:
(Author:作者 {name:'刘慈欣'}) -[:WROTE]-> (Book:书籍 {title:'三体', year:2008})
(Author:作者 {name:'刘慈欣'}) -[:WROTE]-> (Book:书籍 {title:'流浪地球', year:2000})
(Author:作者 {name:'刘慈欣'}) -[:WROTE]-> (Book:书籍 {title:'球状闪电', year:2004})
(Author:作者 {name:'余华'}) -[:WROTE]-> (Book:书籍 {title:'活着', year:1993})
(Author:作者 {name:'余华'}) -[:WROTE]-> (Book:书籍 {title:'许三观卖血记', year:1995})
这种结构非常适合表达现实世界中"谁创作了什么"、"谁和谁有关系"这类场景。
四、初始化示例数据
登录 Neo4j Browser 后,执行以下 Cypher 语句创建示例数据:
// 创建作者节点
CREATE (a1:Author {name: '刘慈欣'})
CREATE (a2:Author {name: '余华'})
CREATE (a3:Author {name: '东野圭吾'})
// 创建书籍节点
CREATE (b1:Book {title: '三体', year: 2008})
CREATE (b2:Book {title: '流浪地球', year: 2000})
CREATE (b3:Book {title: '球状闪电', year: 2004})
CREATE (b4:Book {title: '活着', year: 1993})
CREATE (b5:Book {title: '许三观卖血记', year: 1995})
CREATE (b6:Book {title: '白夜行', year: 1999})
CREATE (b7:Book {title: '解忧杂货店', year: 2012})
// 创建 作者-写了-书籍 关系
CREATE (a1)-[:WROTE]->(b1)
CREATE (a1)-[:WROTE]->(b2)
CREATE (a1)-[:WROTE]->(b3)
CREATE (a2)-[:WROTE]->(b4)
CREATE (a2)-[:WROTE]->(b5)
CREATE (a3)-[:WROTE]->(b6)
CREATE (a3)-[:WROTE]->(b7)
执行后可以在 Browser 中用以下语句可视化查看图结构:
MATCH (n)-[r]->(m) RETURN n, r, m
你会看到一个清晰的作者-书籍关系图。
五、Spring Boot 集成 Neo4j
5.1 添加 Maven 依赖
在 pom.xml 中添加 Spring Data Neo4j 的 Starter:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-neo4j</artifactId>
</dependency>
5.2 配置连接信息
在 application.yml 中添加 Neo4j 连接配置:
spring:
neo4j:
uri: bolt://localhost:7687
authentication:
username: neo4j
password: neo4j666
connection-timeout: 5s
verify-connection: false
配置说明:
uri使用 Bolt 协议,端口7687是 Java 应用与 Neo4j 通信的标准端口verify-connection: false允许应用在 Neo4j 未启动时也能正常启动,避免开发环境中的启动阻塞connection-timeout: 5s设置连接超时时间,防止长时间阻塞
5.3 定义节点实体类
Neo4j 中的节点用 @Node 注解标记:
Author(作者)节点:
@Node("Author")
public class Author {
@Id
private String name;
public Author() {}
public Author(String name) { this.name = name; }
public String getName() { return name; }
}
Book(书籍)节点:
@Node("Book")
public class Book {
@Id
private String title;
private int year;
public Book() {}
public Book(String title, int year) {
this.title = title;
this.year = year;
}
public String getTitle() { return title; }
public int getYear() { return year; }
}
@Node注解中的值(如"Author"、"Book")对应 Neo4j 中的节点标签。@Id标记主键字段。
5.4 定义查询结果 DTO
对于自定义 Cypher 查询的返回结果,定义一个 DTO 类来接收:
public class AuthorBooksDto {
private String author;
private List<String> otherBooks;
public AuthorBooksDto() {}
public AuthorBooksDto(String author, List<String> otherBooks) {
this.author = author;
this.otherBooks = otherBooks;
}
public String getAuthor() { return author; }
public void setAuthor(String author) { this.author = author; }
public List<String> getOtherBooks() { return otherBooks; }
public void setOtherBooks(List<String> otherBooks) { this.otherBooks = otherBooks; }
}
5.5 编写 Repository 接口
这是核心部分——继承 Neo4jRepository,使用 @Query 注解编写自定义 Cypher 查询:
@Repository
public interface BookGraphRepository extends Neo4jRepository<Book, String> {
@Query("""
MATCH (b:Book {title: $title}) <-[:WROTE]- (a:Author) -[:WROTE]-> (other:Book)
WHERE other.title <> $title
RETURN a.name AS author, collect(other.title + ' (' + other.year + ')') AS otherBooks
""")
List<AuthorBooksDto> findOtherBooksBySameAuthor(String title);
}
Cypher 查询逐行解析:
| 语句 | 含义 |
|---|---|
MATCH (b:Book {title: $title}) |
找到指定标题的书籍节点 |
<-[:WROTE]- (a:Author) |
沿着 WROTE 关系反向找到作者 |
-[:WROTE]-> (other:Book) |
再从该作者出发,找到他写的其他书籍 |
WHERE other.title <> $title |
排除当前这本书本身 |
RETURN a.name AS author, collect(...) |
返回作者名和其他书籍列表(格式为"书名 (年份)") |
这条查询实现了:给定一本书,找出它的作者,再找出该作者写的其他所有书。 这就是图数据库的魅力——用一条简洁的语句就能表达"同作者的其他作品"这种关系遍历,而不需要多表 JOIN。
5.6 封装 Service 层
@Service
public class BookGraphService {
@Autowired
private BookGraphRepository repository;
public String findRelatedBooks(String bookTitle) {
List<AuthorBooksDto> results = repository.findOtherBooksBySameAuthor(bookTitle);
if (results.isEmpty()) {
return "未找到写过《" + bookTitle + "》的作者的其他作品。";
}
StringBuilder sb = new StringBuilder();
for (AuthorBooksDto dto : results) {
sb.append(String.format("- 作者 %s 还写了:%s\n",
dto.getAuthor(), String.join("、", dto.getOtherBooks())));
}
return sb.toString().trim();
}
}
调用 findRelatedBooks("三体") 将返回:
- 作者 刘慈欣 还写了:流浪地球 (2000)、球状闪电 (2004)
六、图数据库在 RAG 中的应用
上面实现的"同作者其他书籍"查询,非常适合作为 GraphRAG 的知识增强手段。
当用户问"《三体》的作者还有什么好看的书?"时,传统向量检索可能无法准确召回结构化关系,而图数据库可以精确返回关联信息。
典型流程:
用户提问 → LLM 提取关键实体(书名) → BookGraphService 查询图数据库 → 将结果作为上下文 → LLM 生成最终回答
这种方式将图数据库的精确关系推理能力和大模型的自然语言生成能力结合起来,是 GraphRAG 的核心思路之一。
七、常用运维命令速查
# 查看容器日志
docker logs neo4j
# 停止容器
docker stop neo4j
# 启动已停止的容器
docker start neo4j
# 删除容器(数据不会丢失,因为已挂载到本地)
docker rm neo4j
# 进入容器内部
docker exec -it neo4j bash
# 在 Browser 中查看所有数据
# 访问 http://localhost:7474 后执行:
MATCH (n) RETURN n
八、小结
本文完整介绍了以下内容:
- 安装部署:使用 Docker Desktop 一行命令安装 Neo4j 5.22 社区版,并配置数据持久化和 APOC 插件
- 数据建模:理解图数据库的节点与关系模型,用"作者-书籍"场景创建示例数据
- Spring Boot 集成:通过 Spring Data Neo4j 的
@Node、@Query注解实现实体映射和 Cypher 查询 - 关系遍历查询:用一条 Cypher 语句实现"同作者的其他书籍"查询,对比关系型数据库的 JOIN 操作更加简洁直观
- RAG 增强:将图查询结果作为上下文注入 LLM,实现 GraphRAG 场景下的结构化知识检索
图数据库在处理实体关系查询时具有天然优势。如果你的应用需要频繁处理"谁和谁有什么关系"、"和这个实体关联的有哪些"这类查询,不妨试试 Neo4j 结合 Spring Data Neo4j 的方案。
作者:Work Hard Work Smart
出处:http://www.cnblogs.com/linlf03/
欢迎任何形式的转载,未经作者同意,请保留此段声明!
浙公网安备 33010602011771号