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