使用 Neo4j 訪問資料

本指南將引導您使用 Spring Data Neo4j 構建一個應用程式,該應用程式將資料儲存在基於圖的資料庫 Neo4j 中並從中檢索資料。

您將構建什麼

您將使用 Neo4j 的 NoSQL 基於圖的資料儲存來構建嵌入式 Neo4j 伺服器,儲存實體和關係,以及開發查詢。

你需要什麼

如何完成本指南

與大多數 Spring 入門指南一樣,您可以從頭開始並完成每個步驟,也可以跳過您已熟悉的基本設定步驟。無論哪種方式,您最終都會得到可工作的程式碼。

從頭開始,請轉到從 Spring Initializr 開始

跳過基礎知識,請執行以下操作

完成時,您可以將結果與 gs-accessing-data-neo4j/complete 中的程式碼進行比較。

從 Spring Initializr 開始

您可以使用這個預初始化專案,然後點選生成下載 ZIP 檔案。此專案已配置為符合本教程中的示例。

手動初始化專案

  1. 導航到 https://start.spring.io。此服務會為您拉取應用程式所需的所有依賴項,併為您完成大部分設定。

  2. 選擇 Gradle 或 Maven 以及您想要使用的語言。本指南假設您選擇了 Java。

  3. 點選依賴項並選擇Spring Data Neo4j

  4. 單擊生成

  5. 下載生成的 ZIP 檔案,這是一個已根據您的選擇配置好的 Web 應用程式存檔。

如果您的 IDE 集成了 Spring Initializr,您可以從 IDE 中完成此過程。
您還可以從 GitHub fork 專案,並在您的 IDE 或其他編輯器中開啟它。

啟動 Neo4j 伺服器

在構建此應用程式之前,您需要設定一個 Neo4j 伺服器。

Neo4j 有一個您可以免費安裝的開源伺服器,或者您可以使用 Docker 執行它。

要在安裝了 Homebrew 的 Mac 上安裝伺服器,請執行以下命令

$ brew install neo4j

有關其他選項,請訪問 https://neo4j.com/download/community-edition/

安裝後,執行以下命令以其預設設定啟動它

$ neo4j start

您應該看到類似於以下內容的輸出

Starting Neo4j.
Started neo4j (pid 96416). By default, it is available at https://: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。但是,它要求更改新帳戶密碼。為此,請執行以下命令

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

這會將密碼從 neo4j 更改為 secret — 這在生產環境中絕對不能做!完成此步驟後,您應該可以執行本指南的其餘部分了。

或者,使用 Neo4j Docker 映象執行。您可以使用 NEO4J_AUTH 環境變數更改密碼。

docker run \
  --publish=7474:7474 --publish=7687:7687 \
  --volume=$HOME/neo4j/data:/data \
  --env NEO4J_AUTH=neo4j/password
  neo4j

定義一個簡單的實體

Neo4j 捕獲實體及其關係,兩者同樣重要。想象一下您正在建模一個系統,其中儲存每個人的記錄。但是,您還想跟蹤一個人的同事(在此示例中為 teammates)。使用 Spring Data Neo4j,您可以透過一些簡單的註解捕獲所有這些,如以下列表(在 src/main/java/com/example/accessingdataneo4j/Person.java 中)所示

package com.example.accessingdataneo4j;

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

import org.springframework.data.neo4j.core.schema.Id;
import org.springframework.data.neo4j.core.schema.Node;
import org.springframework.data.neo4j.core.schema.Property;
import org.springframework.data.neo4j.core.schema.Relationship;
import org.springframework.data.neo4j.core.schema.GeneratedValue;

@Node
public class Person {

  @Id @GeneratedValue 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")
  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::getName)
            .collect(Collectors.toList());
  }

  public String getName() {
    return name;
  }

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

這裡有一個 Person 類,它只有一個屬性:name

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/com/example/accessingdataneo4j/PersonRepository.java 中)顯示了這樣的查詢

package com.example.accessingdataneo4j;

import java.util.List;
import org.springframework.data.neo4j.repository.Neo4jRepository;

public interface PersonRepository extends Neo4jRepository<Person, Long> {

  Person findByName(String name);
  List<Person> findByTeammatesName(String name);
}

PersonRepository 擴充套件了 Neo4jRepository 介面,並插入了它操作的型別:Person。此介面提供了許多操作,包括標準的 CRUD(建立、讀取、更新和刪除)操作。

但您可以透過宣告其方法簽名來定義其他查詢。在此示例中,您添加了 findByName,它查詢 Person 型別的節點並找到與 name 匹配的節點。您還有 findByTeammatesName,它查詢 Person 節點,深入到 teammates 欄位的每個條目,並根據隊友的 name 進行匹配。

訪問 Neo4j 的許可權

Neo4j Community Edition 需要憑據才能訪問。您可以透過設定幾個屬性(在 src/main/resources/application.properties 中)來配置這些憑據,如以下列表所示

spring.neo4j.uri=bolt://:7687
spring.neo4j.authentication.username=neo4j
spring.neo4j.authentication.password=secret

這包括預設使用者名稱 (neo4j) 和我們之前設定的新密碼 (secret)。

請勿在您的源儲存庫中儲存真實的憑據。相反,請使用 Spring Boot 的屬性覆蓋在您的執行時中配置它們。

