Browse Source

spring-boot-demo-neo4j 完成

pull/1/head
Yangkai.Shen 5 years ago
parent
commit
c4b5e0c8e3
16 changed files with 699 additions and 0 deletions
  1. +15
    -0
      spring-boot-demo-neo4j/README.md
  2. +16
    -0
      spring-boot-demo-neo4j/pom.xml
  3. +24
    -0
      spring-boot-demo-neo4j/src/main/java/com/xkcoding/neo4j/config/CustomIdStrategy.java
  4. +37
    -0
      spring-boot-demo-neo4j/src/main/java/com/xkcoding/neo4j/constants/NeoConsts.java
  5. +50
    -0
      spring-boot-demo-neo4j/src/main/java/com/xkcoding/neo4j/model/Class.java
  6. +50
    -0
      spring-boot-demo-neo4j/src/main/java/com/xkcoding/neo4j/model/Lesson.java
  7. +60
    -0
      spring-boot-demo-neo4j/src/main/java/com/xkcoding/neo4j/model/Student.java
  8. +41
    -0
      spring-boot-demo-neo4j/src/main/java/com/xkcoding/neo4j/model/Teacher.java
  9. +34
    -0
      spring-boot-demo-neo4j/src/main/java/com/xkcoding/neo4j/payload/ClassmateInfoGroupByLesson.java
  10. +29
    -0
      spring-boot-demo-neo4j/src/main/java/com/xkcoding/neo4j/repository/ClassRepository.java
  11. +20
    -0
      spring-boot-demo-neo4j/src/main/java/com/xkcoding/neo4j/repository/LessonRepository.java
  12. +48
    -0
      spring-boot-demo-neo4j/src/main/java/com/xkcoding/neo4j/repository/StudentRepository.java
  13. +20
    -0
      spring-boot-demo-neo4j/src/main/java/com/xkcoding/neo4j/repository/TeacherRepository.java
  14. +166
    -0
      spring-boot-demo-neo4j/src/main/java/com/xkcoding/neo4j/service/NeoService.java
  15. +7
    -0
      spring-boot-demo-neo4j/src/main/resources/application.yml
  16. +82
    -0
      spring-boot-demo-neo4j/src/test/java/com/xkcoding/neo4j/Neo4jTest.java

+ 15
- 0
spring-boot-demo-neo4j/README.md View File

@@ -1 +1,16 @@
# spring-boot-demo-neo4j

> 此 demo 主要演示了 Spring Boot 如何集成Neo4j操作图数据库,实现一个校园人物关系网。

## 注意

作者编写本demo时,Neo4j 版本为 `3.5.0`,使用 docker 运行,下面是所有步骤:

1. 下载镜像:`docker pull neo4j:3.5.0`
2. 运行容器:`docker run -d -p 7474:7474 -p 7687:7687 --name neo4j-3.5.0 neo4j:3.5.0`
3. 停止容器:`docker stop neo4j-3.5.0`
4. 启动容器:`docker start neo4j-3.5.0`
5. 浏览器 http://localhost:7474/ 访问 neo4j 管理后台,初始账号/密码 neo4j/neo4j,会要求修改初始化密码,我们修改为 neo4j/admin




+ 16
- 0
spring-boot-demo-neo4j/pom.xml View File

@@ -38,6 +38,22 @@
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>

<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>

<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
</dependency>

<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
</dependency>
</dependencies>

<build>


+ 24
- 0
spring-boot-demo-neo4j/src/main/java/com/xkcoding/neo4j/config/CustomIdStrategy.java View File

@@ -0,0 +1,24 @@
package com.xkcoding.neo4j.config;

import cn.hutool.core.util.IdUtil;
import org.neo4j.ogm.id.IdStrategy;

/**
* <p>
* 自定义主键策略
* </p>
*
* @package: com.xkcoding.neo4j.config
* @description: 自定义主键策略
* @author: yangkai.shen
* @date: Created in 2018-12-24 14:40
* @copyright: Copyright (c) 2018
* @version: V1.0
* @modified: yangkai.shen
*/
public class CustomIdStrategy implements IdStrategy {
@Override
public Object generateId(Object o) {
return IdUtil.fastUUID();
}
}

