原文:Accessing Data with Neo4j,译者:hh23485,校对:

本指南将引导您使用 Spring Data Neo4j 来构建一个应用程序,该应用程序能够从基于图的Neo4j数据库中存储和检索数据。

您将创建什么

您将使用Neo4j的存储图数据的NoSQL来创建一个嵌入式的Neo4j服务器,用于存储实体、关系并制定查询。

开始之前您需要准备

如何完成指南?

像大多数 Spring 入门指南一样, 您可以从头开始,完成每一步, 或者您也可以绕过您熟悉的基本步骤再开始。 不管通过哪种方式,您最后都会得到一份可执行的代码。

如果从基础开始,您可以往下查看怎样使用 Gradle 构建项目

如果已经熟悉跳过一些基本步骤,您可以:

  • 下载并解压源码库,或者通过 Git克隆:

git clone https://github.com/spring-guides/gs-accessing-data-neo4j.git

当您完成之后,您可以在gs-accessing-data-neo4j/complete根据代码检查下结果。

使用Gradle构建

首先您需要编写基础构建脚本。在构建 Spring 应用的时候,您可以使用任何您喜欢的系统来构建, 这里提供一份您可能需要用 Gradle 或者 Maven 构建的代码。 如果您两者都不是很熟悉, 您可以先去参考如何使用 Gradle 构建 Java 项目或者如何使用 Maven 构建 Java 项目

创建以下目录结构

在您的项目根目录,创建如下的子目录结构; 例如,如果您使用的是*nix系统,您可以使用mkdir -p src/main/java/hello:

└── src
    └── main
        └── java
            └── hello

创建Gradle构建文件

下面是一份初始化Gradle构建文件

build.gradle

buildscript {
    repositories {
        mavenCentral()
    }
    dependencies {
        classpath("org.springframework.boot:spring-boot-gradle-plugin:1.5.9.RELEASE")
    }
}

apply plugin: 'java'
apply plugin: 'eclipse'
apply plugin: 'idea'
apply plugin: 'org.springframework.boot'

jar {
    baseName = 'gs-accessing-data-neo4j'
    version =  '0.1.0'
}

repositories {
    mavenCentral()
    maven { url "https://repo.spring.io/libs-release" }
}

sourceCompatibility = 1.8
targetCompatibility = 1.8

dependencies {
    compile("org.springframework.boot:spring-boot-starter-data-neo4j")
}

Spring Boot gradle 插件 提供了很多非常方便的功能:

  • 将 classpath 里面所有用到的 jar 包构建成一个可执行的 JAR 文件,使得运行和发布您的服务变得更加便捷。
  • 搜索 public static void main() 方法并且将它标记为可执行类。
  • 提供了将内部依赖的版本都去匹配 Spring Boot 依赖 的版本。您可以根据您的需要来重写版本,但是它默认提供给了 Spring Boot 依赖的版本。

使用Maven构建

首先,您需要设置一个基本的构建脚本。当使用 Spring 构建应用程序时,您可以使用任何您喜欢的构建系统,但是使用 Maven 构建的代码如下所示。如果您不熟悉Maven,请参阅使用Maven构建Java项目

创建目录结构

在您选择的项目目录中,创建以下子目录结构;例如, 在Linux/Unix系统中使用如下命令: mkdir -p src/main/java/hello

└── src
    └── main
        └── java
            └── hello

pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>org.springframework</groupId>
    <artifactId>gs-accessing-data-neo4j</artifactId>
    <version>0.1.0</version>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>1.5.9.RELEASE</version>
    </parent>

    <properties>
        <java.version>1.8</java.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-neo4j</artifactId>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

    <repositories>
        <repository>
            <id>spring-releases</id>
            <name>Spring Releases</name>
            <url>https://repo.spring.io/libs-release</url>
        </repository>
    </repositories>

</project>