有了這個,您就可以將它連線起來看看它是什麼樣子了!

建立應用程式類

Spring Initializr 為應用程式建立了一個簡單的類。以下列表顯示了 Initializr 為此示例建立的類(在 src/main/java/com/example/accessingdataneo4j/AccessingDataNeo4jApplication.java 中)

package com.example.accessingdataneo4j;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class AccessingDataNeo4jApplication {

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

}

@SpringBootApplication 是一個方便的註解,它添加了以下所有內容

  • @Configuration:將類標記為應用程式上下文的 bean 定義源。

  • @EnableAutoConfiguration:告訴 Spring Boot 根據類路徑設定、其他 bean 和各種屬性設定開始新增 bean。例如,如果 spring-webmvc 在類路徑中,此註解會將應用程式標記為 Web 應用程式並激活關鍵行為,例如設定 DispatcherServlet

  • @ComponentScan:告訴 Spring 在 com/example 包中查詢其他元件、配置和服務,使其能夠找到控制器。

main() 方法使用 Spring Boot 的 SpringApplication.run() 方法啟動應用程式。您是否注意到沒有一行 XML?也沒有 web.xml 檔案。這個 Web 應用程式是 100% 純 Java,您不必處理任何管道或基礎設施的配置。

只要這些倉庫包含在您的 @SpringBootApplication 類的同一包(或子包)中,Spring Boot 就會自動處理它們。要對註冊過程進行更多控制,您可以使用 @EnableNeo4jRepositories 註解。

預設情況下,@EnableNeo4jRepositories 掃描當前包中所有擴充套件 Spring Data 倉庫介面的介面。如果您的專案佈局有多個專案並且找不到您的倉庫,您可以使用其 basePackageClasses=MyRepository.class 透過型別安全地告訴 Spring Data Neo4j 掃描不同的根包。

日誌輸出已顯示。服務應在幾秒鐘內啟動並執行。

現在自動裝配您之前定義的 PersonRepository 例項。Spring Data Neo4j 動態實現該介面並插入所需的查詢程式碼以滿足介面的義務。

main 方法使用 Spring Boot 的 SpringApplication.run() 啟動應用程式並呼叫構建關係的 CommandLineRunner

在此示例中,您建立了三個本地 Person 例項:Greg、Roy 和 Craig。最初,它們只存在於記憶體中。請注意,目前還沒有人是任何人的隊友。

首先,您找到 Greg,指出他與 Roy 和 Craig 一起工作,然後再次持久化他。請記住,隊友關係被標記為 UNDIRECTED(即雙向)。這意味著 Roy 和 Craig 也已更新。

這就是為什麼當您需要更新 Roy 時。至關重要的是,您首先從 Neo4j 中獲取該記錄。在將 Craig 新增到列表之前,您需要 Roy 隊友的最新狀態。

為什麼沒有程式碼獲取 Craig 並新增任何關係呢?因為您已經有了!Greg 之前將 Craig 標記為隊友,Roy 也是。這意味著無需再次更新 Craig 的關係。您可以在遍歷每個團隊成員並將其資訊列印到控制檯時看到這一點。

最後,看看另一個查詢,您在其中反向查詢,回答“誰與誰一起工作?”的問題。

以下列表顯示了已完成的 AccessingDataNeo4jApplication 類(位於 src/main/java/com/example/accessingdataneo4j/AccessingDataNeo4jApplication.java

package com.example.accessingdataneo4j;

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 AccessingDataNeo4jApplication {

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

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

	@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()));

			List<Person> teammates = personRepository.findByTeammatesName(greg.getName());
			log.info("The following have Greg as a teammate...");
			teammates.stream().forEach(person -> log.info("\t" + person.getName()));
		};
	}

}

構建可執行 JAR

您可以使用 Gradle 或 Maven 從命令列執行應用程式。您還可以構建一個包含所有必要依賴項、類和資源並執行的單個可執行 JAR 檔案。構建可執行 JAR 使在整個開發生命週期中,跨不同環境等,輕鬆交付、版本化和部署服務作為應用程式。

如果您使用 Gradle,您可以透過使用 ./gradlew bootRun 執行應用程式。或者,您可以透過使用 ./gradlew build 構建 JAR 檔案,然後按如下方式執行 JAR 檔案

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

如果您使用 Maven,您可以透過使用 ./mvnw spring-boot:run 執行應用程式。或者,您可以使用 ./mvnw clean package 構建 JAR 檔案,然後按如下方式執行 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]

從輸出中可以看出,最初沒有人透過任何關係連線。然後,在新增人員之後,他們被連線在一起。最後,您可以看到方便的查詢,它根據隊友查詢人員。

總結

恭喜!您剛剛設定了一個嵌入式 Neo4j 伺服器,儲存了一些簡單的相關實體,並開發了一些快速查詢。

如果您想輕鬆地透過基於超媒體的 RESTful 前端公開 Neo4j 倉庫,請閱讀使用 REST 訪問 Neo4j 資料

另請參閱

以下指南也可能有所幫助

想寫新指南或為現有指南做貢獻嗎?請檢視我們的貢獻指南

所有指南的程式碼均採用 ASLv2 許可,文字內容採用署名-禁止演繹知識共享許可

獲取程式碼