+ 37
- 0
spring-boot-demo-neo4j/src/main/java/com/xkcoding/neo4j/constants/NeoConsts.java View File

@@ -0,0 +1,37 @@
package com.xkcoding.neo4j.constants;

/**
* <p>
* 常量池
* </p>
*
* @package: com.xkcoding.neo4j.constants
* @description: 常量池
* @author: yangkai.shen
* @date: Created in 2018-12-24 14:45
* @copyright: Copyright (c) 2018
* @version: V1.0
* @modified: yangkai.shen
*/
public interface NeoConsts {
/**
* 关系:班级拥有的学生
*/
String R_STUDENT_OF_CLASS = "R_STUDENT_OF_CLASS";

/**
* 关系:班级的班主任
*/
String R_BOSS_OF_CLASS = "R_BOSS_OF_CLASS";

/**
* 关系:课程的老师
*/
String R_TEACHER_OF_LESSON = "R_TEACHER_OF_LESSON";

/**
* 关系:学生选的课
*/
String R_LESSON_OF_STUDENT = "R_LESSON_OF_STUDENT";

}

+ 50
- 0
spring-boot-demo-neo4j/src/main/java/com/xkcoding/neo4j/model/Class.java View File

@@ -0,0 +1,50 @@
package com.xkcoding.neo4j.model;

import com.xkcoding.neo4j.config.CustomIdStrategy;
import com.xkcoding.neo4j.constants.NeoConsts;
import lombok.*;
import org.neo4j.ogm.annotation.GeneratedValue;
import org.neo4j.ogm.annotation.Id;
import org.neo4j.ogm.annotation.NodeEntity;
import org.neo4j.ogm.annotation.Relationship;

/**
* <p>
* 班级节点
* </p>
*
* @package: com.xkcoding.neo4j.model
* @description: 班级节点
* @author: yangkai.shen
* @date: Created in 2018-12-24 14:44
* @copyright: Copyright (c) 2018
* @version: V1.0
* @modified: yangkai.shen
*/
@Data
@NoArgsConstructor
@RequiredArgsConstructor(staticName = "of")
@AllArgsConstructor
@Builder
@NodeEntity
public class Class {
/**
* 主键
*/
@Id
@GeneratedValue(strategy = CustomIdStrategy.class)
private String id;

/**
* 班级名称
*/
@NonNull
private String name;

/**
* 班级的班主任
*/
@Relationship(NeoConsts.R_BOSS_OF_CLASS)
@NonNull
private Teacher boss;
}

+ 50
- 0
spring-boot-demo-neo4j/src/main/java/com/xkcoding/neo4j/model/Lesson.java View File

@@ -0,0 +1,50 @@
package com.xkcoding.neo4j.model;

import com.xkcoding.neo4j.config.CustomIdStrategy;
import com.xkcoding.neo4j.constants.NeoConsts;
import lombok.*;
import org.neo4j.ogm.annotation.GeneratedValue;
import org.neo4j.ogm.annotation.Id;
import org.neo4j.ogm.annotation.NodeEntity;
import org.neo4j.ogm.annotation.Relationship;

/**
* <p>
* 课程节点
* </p>
*
* @package: com.xkcoding.neo4j.model
* @description: 课程节点
* @author: yangkai.shen
* @date: Created in 2018-12-24 14:55
* @copyright: Copyright (c) 2018
* @version: V1.0
* @modified: yangkai.shen
*/
@Data
@NoArgsConstructor
@RequiredArgsConstructor(staticName = "of")
@AllArgsConstructor
@Builder
@NodeEntity
public class Lesson {
/**
* 主键,自定义主键策略,使用UUID生成
*/
@Id
@GeneratedValue(strategy = CustomIdStrategy.class)
private String id;

/**
* 课程名称
*/
@NonNull
private String name;

/**
* 任教老师
*/
@Relationship(NeoConsts.R_TEACHER_OF_LESSON)
@NonNull
private Teacher teacher;
}