Spring Boot Maven 插件 提供了很多便捷的特性:

  • 它收集类路径上的所有jar包,并构建一个可运行的jar包,这样可以更方便地执行和发布您的服务。
  • 它寻找public static void main() 方法来将其标记为一个可执行的类。
  • 它提供了一个内置的依赖解析器将应用与Spring Boot依赖的版本号进行匹配。您可以修改成任意的版本,但它将默认为 Boot所选择了一组版本。

使用您的IDE构建

创建一个Neo4j服务器

在您开始创建该项目之前,您需要部署一个Neo4j服务。

您可以免费安装一个Neo4j的开源服务器。

对于Mac用户,只需要输入:

$ brew install neo4j

对于其他的配置选项,可访问https://neo4j.com/download/community-edition/

在您安装后,可以通过默认的配置启动:

$ neo4j start

您将会看到如下的信息:

Starting Neo4j.
Started neo4j (pid 96416). By default, it is available at http://localhost:7474/
There may be a short delay until the server is ready.
See /usr/local/Cellar/neo4j/3.0.6/libexec/logs/neo4j.log for current status.

默认情况下,Neo4j具有一个为neo4j/neo4j的默认账户/密码。但是,它必须修改为新的账户密码。要做到这一点,请执行以下命令:

$ curl -v -u neo4j:neo4j -X POST localhost:7474/user/neo4j/password -H "Content-type:application/json" -d "{\"password\":\"secret\"}"

这将会将密码从neo4j修改为secret(请注意不要在生产环境下这样做!)在完成这些后,您可以正式开始这篇指南了。

定义一个实体

Neo4j捕获实体和它们之间的关系,其中有两个同等重要的方面。想象一下,您在搭建一个存储所有人记录的系统。而且,您还需要跟踪某个人的同事关系(在这个例子中是teammates)。在Neo4j中,您可以通过简单的注解捕捉所有的这些关系。

src/main/java/hello/Person.java

package hello;

import java.util.Collections;
import java.util.HashSet;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;

import org.neo4j.ogm.annotation.GraphId;
import org.neo4j.ogm.annotation.NodeEntity;
import org.neo4j.ogm.annotation.Relationship;

@NodeEntity
public class Person {

    @GraphId private Long id;

    private String name;

    private Person() {
        // Empty constructor required as of Neo4j API 2.0.5
    };

    public Person(String name) {
        this.name = name;
    }

    /**
     * Neo4j doesn't REALLY have bi-directional relationships. It just means when querying
     * to ignore the direction of the relationship.
     * https://dzone.com/articles/modelling-data-neo4j
     */
    @Relationship(type = "TEAMMATE", direction = Relationship.UNDIRECTED)
    public Set<Person> teammates;

    public void worksWith(Person person) {
        if (teammates == null) {
            teammates = new HashSet<>();
        }
        teammates.add(person);
    }

