Neo4j 演员-电影关系系统 (SpringBoot 实现)
Neo4j 演员-电影关系系统 (SpringBoot 实现)
下面是一个完整的 SpringBoot 项目,使用 Neo4j 数据库实现演员演电影的关系模型,采用三层架构,使用 MyBatis 进行 Neo4j 的查询操作(不使用 Spring Data Neo4j 的 ORM 风格)。
项目结构
src/main/java/com/example/neo4jdemo/ ├── config │ └── Neo4jConfig.java ├── controller │ ├── ActorController.java │ └── MovieController.java ├── entity │ ├── Actor.java │ ├── Movie.java │ └── Relationship.java ├── mapper │ ├── ActorMapper.java │ ├── MovieMapper.java │ └── RelationshipMapper.java ├── service │ ├── impl │ │ ├── ActorServiceImpl.java │ │ └── MovieServiceImpl.java │ ├── ActorService.java │ └── MovieService.java └── Neo4jDemoApplication.java
1. 添加依赖 (pom.xml)
<dependencies> <!-- Spring Boot Starter --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <!-- Lombok --> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <optional>true</optional> </dependency> <!-- MyBatis --> <dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> <version>2.2.0</version> </dependency> <!-- Neo4j JDBC Driver --> <dependency> <groupId>org.neo4j.driver</groupId> <artifactId>neo4j-java-driver</artifactId> <version>4.4.5</version> </dependency> <!-- For testing --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> </dependencies>
2. 配置类 (Neo4jConfig.java)
package com.example.neo4jdemo.config; import org.neo4j.driver.*; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @Configuration public class Neo4jConfig { @Value("${spring.neo4j.uri}") private String uri; @Value("${spring.neo4j.authentication.username}") private String username; @Value("${spring.neo4j.authentication.password}") private String password; @Bean public Driver neo4jDriver() { return GraphDatabase.driver(uri, AuthTokens.basic(username, password)); } @Bean public Session neo4jSession(Driver driver) { return driver.session(); } }
3. 实体类
Actor.java
package com.example.neo4jdemo.entity; import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; @Data @NoArgsConstructor @AllArgsConstructor public class Actor { private Long id; private String name; private Integer born; }
Movie.java
package com.example.neo4jdemo.entity; import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; import java.util.List; @Data @NoArgsConstructor @AllArgsConstructor public class Movie { private Long id; private String title; private Integer released; private String tagline; private List<Actor> actors; }
Relationship.java
package com.example.neo4jdemo.entity; import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; @Data @NoArgsConstructor @AllArgsConstructor public class Relationship { private Long actorId; private Long movieId; private List<String> roles; }
4. Mapper 接口
ActorMapper.java
package com.example.neo4jdemo.mapper; import com.example.neo4jdemo.entity.Actor; import org.apache.ibatis.annotations.Mapper; import java.util.List; @Mapper public interface ActorMapper { void createActor(Actor actor); Actor findActorById(Long id); List<Actor> findAllActors(); void deleteActor(Long id); List<Actor> findActorsByMovieId(Long movieId); }
MovieMapper.java
package com.example.neo4jdemo.mapper; import com.example.neo4jdemo.entity.Movie; import org.apache.ibatis.annotations.Mapper; import java.util.List; @Mapper public interface MovieMapper { void createMovie(Movie movie); Movie findMovieById(Long id); List<Movie> findAllMovies(); void deleteMovie(Long id); List<Movie> findMoviesByActorId(Long actorId); }
RelationshipMapper.java
package com.example.neo4jdemo.mapper; import com.example.neo4jdemo.entity.Relationship; import org.apache.ibatis.annotations.Mapper; @Mapper public interface RelationshipMapper { void createRelationship(Relationship relationship); void deleteRelationshipsByActorId(Long actorId); void deleteRelationshipsByMovieId(Long movieId); }
5. Mapper XML 文件 (resources/mapper/)
ActorMapper.xml
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <mapper namespace="com.example.neo4jdemo.mapper.ActorMapper"> <insert id="createActor"> CREATE (a:Actor {id: #{id}, name: #{name}, born: #{born}}) </insert> <select id="findActorById" resultType="com.example.neo4jdemo.entity.Actor"> MATCH (a:Actor {id: #{id}}) RETURN a.id as id, a.name as name, a.born as born </select> <select id="findAllActors" resultType="com.example.neo4jdemo.entity.Actor"> MATCH (a:Actor) RETURN a.id as id, a.name as name, a.born as born </select> <delete id="deleteActor"> MATCH (a:Actor {id: #{id}}) DELETE a </delete> <select id="findActorsByMovieId" resultType="com.example.neo4jdemo.entity.Actor"> MATCH (m:Movie {id: #{movieId}})<-[:ACTED_IN]-(a:Actor) RETURN a.id as id, a.name as name, a.born as born </select> </mapper>
MovieMapper.xml
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <mapper namespace="com.example.neo4jdemo.mapper.MovieMapper"> <insert id="createMovie"> CREATE (m:Movie {id: #{id}, title: #{title}, released: #{released}, tagline: #{tagline}}) </insert> <select id="findMovieById" resultType="com.example.neo4jdemo.entity.Movie"> MATCH (m:Movie {id: #{id}}) OPTIONAL MATCH (m)<-[r:ACTED_IN]-(a:Actor) RETURN m.id as id, m.title as title, m.released as released, m.tagline as tagline, collect({id: a.id, name: a.name, born: a.born}) as actors </select> <select id="findAllMovies" resultType="com.example.neo4jdemo.entity.Movie"> MATCH (m:Movie) OPTIONAL MATCH (m)<-[r:ACTED_IN]-(a:Actor) RETURN m.id as id, m.title as title, m.released as released, m.tagline as tagline, collect({id: a.id, name: a.name, born: a.born}) as actors </select> <delete id="deleteMovie"> MATCH (m:Movie {id: #{id}}) DELETE m </delete> <select id="findMoviesByActorId" resultType="com.example.neo4jdemo.entity.Movie"> MATCH (a:Actor {id: #{actorId}})-[:ACTED_IN]->(m:Movie) RETURN m.id as id, m.title as title, m.released as released, m.tagline as tagline </select> </mapper>
RelationshipMapper.xml
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <mapper namespace="com.example.neo4jdemo.mapper.RelationshipMapper"> <insert id="createRelationship"> MATCH (a:Actor {id: #{actorId}}), (m:Movie {id: #{movieId}}) CREATE (a)-[r:ACTED_IN {roles: #{roles}}]->(m) </insert> <delete id="deleteRelationshipsByActorId"> MATCH (a:Actor {id: #{actorId}})-[r]->() DELETE r </delete> <delete id="deleteRelationshipsByMovieId"> MATCH ()-[r]->(m:Movie {id: #{movieId}}) DELETE r </delete> </mapper>
6. Service 层
ActorService.java
package com.example.neo4jdemo.service; import com.example.neo4jdemo.entity.Actor; import java.util.List; public interface ActorService { Actor createActor(Actor actor); Actor getActorById(Long id); List<Actor> getAllActors(); void deleteActor(Long id); List<Actor> getActorsByMovieId(Long movieId); }
ActorServiceImpl.java
package com.example.neo4jdemo.service.impl; import com.example.neo4jdemo.entity.Actor; import com.example.neo4jdemo.mapper.ActorMapper; import com.example.neo4jdemo.mapper.RelationshipMapper; import com.example.neo4jdemo.service.ActorService; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import java.util.List; @Service @RequiredArgsConstructor public class ActorServiceImpl implements ActorService { private final ActorMapper actorMapper; private final RelationshipMapper relationshipMapper; @Override public Actor createActor(Actor actor) { actorMapper.createActor(actor); return actor; } @Override public Actor getActorById(Long id) { return actorMapper.findActorById(id); } @Override public List<Actor> getAllActors() { return actorMapper.findAllActors(); } @Override @Transactional public void deleteActor(Long id) { relationshipMapper.deleteRelationshipsByActorId(id); actorMapper.deleteActor(id); } @Override public List<Actor> getActorsByMovieId(Long movieId) { return actorMapper.findActorsByMovieId(movieId); } }
MovieService.java
package com.example.neo4jdemo.service; import com.example.neo4jdemo.entity.Movie; import java.util.List; public interface MovieService { Movie createMovie(Movie movie); Movie getMovieById(Long id); List<Movie> getAllMovies(); void deleteMovie(Long id); List<Movie> getMoviesByActorId(Long actorId); void addActorToMovie(Long actorId, Long movieId, List<String> roles); }
MovieServiceImpl.java
package com.example.neo4jdemo.service.impl; import com.example.neo4jdemo.entity.Movie; import com.example.neo4jdemo.entity.Relationship; import com.example.neo4jdemo.mapper.MovieMapper; import com.example.neo4jdemo.mapper.RelationshipMapper; import com.example.neo4jdemo.service.MovieService; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import java.util.List; @Service @RequiredArgsConstructor public class MovieServiceImpl implements MovieService { private final MovieMapper movieMapper; private final RelationshipMapper relationshipMapper; @Override public Movie createMovie(Movie movie) { movieMapper.createMovie(movie); return movie; } @Override public Movie getMovieById(Long id) { return movieMapper.findMovieById(id); } @Override public List<Movie> getAllMovies() { return movieMapper.findAllMovies(); } @Override @Transactional public void deleteMovie(Long id) { relationshipMapper.deleteRelationshipsByMovieId(id); movieMapper.deleteMovie(id); } @Override public List<Movie> getMoviesByActorId(Long actorId) { return movieMapper.findMoviesByActorId(actorId); } @Override @Transactional public void addActorToMovie(Long actorId, Long movieId, List<String> roles) { Relationship relationship = new Relationship(actorId, movieId, roles); relationshipMapper.createRelationship(relationship); } }
7. Controller 层
ActorController.java
package com.example.neo4jdemo.controller; import com.example.neo4jdemo.entity.Actor; import com.example.neo4jdemo.service.ActorService; import lombok.RequiredArgsConstructor; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; import java.util.List; @RestController @RequestMapping("/api/actors") @RequiredArgsConstructor public class ActorController { private final ActorService actorService; @PostMapping public ResponseEntity<Actor> createActor(@RequestBody Actor actor) { return ResponseEntity.ok(actorService.createActor(actor)); } @GetMapping("/{id}") public ResponseEntity<Actor> getActorById(@PathVariable Long id) { return ResponseEntity.ok(actorService.getActorById(id)); } @GetMapping public ResponseEntity<List<Actor>> getAllActors() { return ResponseEntity.ok(actorService.getAllActors()); } @DeleteMapping("/{id}") public ResponseEntity<Void> deleteActor(@PathVariable Long id) { actorService.deleteActor(id); return ResponseEntity.noContent().build(); } @GetMapping("/by-movie/{movieId}") public ResponseEntity<List<Actor>> getActorsByMovieId(@PathVariable Long movieId) { return ResponseEntity.ok(actorService.getActorsByMovieId(movieId)); } }
MovieController.java
package com.example.neo4jdemo.controller; import com.example.neo4jdemo.entity.Movie; import com.example.neo4jdemo.service.MovieService; import lombok.RequiredArgsConstructor; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; import java.util.List; @RestController @RequestMapping("/api/movies") @RequiredArgsConstructor public class MovieController { private final MovieService movieService; @PostMapping public ResponseEntity<Movie> createMovie(@RequestBody Movie movie) { return ResponseEntity.ok(movieService.createMovie(movie)); } @GetMapping("/{id}") public ResponseEntity<Movie> getMovieById(@PathVariable Long id) { return ResponseEntity.ok(movieService.getMovieById(id)); } @GetMapping public ResponseEntity<List<Movie>> getAllMovies() { return ResponseEntity.ok(movieService.getAllMovies()); } @DeleteMapping("/{id}") public ResponseEntity<Void> deleteMovie(@PathVariable Long id) { movieService.deleteMovie(id); return ResponseEntity.noContent().build(); } @GetMapping("/by-actor/{actorId}") public ResponseEntity<List<Movie>> getMoviesByActorId(@PathVariable Long actorId) { return ResponseEntity.ok(movieService.getMoviesByActorId(actorId)); } @PostMapping("/{movieId}/actors/{actorId}") public ResponseEntity<Void> addActorToMovie( @PathVariable Long movieId, @PathVariable Long actorId, @RequestBody List<String> roles) { movieService.addActorToMovie(actorId, movieId, roles); return ResponseEntity.noContent().build(); } }
8. 主应用类
package com.example.neo4jdemo; import org.mybatis.spring.annotation.MapperScan; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication @MapperScan("com.example.neo4jdemo.mapper") public class Neo4jDemoApplication { public static void main(String[] args) { SpringApplication.run(Neo4jDemoApplication.class, args); } }
9. 配置文件 (application.yml)
spring: neo4j: uri: bolt://localhost:7687 authentication: username: neo4j password: your_password mybatis: mapper-locations: classpath:mapper/*.xml configuration: map-underscore-to-camel-case: true
10. 初始化数据 (可选)
你可以创建一个 CommandLineRunner bean 来初始化一些测试数据:
package com.example.neo4jdemo; import com.example.neo4jdemo.entity.Actor; import com.example.neo4jdemo.entity.Movie; import com.example.neo4jdemo.service.ActorService; import com.example.neo4jdemo.service.MovieService; import lombok.RequiredArgsConstructor; import org.springframework.boot.CommandLineRunner; import org.springframework.stereotype.Component; import java.util.List; @Component @RequiredArgsConstructor public class DataInitializer implements CommandLineRunner { private final ActorService actorService; private final MovieService movieService; @Override public void run(String... args) throws Exception { // 初始化演员 Actor tomHanks = new Actor(1L, "Tom Hanks", 1956); Actor keanuReeves = new Actor(2L, "Keanu Reeves", 1964); Actor laurenceFishburne = new Actor(3L, "Laurence Fishburne", 1961); actorService.createActor(tomHanks); actorService.createActor(keanuReeves); actorService.createActor(laurenceFishburne); // 初始化电影 Movie theMatrix = new Movie(1L, "The Matrix", 1999, "Welcome to the Real World"); Movie forrestGump = new Movie(2L, "Forrest Gump", 1994, "Life is like a box of chocolates"); movieService.createMovie(theMatrix); movieService.createMovie(forrestGump); // 建立关系 movieService.addActorToMovie(keanuReeves.getId(), theMatrix.getId(), List.of("Neo")); movieService.addActorToMovie(laurenceFishburne.getId(), theMatrix.getId(), List.of("Morpheus")); movieService.addActorToMovie(tomHanks.getId(), forrestGump.getId(), List.of("Forrest Gump")); } }
使用说明
- 确保已安装 Neo4j 数据库并运行
- 更新 application.yml 中的 Neo4j 连接信息
- 启动应用程序
- 可以使用以下 API 端点:
演员相关:
- POST /api/actors - 创建演员
- GET /api/actors/{id} - 获取指定ID的演员
- GET /api/actors - 获取所有演员
- DELETE /api/actors/{id} - 删除指定ID的演员
- GET /api/actors/by-movie/{movieId} - 获取参演指定电影的演员
电影相关:
- POST /api/movies - 创建电影
- GET /api/movies/{id} - 获取指定ID的电影
- GET /api/movies - 获取所有电影
- DELETE /api/movies/{id} - 删除指定ID的电影
- GET /api/movies/by-actor/{actorId} - 获取指定演员参演的电影
- POST /api/movies/{movieId}/actors/{actorId} - 添加演员到电影中
这个实现方案:
- 使用 SpringBoot + MyBatis + Lombok
- 完整的三层架构 (Controller-Service-DAO)
- 使用 XML 查询语句
- 不使用 Spring Data Neo4j 的 ORM 风格
- 包含 service 的 impl 实现
更多推荐

所有评论(0)