+ 60
- 0
spring-boot-demo-neo4j/src/main/java/com/xkcoding/neo4j/model/Student.java View File

@@ -0,0 +1,60 @@
package com.xkcoding.neo4j.model;

import com.xkcoding.neo4j.config.CustomIdStrategy;
import com.xkcoding.neo4j.constants.NeoConsts;
import lombok.*;
import org.neo4j.ogm.annotation.GeneratedValue;
import org.neo4j.ogm.annotation.Id;
import org.neo4j.ogm.annotation.NodeEntity;
import org.neo4j.ogm.annotation.Relationship;

import java.util.List;

/**
* <p>
* 学生节点
* </p>
*
* @package: com.xkcoding.neo4j.model
* @description: 学生节点
* @author: yangkai.shen
* @date: Created in 2018-12-24 14:38
* @copyright: Copyright (c) 2018
* @version: V1.0
* @modified: yangkai.shen
*/
@Data
@NoArgsConstructor
@RequiredArgsConstructor(staticName = "of")
@AllArgsConstructor
@Builder
@NodeEntity
public class Student {
/**
* 主键,自定义主键策略,使用UUID生成
*/
@Id
@GeneratedValue(strategy = CustomIdStrategy.class)
private String id;

/**
* 学生姓名
*/
@NonNull
private String name;

/**
* 学生选的所有课程
*/
@Relationship(NeoConsts.R_LESSON_OF_STUDENT)
@NonNull
private List<Lesson> lessons;

/**
* 学生所在班级
*/
@Relationship(NeoConsts.R_STUDENT_OF_CLASS)
@NonNull
private Class clazz;

}

+ 41
- 0
spring-boot-demo-neo4j/src/main/java/com/xkcoding/neo4j/model/Teacher.java View File

@@ -0,0 +1,41 @@
package com.xkcoding.neo4j.model;

import com.xkcoding.neo4j.config.CustomIdStrategy;
import lombok.*;
import org.neo4j.ogm.annotation.GeneratedValue;
import org.neo4j.ogm.annotation.Id;
import org.neo4j.ogm.annotation.NodeEntity;

/**
* <p>
* 教师节点
* </p>
*
* @package: com.xkcoding.neo4j.model
* @description: 教师节点
* @author: yangkai.shen
* @date: Created in 2018-12-24 14:54
* @copyright: Copyright (c) 2018
* @version: V1.0
* @modified: yangkai.shen
*/
@Data
@NoArgsConstructor
@RequiredArgsConstructor(staticName = "of")
@AllArgsConstructor
@Builder
@NodeEntity
public class Teacher {
/**
* 主键,自定义主键策略,使用UUID生成
*/
@Id
@GeneratedValue(strategy = CustomIdStrategy.class)
private String id;

/**
* 教师姓名
*/
@NonNull
private String name;
}

+ 34
- 0
spring-boot-demo-neo4j/src/main/java/com/xkcoding/neo4j/payload/ClassmateInfoGroupByLesson.java View File

@@ -0,0 +1,34 @@
package com.xkcoding.neo4j.payload;

import com.xkcoding.neo4j.model.Student;
import lombok.Data;
import org.springframework.data.neo4j.annotation.QueryResult;

import java.util.List;

/**
* <p>
* 按照课程分组的同学关系
* </p>
*
* @package: com.xkcoding.neo4j.payload
* @description: 按照课程分组的同学关系
* @author: yangkai.shen
* @date: Created in 2018-12-24 19:18
* @copyright: Copyright (c) 2018
* @version: V1.0
* @modified: yangkai.shen
*/
@Data
@QueryResult
public class ClassmateInfoGroupByLesson {
/**
* 课程名称
*/
private String lessonName;

/**
* 学生信息
*/
private List<Student> students;
}

+ 29
- 0
spring-boot-demo-neo4j/src/main/java/com/xkcoding/neo4j/repository/ClassRepository.java View File

@@ -0,0 +1,29 @@
package com.xkcoding.neo4j.repository;

