使用 Neo4j 存取資料

本指南將引導您使用 Spring Data Neo4j 建構應用程式的流程,該應用程式可在 Neo4j(一個圖形資料庫)中儲存和檢索資料。

您將建構什麼

您將使用 Neo4j 的 NoSQL 圖形資料儲存庫來建構一個嵌入式 Neo4j 伺服器、儲存實體和關係,並開發查詢。

您需要什麼

如何完成本指南

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

若要從頭開始,請繼續前往 從 Spring Initializr 開始

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

當您完成時,您可以對照 gs-accessing-data-neo4j/complete 中的程式碼檢查您的結果。

從 Spring Initializr 開始

您可以使用這個 預先初始化的專案,然後按一下 Generate (產生) 以下載 ZIP 檔案。此專案已設定為符合本教學課程中的範例。

若要手動初始化專案

  1. 導覽至 https://start.spring.io。此服務會提取應用程式所需的所有相依性,並為您完成大部分的設定。

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

  3. 按一下 Dependencies (相依性) 並選取 Spring Data Neo4j

  4. 按一下 Generate (產生)

  5. 下載產生的 ZIP 檔案,這是一個已使用您的選擇設定的 Web 應用程式封存檔。

如果您的 IDE 具有 Spring Initializr 整合,您可以從您的 IDE 完成此程序。
您也可以從 GitHub 分叉專案,並在您的 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://127.0.0.1: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 的使用者名稱和密碼為 neo4jneo4j。但是,它要求必須變更新帳戶密碼。若要執行此操作,請執行以下命令

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 儲存它時,會建立一個新節點。此類別也有一個標記為 @GraphIdid。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://127.0.0.1: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 授權發佈程式碼,並以 姓名標示-禁止改作創用 CC 授權條款 3.0 發佈寫作內容。

取得程式碼