- 前言
- 第一部分 基础应用开发
- 第 1 章 Spring Boot 入门
- 第 2 章 在 Spring Boot 中使用数据库
- 第 3 章 Spring Boot 界面设计
- 第 4 章 提高数据库访问性能
- 第 5 章 Spring Boot 安全设计
- 第二部分 分布式应用开发
- 第 6 章 Spring Boot SSO
- 第 7 章 使用分布式文件系统
- 第 8 章 云应用开发
- 第 9 章 构建高性能的服务平台
- 第三部分 核心技术源代码分析
- 第 10 章 Spring Boot 自动配置实现原理
- 第 11 章 Spring Boot 数据访问实现原理
- 第 12 章 微服务核心技术实现原理
- 附录 A 安装 Neo4j
- 附录 B 安装 MongoDB
- 附录 C 安装 Redis
- 附录 D 安装 RabbitMQ
- 结束语
2.4 使用 Neo4j
有没有既具有传统关系型数据库的优点,又具备 NoSQL 数据库优势的一种数据库呢?Neo4j 就是一种这样的数据库。Neo4j 是一个高性能的 NoSQL 图数据库,并且具备完全事务特性。Neo4j 将结构化数据存储在一张图上,图中每一个节点的属性表示数据的内容,每一条有向边表示数据的关系。Neo4j 没有表结构的概念,它的数据用节点的属性来表示。
2.4.1 Neo4j 依赖配置
在 Spring Boot 中使用 Neo4j 非常容易,因为有 spring-data-neo4j 提供了强大的支持。首先,在工程的 Maven 管理中引入 Neo4j 的相关依赖,如代码清单 2-20 所示。
代码清单 2-20 使用 Neo4j 的 Maven 依赖配置
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-rest</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-neo4j</artifactId>
<version>4.0.0.RELEASE</version>
</dependency>
<dependency>
<groupId>com.voodoodyne.jackson.jsog</groupId>
<artifactId>jackson-jsog</artifactId>
<version>1.1</version>
<scope>compile</scope>
</dependency>
</dependencies>
2.4.2 节点和关系实体建模
虽然 Neo4j 没有表结构的概念,但它有节点和关系的概念。例如,现在有演员和电影两个实体,它们的关系表现为一个演员在一部电影中扮演一个角色。那么就可以创建演员和电影两个节点实体,和一个角色关系实体。它们的实体-关系模型如图 2-5 所示。这个实体-关系模型的定义比起关系型数据库的实体-关系模型的定义要简单得多,但是它更加形象和贴切地表现了实体之间的关系。更难能可贵的是,这个实体-关系模型是可以不经过任何转换而直接存入数据库的,也就是说,在 Neo4j 图数据库中保存的数据与图 2-5 所示的相同,它仍然是一张图。这对于业务人员和数据库设计人员来说,它的意义相同。所以使用 Neo4j 数据库,将在很大程度上减轻了设计工作和沟通成本。