import com.xkcoding.neo4j.model.Class;
import org.springframework.data.neo4j.repository.Neo4jRepository;

import java.util.Optional;

/**
* <p>
* 班级节点Repository
* </p>
*
* @package: com.xkcoding.neo4j.repository
* @description: 班级节点Repository
* @author: yangkai.shen
* @date: Created in 2018-12-24 15:05
* @copyright: Copyright (c) 2018
* @version: V1.0
* @modified: yangkai.shen
*/
public interface ClassRepository extends Neo4jRepository<Class, String> {
/**
* 根据班级名称查询班级信息
*
* @param name 班级名称
* @return 班级信息
*/
Optional<Class> findByName(String name);
}

+ 20
- 0
spring-boot-demo-neo4j/src/main/java/com/xkcoding/neo4j/repository/LessonRepository.java View File

@@ -0,0 +1,20 @@
package com.xkcoding.neo4j.repository;

import com.xkcoding.neo4j.model.Lesson;
import org.springframework.data.neo4j.repository.Neo4jRepository;

/**
* <p>
* 课程节点Repository
* </p>
*
* @package: com.xkcoding.neo4j.repository
* @description: 课程节点Repository
* @author: yangkai.shen
* @date: Created in 2018-12-24 15:05
* @copyright: Copyright (c) 2018
* @version: V1.0
* @modified: yangkai.shen
*/
public interface LessonRepository extends Neo4jRepository<Lesson, String> {
}

+ 48
- 0
spring-boot-demo-neo4j/src/main/java/com/xkcoding/neo4j/repository/StudentRepository.java View File

@@ -0,0 +1,48 @@
package com.xkcoding.neo4j.repository;

import com.xkcoding.neo4j.model.Student;
import com.xkcoding.neo4j.payload.ClassmateInfoGroupByLesson;
import org.springframework.data.neo4j.annotation.Depth;
import org.springframework.data.neo4j.annotation.Query;
import org.springframework.data.neo4j.repository.Neo4jRepository;
import org.springframework.data.repository.query.Param;

import java.util.List;
import java.util.Optional;

/**
* <p>
* 学生节点Repository
* </p>
*
* @package: com.xkcoding.neo4j.repository
* @description: 学生节点Repository
* @author: yangkai.shen
* @date: Created in 2018-12-24 15:05
* @copyright: Copyright (c) 2018
* @version: V1.0
* @modified: yangkai.shen
*/
public interface StudentRepository extends Neo4jRepository<Student, String> {
/**
* 根据名称查找学生
*
* @param name 姓名
* @param depth 深度
* @return 学生信息
*/
Optional<Student> findByName(String name, @Depth int depth);

/**
* 根据班级查询班级人数
*
* @param className 班级名称
* @return 班级人数
*/
@Query("MATCH (s:Student)-[r:R_STUDENT_OF_CLASS]->(c:Class{name:{className}}) return count(s)")
Long countByClassName(@Param("className") String className);


@Query("match (s:Student)-[:R_LESSON_OF_STUDENT]->(l:Lesson),(l:Lesson)<-[:R_LESSON_OF_STUDENT]-(:Student) with l.name as lessonName,collect(distinct s) as students return lessonName,students")
List<ClassmateInfoGroupByLesson> findByClassmateGroupByLesson();
}

+ 20
- 0
spring-boot-demo-neo4j/src/main/java/com/xkcoding/neo4j/repository/TeacherRepository.java View File

@@ -0,0 +1,20 @@
package com.xkcoding.neo4j.repository;

import com.xkcoding.neo4j.model.Teacher;
import org.springframework.data.neo4j.repository.Neo4jRepository;

/**
* <p>
* 教师节点Repository
* </p>
*
* @package: com.xkcoding.neo4j.repository
* @description: 教师节点Repository
* @author: yangkai.shen
* @date: Created in 2018-12-24 15:05
* @copyright: Copyright (c) 2018
* @version: V1.0
* @modified: yangkai.shen
*/
public interface TeacherRepository extends Neo4jRepository<Teacher, String> {
}

