spring-data-neo4j by giuseppe-trisciuoglio/developer-kit
npx skills add https://github.com/giuseppe-trisciuoglio/developer-kit --skill spring-data-neo4j在以下场景中使用此技能:
@Query 注解编写 Cypher 查询Spring Data Neo4j 为 Neo4j 集成提供了三个抽象层次:
主要特性包括响应式和命令式操作模式、不可变实体映射、通过 @Query 注解支持自定义查询、Spring 的 Conversion Service 集成,以及对图关系和遍历的完整支持。
添加依赖:
spring-boot-starter-data-neo4j广告位招租
在这里展示您的产品或服务
触达数万 AI 开发者,精准高效
implementation 'org.springframework.boot:spring-boot-starter-data-neo4j'配置连接属性:
spring.neo4j.uri=bolt://localhost:7687 spring.neo4j.authentication.username=neo4j spring.neo4j.authentication.password=secret
配置 Cypher-DSL 方言(推荐):
@Bean Configuration cypherDslConfiguration() { return Configuration.newConfig() .withDialect(Dialect.NEO4J_5).build(); }
@Node 注解标记实体类@Id(不可变,自然标识符)@Id @GeneratedValue(Neo4j 内部 ID)@Relationship 注解定义关系@Property 自定义属性名称Neo4jRepository<Entity, ID> 用于命令式操作ReactiveNeo4jRepository<Entity, ID> 用于响应式操作@Query 注解处理复杂的 Cypher 查询@DataNeo4jTest 进行存储库测试Maven:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-neo4j</artifactId>
</dependency>
Gradle:
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-data-neo4j'
}
application.properties:
spring.neo4j.uri=bolt://localhost:7687
spring.neo4j.authentication.username=neo4j
spring.neo4j.authentication.password=secret
配置 Neo4j Cypher-DSL 方言:
@Configuration
public class Neo4jConfig {
@Bean
Configuration cypherDslConfiguration() {
return Configuration.newConfig()
.withDialect(Dialect.NEO4J_5).build();
}
}
@Node("Movie")
public class MovieEntity {
@Id
private final String title; // 业务键作为 ID
@Property("tagline")
private final String description;
private final Integer year;
@Relationship(type = "ACTED_IN", direction = Direction.INCOMING)
private List<Roles> actorsAndRoles = new ArrayList<>();
@Relationship(type = "DIRECTED", direction = Direction.INCOMING)
private List<PersonEntity> directors = new ArrayList<>();
public MovieEntity(String title, String description, Integer year) {
this.title = title;
this.description = description;
this.year = year;
}
}
@Node("Movie")
public class MovieEntity {
@Id @GeneratedValue
private Long id;
private final String title;
@Property("tagline")
private final String description;
public MovieEntity(String title, String description) {
this.id = null; // 切勿手动设置
this.title = title;
this.description = description;
}
// 用于生成 ID 的不可变性的 wither 方法
public MovieEntity withId(Long id) {
if (this.id != null && this.id.equals(id)) {
return this;
} else {
MovieEntity newObject = new MovieEntity(this.title, this.description);
newObject.id = id;
return newObject;
}
}
}
@Repository
public interface MovieRepository extends Neo4jRepository<MovieEntity, String> {
// 从方法名推导查询
MovieEntity findOneByTitle(String title);
List<MovieEntity> findAllByYear(Integer year);
List<MovieEntity> findByYearBetween(Integer startYear, Integer endYear);
}
@Repository
public interface MovieRepository extends ReactiveNeo4jRepository<MovieEntity, String> {
Mono<MovieEntity> findOneByTitle(String title);
Flux<MovieEntity> findAllByYear(Integer year);
}
命令式与响应式:
Neo4jRepository 进行阻塞式、命令式操作ReactiveNeo4jRepository 进行非阻塞式、响应式操作@Query 的自定义查询@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);
@Query("MATCH (b:Book)-[:WRITTEN_BY]->(a:Author) " +
"WHERE a.name = $name " +
"RETURN b ORDER BY b.year DESC")
List<Book> findBooksByAuthorOrderByYearDesc(@Param("name") String name);
}
自定义查询最佳实践:
$parameterName 作为参数占位符@Param 注解测试配置:
@DataNeo4jTest
class BookRepositoryIntegrationTest {
private static Neo4j embeddedServer;
@BeforeAll
static void initializeNeo4j() {
embeddedServer = Neo4jBuilders.newInProcessBuilder()
.withDisabledServer() // 无需 HTTP 访问
.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() {
embeddedServer.close();
}
@DynamicPropertySource
static void neo4jProperties(DynamicPropertyRegistry registry) {
registry.add("spring.neo4j.uri", embeddedServer::boltURI);
registry.add("spring.neo4j.authentication.username", () -> "neo4j");
registry.add("spring.neo4j.authentication.password", () -> "null");
}
@Autowired
private BookRepository bookRepository;
@Test
void givenBookExists_whenFindOneByTitle_thenBookIsReturned() {
Book book = bookRepository.findOneByTitle("The Fellowship of the Ring");
assertThat(book.getIsbn()).isEqualTo("978-0547928210");
}
}
输入:
MovieEntity movie = new MovieEntity("The Matrix", "Welcome to the Real World", 1999);
movieRepository.save(movie);
MovieEntity found = movieRepository.findOneByTitle("The Matrix");
输出:
MovieEntity{
title="The Matrix",
description="Welcome to the Real World",
year=1999,
actorsAndRoles=[],
directors=[]
}
输入:
List<Book> books = authorRepository.findBooksAfterYear("J.R.R. Tolkien", 1950);
输出:
[
Book{isbn="978-0547928210", name="The Fellowship of the Ring", year=1954},
Book{isbn="978-0547928203", name="The Two Towers", year=1956},
Book{isbn="978-0547928227", name="The Return of the King", year=1957}
]
输入:
@Query("MATCH (m:Movie)<-[:ACTED_IN]-(a:Person) " +
"WHERE m.title = $title RETURN a.name as actorName")
List<String> findActorsByMovieTitle(@Param("title") String title);
List<String> actors = movieRepository.findActorsByMovieTitle("The Matrix");
输出:
["Keanu Reeves", "Laurence Fishburne", "Carrie-Anne Moss", "Hugo Weaving"]
从基础到高级示例的进展,涵盖完整的电影数据库、社交网络模式、电子商务产品目录、自定义查询和响应式操作。
查看示例以获取全面的代码示例。
@Id)和生成 ID(@Id @GeneratedValue)之间选择Neo4jRepository 用于命令式操作,或 ReactiveNeo4jRepository 用于响应式操作@QuerywithFixture() Cypher 查询提供测试数据@DataNeo4jTest 进行测试切片@Transactional 已正确配置。有关详细文档,包括完整的 API 参考、Cypher 查询模式和配置选项:
每周安装次数
349
代码仓库
GitHub 星标数
173
首次出现
2026年2月3日
安全审计
安装于
claude-code275
gemini-cli267
opencode266
cursor261
codex260
github-copilot245
To use this skill when you need to:
@Query annotationsSpring Data Neo4j provides three levels of abstraction for Neo4j integration:
Key features include reactive and imperative operation modes, immutable entity mapping, custom query support via @Query annotation, Spring's Conversion Service integration, and full support for graph relationships and traversals.
Add the dependency:
spring-boot-starter-data-neo4jimplementation 'org.springframework.boot:spring-boot-starter-data-neo4j'Configure connection properties:
spring.neo4j.uri=bolt://localhost:7687 spring.neo4j.authentication.username=neo4j spring.neo4j.authentication.password=secret
Configure Cypher-DSL dialect (recommended):
@Bean Configuration cypherDslConfiguration() { return Configuration.newConfig() .withDialect(Dialect.NEO4J_5).build(); }
@Node annotation to mark entity classes@Id (immutable, natural identifier)@Id @GeneratedValue (Neo4j internal ID)@Relationship annotation@Property for custom property namesNeo4jRepository<Entity, ID> for imperative operationsReactiveNeo4jRepository<Entity, ID> for reactive operations@Query annotation for complex Cypher queries@DataNeo4jTest for repository testingMaven:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-neo4j</artifactId>
</dependency>
Gradle:
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-data-neo4j'
}
application.properties:
spring.neo4j.uri=bolt://localhost:7687
spring.neo4j.authentication.username=neo4j
spring.neo4j.authentication.password=secret
Configure Neo4j Cypher-DSL Dialect:
@Configuration
public class Neo4jConfig {
@Bean
Configuration cypherDslConfiguration() {
return Configuration.newConfig()
.withDialect(Dialect.NEO4J_5).build();
}
}
@Node("Movie")
public class MovieEntity {
@Id
private final String title; // Business key as ID
@Property("tagline")
private final String description;
private final Integer year;
@Relationship(type = "ACTED_IN", direction = Direction.INCOMING)
private List<Roles> actorsAndRoles = new ArrayList<>();
@Relationship(type = "DIRECTED", direction = Direction.INCOMING)
private List<PersonEntity> directors = new ArrayList<>();
public MovieEntity(String title, String description, Integer year) {
this.title = title;
this.description = description;
this.year = year;
}
}
@Node("Movie")
public class MovieEntity {
@Id @GeneratedValue
private Long id;
private final String title;
@Property("tagline")
private final String description;
public MovieEntity(String title, String description) {
this.id = null; // Never set manually
this.title = title;
this.description = description;
}
// Wither method for immutability with generated IDs
public MovieEntity withId(Long id) {
if (this.id != null && this.id.equals(id)) {
return this;
} else {
MovieEntity newObject = new MovieEntity(this.title, this.description);
newObject.id = id;
return newObject;
}
}
}
@Repository
public interface MovieRepository extends Neo4jRepository<MovieEntity, String> {
// Query derivation from method name
MovieEntity findOneByTitle(String title);
List<MovieEntity> findAllByYear(Integer year);
List<MovieEntity> findByYearBetween(Integer startYear, Integer endYear);
}
@Repository
public interface MovieRepository extends ReactiveNeo4jRepository<MovieEntity, String> {
Mono<MovieEntity> findOneByTitle(String title);
Flux<MovieEntity> findAllByYear(Integer year);
}
Imperative vs Reactive:
Neo4jRepository for blocking, imperative operationsReactiveNeo4jRepository for non-blocking, reactive operations@Query@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);
@Query("MATCH (b:Book)-[:WRITTEN_BY]->(a:Author) " +
"WHERE a.name = $name " +
"RETURN b ORDER BY b.year DESC")
List<Book> findBooksByAuthorOrderByYearDesc(@Param("name") String name);
}
Custom Query Best Practices:
$parameterName for parameter placeholders@Param annotation when parameter name differs from method parameterTest Configuration:
@DataNeo4jTest
class BookRepositoryIntegrationTest {
private static Neo4j embeddedServer;
@BeforeAll
static void initializeNeo4j() {
embeddedServer = Neo4jBuilders.newInProcessBuilder()
.withDisabledServer() // No HTTP access needed
.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() {
embeddedServer.close();
}
@DynamicPropertySource
static void neo4jProperties(DynamicPropertyRegistry registry) {
registry.add("spring.neo4j.uri", embeddedServer::boltURI);
registry.add("spring.neo4j.authentication.username", () -> "neo4j");
registry.add("spring.neo4j.authentication.password", () -> "null");
}
@Autowired
private BookRepository bookRepository;
@Test
void givenBookExists_whenFindOneByTitle_thenBookIsReturned() {
Book book = bookRepository.findOneByTitle("The Fellowship of the Ring");
assertThat(book.getIsbn()).isEqualTo("978-0547928210");
}
}
Input:
MovieEntity movie = new MovieEntity("The Matrix", "Welcome to the Real World", 1999);
movieRepository.save(movie);
MovieEntity found = movieRepository.findOneByTitle("The Matrix");
Output:
MovieEntity{
title="The Matrix",
description="Welcome to the Real World",
year=1999,
actorsAndRoles=[],
directors=[]
}
Input:
List<Book> books = authorRepository.findBooksAfterYear("J.R.R. Tolkien", 1950);
Output:
[
Book{isbn="978-0547928210", name="The Fellowship of the Ring", year=1954},
Book{isbn="978-0547928203", name="The Two Towers", year=1956},
Book{isbn="978-0547928227", name="The Return of the King", year=1957}
]
Input:
@Query("MATCH (m:Movie)<-[:ACTED_IN]-(a:Person) " +
"WHERE m.title = $title RETURN a.name as actorName")
List<String> findActorsByMovieTitle(@Param("title") String title);
List<String> actors = movieRepository.findActorsByMovieTitle("The Matrix");
Output:
["Keanu Reeves", "Laurence Fishburne", "Carrie-Anne Moss", "Hugo Weaving"]
Progress from basic to advanced examples covering complete movie database, social network patterns, e-commerce product catalogs, custom queries, and reactive operations.
See examples for comprehensive code examples.
@Id) or generated IDs (@Id @GeneratedValue)Neo4jRepository for imperative or ReactiveNeo4jRepository for reactive@Query for complex graph patternswithFixture() Cypher queries@DataNeo4jTest for test slicing@Transactional is properly configured.For detailed documentation including complete API reference, Cypher query patterns, and configuration options:
Weekly Installs
349
Repository
GitHub Stars
173
First Seen
Feb 3, 2026
Security Audits
Gen Agent Trust HubPassSocketPassSnykPass
Installed on
claude-code275
gemini-cli267
opencode266
cursor261
codex260
github-copilot245
React 组合模式指南:Vercel 组件架构最佳实践,提升代码可维护性
106,200 周安装
程序化SEO策略指南:大规模生成高质量可索引页面的可行性评估与系统设计
262 周安装
Sentry PR代码审查技能:自动化修复GitHub PR中Seer指出的代码问题
262 周安装
Electron 33 + Vite + React + TypeScript 构建安全桌面应用教程与架构指南
262 周安装
Go命名规范详解:Google官方风格指南与最佳实践
263 周安装
Mole Mac 清理工具 - macOS 系统优化与磁盘空间管理 CLI 工具
263 周安装
React Three Fiber 后期处理教程:Bloom辉光、Vignette暗角等效果实现
263 周安装