    public String toString() {

        return this.name + "'s teammates => "
                + Optional.ofNullable(this.teammates).orElse(
                        Collections.emptySet()).stream().map(
                                person -> person.getName()).collect(Collectors.toList());
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

在这里类 Person只包含一个属性name

在本篇指南中,典型的getter和setter方法将不再赘述。

Person被标注了注解@NodeEntity。当Neo4j存储它的时候,它会创建一个新的节点。该类还有一个属性id被注解@GraphId标注。Neo4j使用@GraphId注解在内部跟踪数据。

下一个重要的点是一组teammates的集合。它使用简单的Set<Person>来表示,但是被标注了@Relationship注解。这表示该集合所有的成员都被期望作为独立于Person的节点。注意,这里的关系方向被设置为UNDIRECTED。这表示当您查询TEAMMATE关系的时候,Spring Data Neo4j将会忽略关系的方向。

有了worksWith()方法,您可以轻松的将人们连接到一起。

最后,您可以方便的使用toString()方法来输出所有人的姓名和这个人的同事们。

创建简单的查询

Spring Data Neo4j 专注于在Neo4j中存储数据。但它继承了Spring Data Commons 项目的功能,包括了派生查询的能力。本质上来说,您不需要学习如何使用Neo4j的查询语言,您也可以简单的写出易用的查询和方法。

为了展示它是如何工作的,创建一个接口来查询Person节点。

src/main/java/hello/PersonRepository.java

package hello;

import java.util.List;

import org.springframework.data.neo4j.repository.GraphRepository;
import org.springframework.data.repository.CrudRepository;

public interface PersonRepository extends GraphRepository<Person> {

    Person findByName(String name);
}

PersonRepository继承自GraphRepository接口,并且插入了Person作为操作的类型。开箱即用,该接口生来即包括了标准的CURD(创建create-读取read-更新update-delete删除)操作。

您可以按需定义其他的查询,只需要简单的声明方法的签名即可。在这个例子中,您添加了findByName,该方法将会查询Person节点,并且找到一个name字段相匹配的元素。您也可以用findByTeammatesName来钻入每一个实体的teammates字段查找,直到找到一个匹配了同事名称为namePerson节点。

Neo4j访问权限

Neo4j社区版需要凭据来访问数据。凭据可以用如下几个属性进行配置。

spring.data.neo4j.username=neo4j
spring.data.neo4j.password=secret

这囊括了默认的用户名neo4j和一个我们早先新设置的密码secret

不要在您的源代码库中存储真实的凭证。相反,在运行时使用SpringBoot属性覆盖来进行配置。

这个时候,让我们来把他们组装起来看看。

创建一个应用程序类

我们使用所有的组件来创建一个应用程序类。

src/main/java/hello/Application.java

package hello;

import java.util.Arrays;
import java.util.List;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.data.neo4j.repository.config.EnableNeo4jRepositories;

@SpringBootApplication
@EnableNeo4jRepositories
public class Application {

    private final static Logger log = LoggerFactory.getLogger(Application.class);

    public static void main(String[] args) throws Exception {
        SpringApplication.run(Application.class, args);
    }

    @Bean
    CommandLineRunner demo(PersonRepository personRepository) {
        return args -> {

            personRepository.deleteAll();

            Person greg = new Person("Greg");
            Person roy = new Person("Roy");
            Person craig = new Person("Craig");

            List<Person> team = Arrays.asList(greg, roy, craig);

            log.info("Before linking up with Neo4j...");

            team.stream().forEach(person -> log.info("\t" + person.toString()));

            personRepository.save(greg);
            personRepository.save(roy);
            personRepository.save(craig);

            greg = personRepository.findByName(greg.getName());
            greg.worksWith(roy);
            greg.worksWith(craig);
            personRepository.save(greg);

            roy = personRepository.findByName(roy.getName());
            roy.worksWith(craig);
            // We already know that roy works with greg
            personRepository.save(roy);

            // We already know craig works with roy and greg

            log.info("Lookup each person by name...");
            team.stream().forEach(person -> log.info(
                    "\t" + personRepository.findByName(person.getName()).toString()));
        };
    }

}

@SpringBootApplication是一个简洁的注解来代替下面的所有:

  • @Configuration标记这个类为定义应用上下文来源的Bean
  • @EnableAutoConfiguration告诉SpringBoot开始根据类路径设置、其他的beans和各种属性设置来添加beans
  • 通常您会为Spring MVC 应用添加@EnableWebMvc注解,但是SpringBoot在类路径中发现spring-webmvc会自动添加该注解。这表示应用程序作为web应用程序并且激活DispatcherServlet的核心功能
  • @ComponentScan告诉Spring在hello包中查找其他的组件、配置和服务,也允许发现控制器。

main()方法中,使用Spring Boot的SpringApplication.run()方法来启动一个应用程序。您是不是注意到这里一行XML都没有?也没有web.xml文件。这个web应用程序是100%纯Java的,您并不需要为任何管道或基础平台进行配置。

Spring Boot 将会自动处理repositories,只要他们和@SpringBootAplication标注的类放在同一个包(或子包)即可。如果需要在注册的时候添加更多的控制,您可以使用@EnableNeo4jRepositories注解。

默认情况下,@EnableNeo4jRepositories将会自动扫描当前包来查找所有继承自Spring Data 的repository的接口。如果您的项目布局中有多个项目,而且不能够找到您的repositories,您可以使用它的basePackageClasses=MyRepository.class可以告诉Spring Data Neo4j去扫描一个不同的根包和类型。

日志会被输出。服务会在启动的几秒后开始运行。

您自动装配了一个您之前定义的PersonRepository的实例。Spring Data Neo4j 将会动态的实现接口并且插入所需的查询语句以满足您的查询功能。

public static void main使用SpringBoot的SpringApplication.run()方法来启动一个应用程序并调用CommandLineRunner来创建关系。

在这个例子中,您创建了3个本地的Person对象,GregRoyCraig。最开始的时候,他们只是存在于内存中。同样需要注意的是,他们现在彼此都不是彼此的同事。

首先,您找到Greg并且推断他与Roy和Craig一起工作,然后存储他。记住,由于同事关系被标注为UNDIRECTED,这意味着,同事关系是双向的。说明Roy和Craig也将会被更新。

这就是为什么当您想要更新Roy的时候,您需要先从Neo4j抓取记录。而且在您将Craig添加到Roy的同事列表之前,您需要保证他的同事列表状态是最新的。

为什么没有代码获取Craig数据和添加任何关系呢?因为您已经完成了它!Greg早先标注了Craig为一个同事,Roy也是。这意味着此时不再需要更新Craig的关系列表了。您可以通过遍历他们的队员并输出到控制台的方式来查看这些关系。

最后,检查您之前看到的查询,然后回答:“谁和谁一起工作呢?”

创建可执行的JAR

您可以在命令行使用 ZradleMaven 运行应用程序。或者您可以构建一个可执行的 JAR 文件,其中包含所有必需的依赖关系,类,和资源,并运行。这使得在整个开发生命周期中非常容易,跨不同的环境,版本等传输和部署服务成为一个应用程序,等等。

如果您使用的是 Gradle,则可以使用该应用程序 ./gradlew bootRun,或者您可以使用 JAR 文件构建 ./gradlew build,然后可以运行 JAR 文件:

java -jar build/libs/gs-accessing-data-neo4j-0.1.0.jar

如果您使用 Maven,则可以使用该命令 ./mvnw spring-boot:run,或者您可以使用 JAR 文件来构建 ./mvnw clean package,然后可以运行 JAR 文件:

java -jar target/gs-accessing-data-neo4j-0.1.0.jar

上面的过程将创建一个可运行的 JAR。您也可以选择 构建一个 WAR 文件

您应该能看到如下的的内容(以及其他查询之类的内容)

Before linking up with Neo4j...
    Greg's teammates => []
    Roy's teammates => []
    Craig's teammates => []

Lookup each person by name...
    Greg's teammates => [Roy, Craig]
    Roy's teammates => [Greg, Craig]
    Craig's teammates => [Roy, Greg]

您可以从输出中发现。最开始没有人和其他人之间有关联关系。再添加了人之后,他们连接在了一起。最终,您可以看到,基于teammate能够进行方便的查询。

总结

恭喜!您创建了一个嵌入式的Neo4j服务,存储了一些简单、存在关联的实体,并且开发了一些快捷的查询。

如果您对通过超媒体来从前端访问后端暴露的Neo4j仓库感兴趣,您可以阅读使用REST访问Neo4j

了解更多

下面的指南也非常有帮助:

想写一个新的指南或贡献一个现有的?查看我们的贡献指南

本文由spring4all.com翻译小分队创作,采用知识共享-署名-非商业性使用-相同方式共享 4.0 国际 许可 协议进行许可。

评论 抢沙发

请登录后发表评论

    暂无评论内容