1. 概述
Neo4j 是一款流行的图数据库管理系统,专为基于图模型的数据存储、管理和查询而设计。本文将带你一步步配置项目,并使用 Spring Data Neo4j 的各种组件与 Neo4j 数据库交互。
提示:图数据库特别适合处理高度关联的数据,比如社交网络、推荐系统等场景。如果你还没接触过,可以把它想象成“关系型数据库的增强版”——但别被这个说法误导,两者底层设计理念完全不同。
2. 项目搭建
2.1 依赖配置
首先在项目中添加必要依赖。核心依赖是 Spring Boot Starter Data Neo4j:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-neo4j</artifactId>
<version>2.7.14</version>
</dependency>
2.2 测试工具:Neo4j Harness
为了方便测试,我们使用 Neo4j Harness(嵌入式测试工具):
<dependency>
<groupId>org.neo4j.test</groupId>
<artifactId>neo4j-harness</artifactId>
<version>5.10.0</version>
<scope>test</scope>
</dependency>
踩坑提醒:Harness 能让你在测试中启动内存数据库,无需安装完整的 Neo4j 服务。但注意版本兼容性——这里我们用的是 Neo4j 5.x 版本,需确保与 Spring Boot 版本匹配。
2.3 数据库连接配置
在 application.properties
中配置连接参数:
spring.neo4j.uri=bolt://localhost:7687
spring.neo4j.authentication.username=neo4j
spring.neo4j.authentication.password=your_secure_password
接着创建配置类指定数据库方言:
@Configuration
public class Neo4jConfig {
@Bean
Configuration cypherDslConfiguration() {
return Configuration.newConfig()
.withDialect(Dialect.NEO4J_5).build();
}
}
关键点:
NEO4J_5
方向 Spring Data Neo4j 使用 Neo4j 5.x 的语法规范生成查询。如果实际数据库版本不同,这里需要调整。
3. 代码实战
3.1 定义实体类
先创建 Book
实体:
@Node("Book")
public class Book {
@Id
private String isbn;
@Property("name")
private String title;
private Integer year;
@Relationship(type = "WRITTEN_BY", direction = Relationship.Direction.OUTGOING)
private Author author;
// 构造方法、getter/setter 省略
}
注解说明:
@Node
:标记为图数据库节点@Id
:主键标识@Property
:当字段名与数据库属性名不同时使用@Relationship
:定义节点关系(必须指定类型和方向)
再创建 Author
实体:
@Node("Author")
public class Author {
@Id
private Long id;
private String name;
@Relationship(type = "WRITTEN_BY", direction = Relationship.Direction.INCOMING)
private List<Book> books;
// 构造方法、getter/setter 省略
}
注意:
Author
实体中关系方向为INCOMING
,与Book
实体中的OUTGOING
形成双向映射。
3.2 创建仓库接口
定义 BookRepository
:
@Repository
public interface BookRepository extends Neo4jRepository<Book, String> {
Book findOneByTitle(String title);
List<Book> findAllByYear(Integer year);
}
简单粗暴:Spring Data 会自动根据方法名生成 Cypher 查询,无需手写 SQL。类似 JPA 的体验,但底层是图查询语言。
3.3 自定义查询
使用 @Query
注解编写复杂查询。在 AuthorRepository
中添加:
@Repository
public interface AuthorRepository extends Neo4jRepository<Author, Long> {
@Query("MATCH (b:Book)-[:WRITTEN_BY]->(a:Author) WHERE a.name = $name AND b.year > $year RETURN b")
List<Book> findBooksAfterYear(@Param("name") String name, @Param("year") Integer year);
}
查询语法要点:
MATCH
:匹配图模式(类似 SQL 的 JOIN)WHERE
:添加过滤条件$name
:参数占位符(用@Param
绑定方法参数)
老鸟须知:Cypher 是图查询语言,语法与 SQL 差异较大。建议先掌握基础模式匹配语法再进阶。
4. 测试实战
4.1 配置测试环境
使用 Neo4j Harness 搭建测试环境:
class BookAndAuthorRepositoryIntegrationTest {
private static Neo4j newServer;
@BeforeAll
static void initializeNeo4j() {
newServer = Neo4jBuilders.newInProcessBuilder()
.withDisabledServer()
.withFixture("CREATE (b:Book {isbn: '978-0547928210', name: 'The Fellowship of the Ring', year: 1954})" +
"-[:WRITTEN_BY]->(a:Author {id: 1, name: 'J. R. R. Tolkien'})" +
"CREATE (b2:Book {isbn: '978-0547928203', name: 'The Two Towers', year: 1956})-[:WRITTEN_BY]->(a)")
.build();
}
@AfterAll
static void stopNeo4j() {
newServer.close();
}
@DynamicPropertySource
static void neo4jProperties(DynamicPropertyRegistry registry) {
registry.add("spring.neo4j.uri", newServer::boltURI);
registry.add("spring.neo4j.authentication.username", () -> "neo4j");
registry.add("spring.neo4j.authentication.password", () -> "test123");
}
}
关键配置解析:
withDisabledServer()
:禁用 HTTP 接口(测试无需外部访问)withFixture()
:初始化测试数据(Cypher 脚本)@DynamicPropertySource
:动态覆盖配置文件中的连接参数
4.2 仓库测试
添加测试用例:
@DataNeo4jTest
class BookAndAuthorRepositoryIntegrationTest {
// 前面的配置代码...
@Autowired
private BookRepository bookRepository;
@Autowired
private AuthorRepository authorRepository;
@Test
void givenBookExists_whenFindOneByTitle_thenBookIsReturned() {
Book book = bookRepository.findOneByTitle("The Fellowship of the Ring");
assertEquals("978-0547928210", book.getIsbn());
}
@Test
void givenOneBookExistsForYear_whenFindAllByYear_thenOneBookIsReturned() {
List<Book> books = bookRepository.findAllByYear(1954);
assertEquals(1, books.size());
}
@Test
void givenOneBookExistsAfterYear_whenFindBooksAfterYear_thenOneBookIsReturned() {
List<Book> books = authorRepository.findBooksAfterYear("J. R. R. Tolkien", 1955);
assertEquals(1, books.size());
}
}
@DataNeo4jTest
注解会自动配置 Spring Data Neo4j 相关的测试环境,比手动配置更高效。
5. 总结
本文系统介绍了 Spring Data Neo4j 的核心用法:
- 项目搭建与依赖配置
- 实体关系映射(
@Node
、@Relationship
等) - 仓库接口与自定义查询
- 基于 Harness 的集成测试
实战建议:图数据库在复杂关系查询上优势明显,但需注意事务管理和性能优化。生产环境建议使用 Neo4j 集群模式,并配置适当的索引策略。
完整代码示例可在 GitHub 获取。