Neo4j 演员-电影关系系统 (SpringBoot 实现)

Neo4j 演员-电影关系系统 (SpringBoot 实现)

下面是一个完整的 SpringBoot 项目,使用 Neo4j 数据库实现演员演电影的关系模型,采用三层架构,使用 MyBatis 进行 Neo4j 的查询操作(不使用 Spring Data Neo4j 的 ORM 风格)。

项目结构

css 复制代码
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)

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)

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

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

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

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

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

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

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 复制代码
<?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 复制代码
<?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 复制代码
<?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

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

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

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

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

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

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. 主应用类

java 复制代码
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)

yaml 复制代码
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 来初始化一些测试数据:

java 复制代码
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"));
    }
}

使用说明

  1. 确保已安装 Neo4j 数据库并运行
  2. 更新 application.yml 中的 Neo4j 连接信息
  3. 启动应用程序
  4. 可以使用以下 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} - 添加演员到电影中

这个实现方案:

  1. 使用 SpringBoot + MyBatis + Lombok
  2. 完整的三层架构 (Controller-Service-DAO)
  3. 使用 XML 查询语句
  4. 不使用 Spring Data Neo4j 的 ORM 风格
  5. 包含 service 的 impl 实现
相关推荐
Layux1 小时前
使用钉钉开源api发送钉钉工作消息
java·spring boot·钉钉
midsummer_woo1 小时前
基于springboot的在线教育系统(源码+论文)
vue.js·spring boot·mysql
超浪的晨3 小时前
Java 实现 B/S 架构详解:从基础到实战,彻底掌握浏览器/服务器编程
java·开发语言·后端·学习·个人开发
Littlewith3 小时前
Java进阶3:Java集合框架、ArrayList、LinkedList、HashSet、HashMap和他们的迭代器
java·开发语言·spring boot·spring·java-ee·eclipse·tomcat
没有bug.的程序员4 小时前
《 Spring Boot启动流程图解:自动配置的真相》
前端·spring boot·自动配置·流程图
追逐时光者4 小时前
一款超级经典复古的 Windows 9x 主题风格 Avalonia UI 控件库,满满的回忆杀!
后端·.net
Python涛哥5 小时前
go语言基础教程:【1】基础语法:变量
开发语言·后端·golang
我命由我123455 小时前
PostgreSQL 保留关键字冲突问题:语法错误 在 “user“ 或附近的 LINE 1: CREATE TABLE user
数据库·后端·sql·mysql·postgresql·问题·数据库系统
顽疲6 小时前
从零用java实现 小红书 springboot vue uniapp(14) 集成阿里云短信验证码
java·vue.js·spring boot·阿里云·uni-app
LUCIAZZZ6 小时前
final修饰符不可变的底层
java·开发语言·spring boot·后端·spring·操作系统