+ 166
- 0
spring-boot-demo-neo4j/src/main/java/com/xkcoding/neo4j/service/NeoService.java View File

@@ -0,0 +1,166 @@
package com.xkcoding.neo4j.service;

import cn.hutool.core.util.StrUtil;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.xkcoding.neo4j.model.Class;
import com.xkcoding.neo4j.model.Lesson;
import com.xkcoding.neo4j.model.Student;
import com.xkcoding.neo4j.model.Teacher;
import com.xkcoding.neo4j.payload.ClassmateInfoGroupByLesson;
import com.xkcoding.neo4j.repository.ClassRepository;
import com.xkcoding.neo4j.repository.LessonRepository;
import com.xkcoding.neo4j.repository.StudentRepository;
import com.xkcoding.neo4j.repository.TeacherRepository;
import org.neo4j.ogm.session.Session;
import org.neo4j.ogm.session.SessionFactory;
import org.neo4j.ogm.transaction.Transaction;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
* <p>
* NeoService
* </p>
*
* @package: com.xkcoding.neo4j.service
* @description: NeoService
* @author: yangkai.shen
* @date: Created in 2018-12-24 15:19
* @copyright: Copyright (c) 2018
* @version: V1.0
* @modified: yangkai.shen
*/
@Service
public class NeoService {
@Autowired
private ClassRepository classRepo;

@Autowired
private LessonRepository lessonRepo;

@Autowired
private StudentRepository studentRepo;

@Autowired
private TeacherRepository teacherRepo;

@Autowired
private SessionFactory sessionFactory;

/**
* 初始化数据
*/
@Transactional
public void initData() {
// 初始化老师
Teacher akai = Teacher.of("迈特凯");
Teacher kakaxi = Teacher.of("旗木卡卡西");
Teacher zilaiye = Teacher.of("自来也");
Teacher gangshou = Teacher.of("纲手");
Teacher dashewan = Teacher.of("大蛇丸");
teacherRepo.save(akai);
teacherRepo.save(kakaxi);
teacherRepo.save(zilaiye);
teacherRepo.save(gangshou);
teacherRepo.save(dashewan);

// 初始化课程
Lesson tishu = Lesson.of("体术", akai);
Lesson huanshu = Lesson.of("幻术", kakaxi);
Lesson shoulijian = Lesson.of("手里剑", kakaxi);
Lesson luoxuanwan = Lesson.of("螺旋丸", zilaiye);
Lesson xianshu = Lesson.of("仙术", zilaiye);
Lesson yiliao = Lesson.of("医疗", gangshou);
Lesson zhouyin = Lesson.of("咒印", dashewan);
lessonRepo.save(tishu);
lessonRepo.save(huanshu);
lessonRepo.save(shoulijian);
lessonRepo.save(luoxuanwan);
lessonRepo.save(xianshu);
lessonRepo.save(yiliao);
lessonRepo.save(zhouyin);

// 初始化班级
Class three = Class.of("第三班", akai);
Class seven = Class.of("第七班", kakaxi);
classRepo.save(three);
classRepo.save(seven);

// 初始化学生
List<Student> threeClass = Lists.newArrayList(Student.of("漩涡鸣人", Lists.newArrayList(tishu, shoulijian, luoxuanwan, xianshu), seven), Student
.of("宇智波佐助", Lists.newArrayList(huanshu, zhouyin, shoulijian), seven), Student.of("春野樱", Lists.newArrayList(tishu, yiliao, shoulijian), seven));
List<Student> sevenClass = Lists.newArrayList(Student.of("李洛克", Lists.newArrayList(tishu), three), Student.of("日向宁次", Lists
.newArrayList(tishu), three), Student.of("天天", Lists.newArrayList(tishu), three));

studentRepo.saveAll(threeClass);
studentRepo.saveAll(sevenClass);

}

/**
* 删除数据
*/
@Transactional
public void delete() {
// 使用语句删除
Session session = sessionFactory.openSession();
Transaction transaction = session.beginTransaction();
session.query("match (n)-[r]-() delete n,r", Maps.newHashMap());
session.query("match (n)-[r]-() delete r", Maps.newHashMap());
session.query("match (n) delete n", Maps.newHashMap());
transaction.commit();

// 使用 repository 删除
studentRepo.deleteAll();
classRepo.deleteAll();
lessonRepo.deleteAll();
teacherRepo.deleteAll();
}

/**
* 根据学生姓名查询所选课程
*
* @param studentName 学生姓名
* @param depth 深度
* @return 课程列表
*/
public List<Lesson> findLessonsFromStudent(String studentName, int depth) {
List<Lesson> lessons = Lists.newArrayList();
studentRepo.findByName(studentName, depth).ifPresent(student -> lessons.addAll(student.getLessons()));
return lessons;
}

/**
* 查询全校学生数
*
* @return 学生总数
*/
public Long studentCount(String className) {
if (StrUtil.isBlank(className)) {
return studentRepo.count();
} else {
return studentRepo.countByClassName(className);
}
}

/**
* 查询同学关系,根据课程
*
* @return 返回同学关系
*/
public Map<String, List<Student>> findClassmatesGroupByLesson() {
List<ClassmateInfoGroupByLesson> groupByLesson = studentRepo.findByClassmateGroupByLesson();
Map<String, List<Student>> result = Maps.newHashMap();

groupByLesson.forEach(classmateInfoGroupByLesson -> result.put(classmateInfoGroupByLesson.getLessonName(), classmateInfoGroupByLesson
.getStudents()));

return result;
}
}

