$ brew install neo4j
使用 Neo4j 訪問資料
本指南將引導你完成使用 Spring Data Neo4j 構建一個應用程式的過程,該應用程式將資料儲存在基於圖的資料庫 Neo4j 中並從中檢索資料。
你將構建什麼
你將使用 Neo4j 的 NoSQL 圖資料庫來構建一個嵌入式 Neo4j 伺服器,儲存實體和關係,並開發查詢。
你需要什麼
-
約 15 分鐘
-
一個喜歡的文字編輯器或 IDE
-
Java 17 或更高版本
-
你也可以直接將程式碼匯入你的 IDE
如何完成本指南
與大多數 Spring 入門指南一樣,你可以從頭開始並完成每個步驟,或者跳過你已經熟悉的基本設定步驟。無論哪種方式,你都能得到可以工作的程式碼。
要從頭開始,請轉到使用 Spring Initializr 入門。
要跳過基礎部分,請執行以下操作
-
下載並解壓本指南的原始碼倉庫,或者使用 Git 克隆它:
git clone https://github.com/spring-guides/gs-accessing-data-neo4j.git
-
cd 進入
gs-accessing-data-neo4j/initial
-
跳轉到定義一個簡單的實體。
完成後,你可以對照 gs-accessing-data-neo4j/complete
中的程式碼檢查你的結果。
使用 Spring Initializr 入門
你可以使用這個預初始化專案,然後點選“生成”下載 ZIP 檔案。這個專案已經配置好,適合本教程中的示例。
手動初始化專案
-
導航到 https://start.spring.io。這個服務會為你拉取應用程式所需的所有依賴,併為你完成大部分設定工作。
-
選擇 Gradle 或 Maven 以及你想使用的語言。本指南假設你選擇了 Java。
-
點選依賴(Dependencies)並選擇 Spring Data Neo4j。
-
點選生成(Generate)。
-
下載生成的 ZIP 檔案,它是一個根據你的選擇配置好的 Web 應用程式存檔。
如果你的 IDE 集成了 Spring Initializr,你可以從 IDE 中完成這個過程。 |
你也可以從 GitHub fork 專案,並在你的 IDE 或其他編輯器中開啟它。 |
搭建 Neo4j 伺服器
在構建此應用程式之前,你需要設定一個 Neo4j 伺服器。
Neo4j 提供了一個開源伺服器,你可以免費安裝,或者透過 Docker 執行。
要在已安裝 Homebrew 的 Mac 上安裝伺服器,請執行以下命令
對於其他選項,請訪問 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
和 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 儲存它時,會建立一個新節點。這個類還有一個標記為 @GraphId
的 id
。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,你無需配置任何管道或基礎設施。
Spring Boot 會自動處理這些倉庫,只要它們包含在你 @SpringBootApplication
類的同一包(或子包)中。為了更好地控制註冊過程,你可以使用 @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 檔案,如下所示
如果你使用 Maven,可以透過 ./mvnw spring-boot:run
執行應用程式。或者,你可以使用 ./mvnw clean package
構建 JAR 檔案,然後執行 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 資料。 |