图 2-5 Neo4j 实体-关系模型示例
像 JPA 使用了 ORM 一样,Neo4j 使用了对象-图形映射(Object-Graph Mapping,OGM)的方式来建模。代码清单 2-21 是演员节点实体建模,使用注解 @JsonIdentityInfo 是防止查询数据时引发递归访问效应,注解 @NodeEntity 标志这个类是一个节点实体,注解 @GraphId 定义了节点的一个唯一性标识,它将在创建节点时由系统自动生成,所以它是不可缺少的。这个节点预定义了其他两个属性,name 和 born。节点的属性可以随需要增加或减少,这并不影响节点的使用。
代码清单 2-21 演员节点实体建模
@JsonIdentityInfo(generator=JSOGGenerator.class)
@NodeEntity
public class Actor {
@GraphId Long id;
private String name;
private int born;
public Actor() { }……
代码清单 2-22 是电影节点实体建模,注解 @Relationship 表示 List<Role>是一个关系列表,其中 type 设定了关系的类型,direction 设定这个关系的方向,Relationship.INCOMING 表示以这个节点为终点。addRole 定义了增加一个关系的方法。
代码清单 2-22 电影节点实体建模
@JsonIdentityInfo(generator=JSOGGenerator.class)
@NodeEntity
public class Movie {
@GraphId Long id;
String title;
String year;
String tagline;
@Relationship(type="ACTS_IN", direction = Relationship.INCOMING)
List<Role> roles = new ArrayList<>();
public Role addRole(Actor actor, String name){
Role role = new Role(actor,this,name);
this.roles.add(role);
return role;
}
public Movie() { }……
代码清单 2-23 是角色的关系实体建模,注解 @RelationshipEntity 表明这个类是一个关系实体,并用 type 指定了关系的类型,其中 @StartNode 指定起始节点的实体,@EndNode 指定终止节点的实体,这说明了图中一条有向边的起点和终点的定义。其中定义了一个创建关系的构造函数 Role(Actor actor,Movie movie,String name),这里的 name 参数用来指定这个关系的属性。
代码清单 2-23 角色关系实体建模
@JsonIdentityInfo(generator=JSOGGenerator.class)
@RelationshipEntity(type = "ACTS_IN")
public class Role {
@GraphId
Long id;
String role;
@StartNode
Actor actor;
@EndNode
Movie movie;
public Role() {
}
public Role(Actor actor, Movie movie, String name) {
this.actor = actor;
this.movie = movie;
this.role = name;
}
......
2.4.3 节点实体持久化
像对其他数据库的访问和存取等操作一样,spring-data-neo4j 提供了功能丰富的资源库可供调用,因此,对于演员和电影节点实体,可以创建它们对应的资源库接口,实现实体的持久化。代码清单 2-24 是电影资源库接口的定义,它继承于 GraphRepository 接口,实现了电影实体的持久化。使用相同方法可以对演员的节点实体实现持久化。关系实体却不用实现持久化,当保存节点实体时,节点实体的关系将会同时保存。
代码清单 2-24 电影实体持久化
@Repository
public interface MovieRepository extends GraphRepository<Movie> {
Movie findByTitle(@Param("title") String title);
}
其中 GraphRepository 接口的继承关系也遵循了 Spring Boot 资源库定义的规则,即使用与 JPA 相同的标准规范,所以它同样包含使用数据库的丰富功能,如图 2-6 所示。

图 2-6 GraphRepository 接口继承关系
2.4.4 Neo4j 测试
代码清单 2-24 是 Neo4j 的数据库配置类,其中 @Enable-TransactionManagement 启用了事务管理,@EnableNeo4jRe-positories 启用了 Neo4j 资源库并指定了我们定义的资源库接口的位置,在重载的 SessionFactory 函数中设定了定义实体的位置,这将促使定义的实体被作为域对象导入,RemoteServer 设定连接 Neo4j 服务器的 URL、用户名和密码,这些参数要依据安装 Neo4j 服务器的情况来设置。如果还没有安装 Neo4j 服务器,可参考附录 A 的方法进行安装,安装完成后启动服务器以备使用。
代码清单 2-25 Neo4j 配置类
@Configuration
@EnableTransactionManagement
@EnableNeo4jRepositories(basePackages = { "dbdemo.neo4j.repositories" })
public class Neo4jConfig extends Neo4jConfiguration {
@Override
public Neo4jServer neo4jServer() {
return new RemoteServer("http://192.168.1.221:7474","neo4j","12345678");
}
@Override
public SessionFactory getSessionFactory() {
return new SessionFactory("dbdemo.neo4j.domain");
}
}
现在可以编写一个测试程序来验证和演示上面编写的代码的功能,如代码清单 2-26 所示。这个测试程序分别创建了三部电影和三个演员,以及三个演员在三部电影中各自扮演的角色,然后按照电影标题查出一部电影,按照其内在的关系输出这部电影的信息和每个演员扮演的角色。这些数据的内容参照了 Neo4j 帮助文档中提供的示例数据。
代码清单 2-26 使用 Neo4j 的 JUint 测试程序
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = {Neo4jConfig.class})
public class MovieTest {
private static Logger logger = LoggerFactory.getLogger(MovieTest.class);
@Autowired
MovieRepository movieRepository;
@Before
public void initData(){
movieRepository.deleteAll();
Movie matrix1 = new Movie();
matrix1.setTitle("The Matrix");
matrix1.setYear("1999-03-31");
Movie matrix2 = new Movie();
matrix2.setTitle("The Matrix Reloaded");
matrix2.setYear("2003-05-07");
Movie matrix3 = new Movie();
matrix3.setTitle("The Matrix Revolutions");
matrix3.setYear("2003-10-27");
Actor keanu = new Actor();
keanu.setName("Keanu Reeves");
Actor laurence = new Actor();
laurence.setName("Laurence Fishburne");
Actor carrieanne = new Actor();
carrieanne.setName("Carrie-Anne Moss");
matrix1.addRole(keanu, "Neo");
matrix1.addRole(laurence, "Morpheus");
matrix1.addRole(carrieanne, "Trinity");
movieRepository.save(matrix1);
Assert.notNull(matrix1.getId());
matrix2.addRole(keanu, "Neo");
matrix2.addRole(laurence, "Morpheus");
matrix2.addRole(carrieanne, "Trinity");
movieRepository.save(matrix2);
Assert.notNull(matrix2.getId());
matrix3.addRole(keanu, "Neo");
matrix3.addRole(laurence, "Morpheus");
matrix3.addRole(carrieanne, "Trinity");
movieRepository.save(matrix3);
Assert.notNull(matrix3.getId());
}
@Test
public void get(){
Movie movie = movieRepository.findByTitle("The Matrix");
Assert.notNull(movie);
logger.info("===movie=== movie:{}, {}",movie.getTitle(), movie.getYear());
for(Role role : movie.getRoles()){
logger.info("====== actor:{}, role:{}", role.getActor().getName(), role.getRole());
}
}
}
在 IDEA 的 Run/Debug Configuration 配置中增加一个 JUint 的配置项目,模块选择 neo4j,工作目录选择模块所在的根目录,测试程序选择 MovieTest 这个类,并将配置保存为 neo4jtest。
使用 Debug 模式运行测试项目 neo4jtest,如果测试通过,将在控制台中看到输出查询的这部电影和所有演员及其扮演的角色,如下所示:
=== movie=== movie:The Matrix, 1999-03-31 ====== actor:Keanu Reeves, role:Neo ====== actor:Laurence Fishburne, role:Morpheus ====== actor:Carrie-Anne Moss, role:Trinity
这时,在数据库客户端的控制台上,单击左面侧边栏的关系类型 ACTS_IN,可以看到一个很酷的图形,图中每部电影和每个演员是一个节点,节点的每条有向边代表了这个演员在那部电影中扮演的角色,如图 2-7 所示。

图 2-7 演员和电影的角色关系图
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论