+ 7
- 0
spring-boot-demo-neo4j/src/main/resources/application.yml View File

@@ -0,0 +1,7 @@
spring:
data:
neo4j:
uri: bolt://localhost
username: neo4j
password: admin
open-in-view: false

+ 82
- 0
spring-boot-demo-neo4j/src/test/java/com/xkcoding/neo4j/Neo4jTest.java View File

@@ -0,0 +1,82 @@
package com.xkcoding.neo4j;

import cn.hutool.json.JSONUtil;
import com.xkcoding.neo4j.model.Lesson;
import com.xkcoding.neo4j.model.Student;
import com.xkcoding.neo4j.service.NeoService;
import lombok.extern.slf4j.Slf4j;
import org.junit.Test;
import org.springframework.beans.factory.annotation.Autowired;

import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
* <p>
* 测试Neo4j
* </p>
*
* @package: com.xkcoding.neo4j
* @description: 测试Neo4j
* @author: yangkai.shen
* @date: Created in 2018-12-24 15:17
* @copyright: Copyright (c) 2018
* @version: V1.0
* @modified: yangkai.shen
*/
@Slf4j
public class Neo4jTest extends SpringBootDemoNeo4jApplicationTests {
@Autowired
private NeoService neoService;

/**
* 测试保存
*/
@Test
public void testSave() {
neoService.initData();
}

/**
* 测试删除
*/
@Test
public void testDelete() {
neoService.delete();
}

/**
* 测试查询 鸣人 学了哪些课程
*/
@Test
public void testFindLessonsByStudent() {
// 深度为1,则课程的任教老师的属性为null
// 深度为2,则会把课程的任教老师的属性赋值
List<Lesson> lessons = neoService.findLessonsFromStudent("漩涡鸣人", 2);

lessons.forEach(lesson -> log.info("【lesson】= {}", JSONUtil.toJsonStr(lesson)));
}

/**
* 测试查询班级人数
*/
@Test
public void testCountStudent() {
Long all = neoService.studentCount(null);
log.info("【全校人数】= {}", all);
Long seven = neoService.studentCount("第七班");
log.info("【第七班人数】= {}", seven);
}

/**
* 测试根据课程查询同学关系
*/
@Test
public void testFindClassmates() {
Map<String, List<Student>> classmates = neoService.findClassmatesGroupByLesson();
classmates.forEach((k, v) -> log.info("因为一起上了【{}】这门课,成为同学关系的有:{}", k, JSONUtil.toJsonStr(v.stream()
.map(Student::getName)
.collect(Collectors.toList()))));
}
}

Loading…
Cancel
Save