?譯者 | 陳峻
審校 | 孫淑娟
在本文中,我將構建一個Java微服務,與Neo4j AuraDB的免費數據庫中的圖形數據進行連接和交互。這些數據是??Goodreads數據集??的精簡版,其中包含了各種書籍、作者和評論等信息。雖然書籍和作者類別的數據適合于MongoDB等??文檔數據庫??,但是一旦我們將評論添加到該組合之中,則需要通過AuraDB來體現各個實體之間的關系,并最大限度地簡化針對實體連接方式的查詢。
預備知識
如果您對圖形數據庫不太熟悉的話,請通過如下資源自行惡補:
- 博客文章:??《圖形數據庫能夠解決哪些問題?》???
- Neo4j指南:??《什么是圖形數據庫?》??
總的說來,Neo4j提供了多種部署選項。我們既可以啟動一個??Docker容器??(就像我們早期使用MongoDB項目那樣)或利用帶有免費層的數據庫即服務(database-as-a-service),如:??AuraDB??。
在本例中,我們將先創建自己的Neo4j數據庫,再加載數據,然后構建一個與數據庫交互、且能夠為客戶端各自服務提供API的微服務。
AuraDB
通常,您需要花費幾分鐘的時間,才能完成Neo4j AuraDB免費實例的注冊和創建,其中包括:驗證您的電子郵件地址,以及等待實例的啟動。您可以通過??《Discover AuraDB Free》??一文了解該過程的詳細信息、以及屏幕截圖。
圖形數據加載
一旦實例開始運行,我們就可以加載數據了。您可以通過??代碼存儲庫??,查看到包含了各種書籍的啟動文件、文件夾自述文件、以及加載腳本中的說明。其中,自述文件中包含了各種查詢,以方便我們去驗證數據。
值得注意的是,雖然具有較大數據集的版本也適合AuraDB的免費層實例,但為了便于實現快速加載,我們暫時選擇并保持較小的數據集。
應用服務
下面,我們需要通過構建應用,來提取評論數據。就微服務而言,我們將構建一個帶有一組REST端點的Spring Boot應用程序,以訪問連入Neo4j數據庫中的數據。
在此,我們可以使用??start.spring.i??中的Spring Initializr來整理出項目的大綱。而在表單上,??我們可以將Project、Language和Spring Boot字段保留為默認值。同時,在項目的Metadata部分下,我添加了該項目的組名。當然,您也可以保留默認值。雖然工件(artifact)名不太重要,但是我仍將其命名為neo4j-java-microservice,而所有其他字段則保持原樣。
在Dependencies部分下,我們將需要Spring Reactive Web、Lombok和??Spring Data Neo4j??。其中,Spring Data Neo4j是用于連接各種數據存儲的一個Spring Data項目。它可以幫助我們映射和訪問加載到數據庫中的數據。至此,該工程的模板已完成,我們可以點擊底部的Generate按鈕,來下載該工程(請參見下圖)。
注意:Spring Initializr會通過頁面右欄中的月亮和太陽圖標,來切換夜間和日間模式
由于項目被下載成為ZIP文件,因此我們可以將其解壓縮,并在自己喜好的IDE中打開它。
由于pom.xml文件包含了我們在Spring Initializr上設置的依賴項和軟件版本,因此我們可以輕松地定位到src/main/resources夾中的application.properties文件。在此,我們需要使用URI和數據庫憑據,來連接到Neo4j的實例上。畢竟,對于基于云端的實例而言,哪怕是對這些值采用了硬編碼,也可能會讓其他人意外地登錄進去,進而篡改我們的數據庫。因此,我們通常應當避免將數據庫的憑據直接嵌入到應用程序之中。為此,諸如數據庫憑據之類的配置值,需要在運行時(runtime),使用??Spring Cloud Config??之類的項目進行外部化與讀取。在本例中,我們可以在屬性文件中嵌入數據庫的URI、用戶名、密碼、以及數據庫的名稱。請參見如下代碼:
#database connection
spring.neo4j.uri=<insert Neo4j URI here>
spring.neo4j.authentication.username=<insert Neo4j username here>
spring.neo4j.authentication.password=<insert Neo4j password here>
spring.data.neo4j.database=<insert Neo4j database here>
注意:除非您專門使用命令去更改其默認值,否則此處數據庫默認為neo4j。
項目代碼
讓我們從整個域的類開始瀏覽各個Java文件:
@Data
@Node
class Review {
@Id
@GeneratedValue
private Long neoId;
@NonNull
private String review_id;
private String book_id, review_text, date_added, date_updated, started_at, read_at;
private Integer rating, n_comments, n_votes;
}
此處的@Data是一個??Lombok注釋??。它不但能夠為整個域的類生成我們所需要的getter、setter、equals、hashCode、以及toString方法,而且有效地減少了樣板(boilerplate)代碼的數量。而作為Spring Data Neo4j注釋的@Node,會將自己標記為Neo4j實體類(Neo4j的各個實體都稱為節點)。
在類的聲明中,我們為類定義了一些屬性字段。其中,@Id注釋是將字段標記為唯一的標識符。而@GeneratedValue可以表示該值是由Neo4j內部生成的。對于review_id字段,我們有一個Lombok@NonNull注釋,指定了該字段不能為空。同時,我們還需要檢索一些其他字段,以獲取評論文本、日期和評分信息。
接下來,我們需要一個存儲庫的接口,以便在其中定義與數據庫里的數據進行交互的方法。
interface ReviewRepository extends ReactiveCrudRepository<Review, Long> {
Flux<Review> findFirst1000By();
@Query("MATCH (r:Review)-[rel:WRITTEN_FOR]->(b:Book {book_id: $book_id}) RETURN r;")
Flux<Review> findReviewsByBook(String book_id);
}
我們希望該存儲庫能夠擴展ReactiveCrudRepository,以便使用各種響應式方法和類型,去處理數據。下面,我們來定義一組方法。雖然我們可以使用Spring Data的一些默認方法,以開箱即用的方式實現(請參見代碼示例文檔-- https://docs.spring.io/spring-data/commons/docs/current/reference/html/#repositories.core-concepts),不過,我們在此進行了自定義。畢竟,考慮到提取所有35,342條評論,可能會使得客戶端在呈現結果的過程中出現過載,因此我們并沒有使用默認的.findAll()方法,而是只提取其中的前1,000個結果。而且,我們并沒有去實現findFirst1000By()方法(例如,實現其查詢邏輯),而是使用了Spring Data的另一個特性:??派生方法??,即:利用Spring,根據方法的名稱去構造(即稱“派生”)查詢。
在本例中,由于我們的存儲庫可以使用ReactiveCrudRepository<Review, Long>來處理評論,因此findFirst1000會去尋找前1,000條評論。通常,該語法會通過by的特定標準(如:評分、評論者、以及日期等)來持續查找結果。不過,由于我們只想提取隨機的評論集合,因此我們僅通過簡單地從方法名稱中省去具體標準,來“欺騙”Spring,并獲得findFirst1000By的結果。
我們的下一個??方法??--findReviewsByBook()要簡單得多。如您所知,為了查找某本指定書目的評論,我們需要通過book_id去進行查找。在此,我們將@Query注釋與數據庫相關的查詢語句一起使用,即??Neo4j的Cypher??。
而在存儲庫完成之后,我們便可以編寫??控制器的類??,以便為其他服務設置一些用于訪問數據的REST端點。
@RestController
@RequestMapping("/neo")
@AllArgsConstructor
class ReviewController {
private final ReviewRepository reviewRepo;
@GetMapping
String liveCheck() { return "Neo4j Java Microservice is up"; }
@GetMapping("/reviews")
Flux<Review> getReviews() { return reviewRepo.findFirst1000By(); }
@GetMapping("/reviews/{book_id}")
Flux<Review> getBookReviews(@PathVariable String book_id) { return reviewRepo.findReviewsByBook(book_id); }
}
Spring注釋--@RestController會將此代碼塊指定為一個REST控制器類,而@RequestMapping則為所有類的方法都定義一個高級端點。如您所見,在類的聲明中,我們注入了??ReviewRepository??,以便使用已寫好的方法。
接下來,我們為每個方法映射相應的端點。其中,liveCheck()方法使用高級的/neo端點來返回一個字符串,以確保我們的服務是實時且可訪問的。同時,我們可以通過添加嵌套端點(/reviews),來執行getReviews()方法。該方法用到了我們在存儲庫中編寫的findFirst1000By()方法,并返回一個反應式Flux<>類型,并帶有零到多條評論結果。
我們的最終??方法??嵌套了端點/reviews/{book_id}。其中,書籍id是一個路徑變量,它會根據待搜索的書目而變化。而getBookReviews()方法會傳入指定的書的id作為路徑變量,然后從存儲庫中調用findReviewsByBook()方法,并返回一個帶有評論的Flux<>。
進行測試
下面,我們可以測試這個新服務了。首先,我需要確保Neo4j AuraDB實例仍在運行。注意:AuraDB的免費套餐通常會在三天后自動暫停。對此,您需要使用實例上的“播放”圖標予以恢復。
接下來,我們需要通過IDE或命令行,來啟動我們的neo4j-java-microservice應用。在其開始運行后,我們可以使用以下命令開展應用測試:
1. 測試應用的上線狀態:打開瀏覽器,輸入localhost:8080/neo;或是在命令行中輸入curl localhost:8080/neo。
2. 通過查找評論來測試后端的評論api:打開瀏覽器,輸入localhost:8080/neo/reviews;或是在命令行中輸入curl localhost:8080/neo/reviews。
3. 通過查找具體某本書的評論,來測試API:打開瀏覽器,輸入localhost:8080/neo/reviews/178186;或是在命令行中輸入curl localhost:8080/neo/178186。
下圖展示了該服務的評論api的輸出結果:
找到前1000條評論
按書目查找評論
小結
通過上述步驟,我們使用Neo4j AuraDB的免費層,創建了一個圖形數據庫實例,并為書籍、作者和評論加載了相關數據。接著,我們構建了一個微服務應用,以連接到云端數據庫中,并檢索相關評論。最后,我們通過啟動應用和訪問每個端點的方式,來測試我們的所有代碼,以確保數據能夠被正常地查詢到。
同時,我們在此基礎上進行了擴展。其中包括:為了保持敏感數據的機密性,并可供多個服務訪問,我們使用Spring Cloud Config將數據庫的憑證予以了外部化。當然,我們也可以將該服務添加到Docker Compose等編排工具中,以協同管理多個服務。將來,我們還可以通過從數據庫中提取更多相關實體,更加充分地利用圖形數據的優勢。
譯者介紹
陳峻 (Julian Chen),51CTO社區編輯,具有十多年的IT項目實施經驗,善于對內外部資源與風險實施管控,專注傳播網絡與信息安全知識與經驗;持續以博文、專題和譯文等形式,分享前沿技術與新知;經常以線上、線下等方式,開展信息安全類培訓與授課。
原文標題:??Build a Java Microservice With AuraDB Free???,作者:Jennifer Reif?