1.說明
請通過依賴項管理包含啟動器模塊并配置要使用的 Bolt URL,例如spring.neo4j.uri=bolt://localhost:7687
。啟動器假設(shè)服務(wù)器已禁用身份驗證。由于 SDN 啟動器依賴于 Java 驅(qū)動程序的啟動器,因此此處所說的有關(guān)配置的所有內(nèi)容也適用于此處。有關(guān)可用屬性的參考,請在spring.neo4j
命名空間中使用 IDE 自動完成功能。
SDN支持
-
眾所周知且易于理解的命令式編程模型(很像 Spring Data JDBC 或 JPA)
-
基于Reactive Streams 的反應(yīng)式編程,包括對反應(yīng)式事務(wù)的全面支持。
這些都包含在同一個二進制文件中。響應(yīng)式編程模型在數(shù)據(jù)庫端需要 4+ Neo4j 服務(wù)器,而在另一方面則需要響應(yīng)式 Spring。
2.引入依賴
引入maven依賴
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-neo4j</artifactId>
</dependency>
3.安裝數(shù)據(jù)庫(如果已經(jīng)安裝跳過此步驟)
如果您沒有正在運行的數(shù)據(jù)庫,但安裝了 Docker,請運行:
先下載對應(yīng)版本的docker 下述命令替換對應(yīng)版本號
docker run --publish=7474:7474 --publish=7687:7687 -e 'NEO4J_AUTH=neo4j/secret' neo4j:4.3.6
您現(xiàn)在可以訪問http://localhost:7474。上述命令將服務(wù)器的密碼設(shè)置為secret
。請注意提示符 ( ) 中準備運行的命令:play movies
。執(zhí)行它以用一些測試數(shù)據(jù)填充您的數(shù)據(jù)庫。
4.配置文件
spring.neo4j.uri=bolt://localhost:7687
spring.neo4j.authentication.username=neo4j
spring.neo4j.authentication.password=secret
5.示例節(jié)點實體
import java.util.ArrayList;
import java.util.List;
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.Relationship.Direction;
@Node("Movie") (1)
public class MovieEntity {
@Id (2)
private final String title;
@Property("tagline") (3)
private final String description;
@Relationship(type = "ACTED_IN", direction = Direction.INCOMING) (4)
private List<Roles> actorsAndRoles;
@Relationship(type = "DIRECTED", direction = Direction.INCOMING)
private List<PersonEntity> directors = new ArrayList<>();
public MovieEntity(String title, String description) { (5)
this.title = title;
this.description = description;
}
// Getters omitted for brevity
}
? 1.@Node用于將此類標記為托管實體。它還用于配置 Neo4j 標簽。如果您只是使用普通的,標簽?zāi)J為類的名稱@Node。
2.每個實體都必須有一個 id。此處顯示的電影類使用該屬性title作為唯一的業(yè)務(wù)鍵。如果您沒有這樣的唯一密鑰,您可以使用@Id和的組合@GeneratedValue 來配置 SDN 以使用 Neo4j 的內(nèi)部 id。我們還提供 UUID 生成器。
3.顯示@Property為字段使用與圖形屬性不同的名稱的一種方法。
4.定義了與類型類PersonEntity和關(guān)系類型的關(guān)系A(chǔ)CTED_IN
5.這是您的應(yīng)用程序代碼要使用的構(gòu)造函數(shù)。
一般而言:使用內(nèi)部生成的 id 的不可變實體有點矛盾,因為 SDN 需要一種方法來使用數(shù)據(jù)庫生成的值來設(shè)置字段。
如果您沒有找到好的業(yè)務(wù)密鑰或者不想使用 ID 生成器,這里是使用內(nèi)部生成的 id 以及常規(guī)構(gòu)造函數(shù)和SDN 使用的所謂的wither方法的同一實體:
import org.springframework.data.neo4j.core.schema.GeneratedValue;
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.annotation.PersistenceConstructor;
@Node("Movie")
public class MovieEntity {
@Id @GeneratedValue
private Long id;
private final String title;
@Property("tagline")
private final String description;
public MovieEntity(String title, String description) { (1)
this.id = null;
this.title = title;
this.description = description;
}
public MovieEntity withId(Long id) { (2)
if (this.id.equals(id)) {
return this;
} else {
MovieEntity newObject = new MovieEntity(this.title, this.description);
newObject.id = id;
return newObject;
}
}
}
1.這是您的應(yīng)用程序代碼要使用的構(gòu)造函數(shù)。它將 id 設(shè)置為 null,因為包含內(nèi)部 id 的字段永遠不應(yīng)該被操作。
2.創(chuàng)建一個新實體并相應(yīng)地設(shè)置字段,而不修改原始實體,從而使其不可變
import reactor.core.publisher.Mono;
import org.springframework.data.neo4j.repository.ReactiveNeo4jRepository;
public interface MovieRepository extends ReactiveNeo4jRepository<MovieEntity, String> {
Mono<MovieEntity> findOneByTitle(String title);
}
這里的接口類似mybatis-plus,繼承后有一下公共的方法可以使用
6.注解說明
-
@Node
:在類級別應(yīng)用以指示該類是映射到數(shù)據(jù)庫的候選類。 -
@Id
:應(yīng)用于字段級別,標記用于身份目的的字段。 -
@GeneratedValue
:在字段級別應(yīng)用,并@Id
指定如何生成唯一標識符。 -
@Property
:應(yīng)用于字段級別以修改從屬性到屬性的映射。 -
@CompositeProperty
:在字段級別應(yīng)用于應(yīng)作為組合讀回的 Map 類型的屬性。請參閱復(fù)合屬性。 -
@Relationship
:應(yīng)用于字段級別以指定關(guān)系的詳細信息。 -
@DynamicLabels
:應(yīng)用于字段級別,指定動態(tài)標簽的來源。 -
@RelationshipProperties
:應(yīng)用于類級別以指示此類作為關(guān)系屬性的目標。 -
@TargetNode
:應(yīng)用于用 注釋的類的字段,@RelationshipProperties
從另一端的角度標記該關(guān)系的目標。
以下注釋用于指定轉(zhuǎn)換并確保與 OGM 的向后兼容性。
-
@DateLong
-
@DateString
-
@ConvertWith
來自 Spring Data commons
-
@org.springframework.data.annotation.Id
與 SDN 中的相同@Id
,實際上@Id
是使用 Spring Data Common 的 Id-annotation 進行注釋。 -
@CreatedBy
:應(yīng)用于字段級別,指示節(jié)點的創(chuàng)建者。 -
@CreatedDate
:應(yīng)用于字段級別,指示節(jié)點的創(chuàng)建日期。 -
@LastModifiedBy
:應(yīng)用于字段級別,指示節(jié)點最后更改的作者。 -
@LastModifiedDate
:應(yīng)用于字段級別,指示節(jié)點的最后修改日期。 -
@PersistenceCreator
:應(yīng)用于一個構(gòu)造函數(shù)以將其標記為讀取實體時的首選構(gòu)造函數(shù)。 -
@Persistent
:在類級別應(yīng)用以指示該類是映射到數(shù)據(jù)庫的候選類。 -
@Version
:應(yīng)用于字段級別,用于樂觀鎖定并檢查保存操作的修改。初始值為零,每次更新時都會自動增加。 -
@ReadOnlyProperty
:應(yīng)用于字段級別,將屬性標記為只讀。該屬性將在數(shù)據(jù)庫讀取期間被水化,但不會受到寫入的影響。當用于關(guān)系時,請注意,如果不相關(guān),則該集合中的任何相關(guān)實體都不會被持久化。
?7.基本構(gòu)建塊:@Node
注釋@Node
用于將類標記為托管域類,并接受映射上下文的類路徑掃描。
要將對象映射到圖中的節(jié)點(反之亦然),我們需要一個標簽來標識要映射到和映射的類。
@Node
有一個屬性labels
,允許您配置在讀取和寫入帶注釋的類的實例時使用的一個或多個標簽。該value
屬性是 的別名labels
。如果您不指定標簽,則簡單類名稱將用作主標簽。如果您想提供多個標簽,您可以:
-
向?qū)傩蕴峁┮粋€數(shù)組
labels
。數(shù)組中的第一個元素將被視為主標簽。 -
提供一個值
primaryLabel
并將附加標簽放入其中labels
。
主標簽應(yīng)該始終是反映您的域類的最具體的標簽。
對于通過存儲庫或 Neo4j 模板編寫的帶注釋類的每個實例,將寫入圖中至少具有主標簽的一個節(jié)點。反之亦然,所有具有主標簽的節(jié)點都將映射到帶注釋的類的實例。
8.關(guān)于類層次結(jié)構(gòu)的注釋
該@Node
注釋不是從超類型和接口繼承的。然而,您可以在每個繼承級別單獨注釋您的域類。這允許多態(tài)查詢:您可以傳入基類或中間類并檢索節(jié)點的正確的具體實例。僅支持用 注釋的抽象基@Node
。在此類上定義的標簽將與具體實現(xiàn)的標簽一起用作附加標簽。
對于某些場景,還支持域類層次結(jié)構(gòu)中的接口:
public interface SomeInterface { (1)
String getName();
SomeInterface getRelated();
}
@Node("SomeInterface") (2)
public static class SomeInterfaceEntity implements SomeInterface {
@Id @GeneratedValue private Long id;
private final String name;
private SomeInterface related;
public SomeInterfaceEntity(String name) {
this.name = name;
}
@Override
public String getName() {
return name;
}
@Override
public SomeInterface getRelated() {
return related;
}
}
1.只是簡單的接口名稱,就像您命名您的域一樣
2.由于我們需要同步主標簽,因此我們放置@Node了實現(xiàn)類,該類可能位于另一個模塊中。請注意,該值與實現(xiàn)的接口名稱完全相同。重命名是不可能的。
也可以使用不同的主標簽代替接口名稱:
@Node("PrimaryLabelWN") (1)
public interface SomeInterface2 {
String getName();
SomeInterface2 getRelated();
}
public static class SomeInterfaceEntity2 implements SomeInterface2 {
// Overrides omitted for brevity
}
1.將@Node
注解放在接口上
還可以使用接口的不同實現(xiàn)并擁有多態(tài)域模型。這樣做時,至少需要兩個標簽:一個確定接口的標簽,一個確定具體類的標簽:
@Node("SomeInterface3") (1)
public interface SomeInterface3 {
String getName();
SomeInterface3 getRelated();
}
@Node("SomeInterface3a") (2)
public static class SomeInterfaceImpl3a implements SomeInterface3 {
// Overrides omitted for brevity
}
@Node("SomeInterface3b") (3)
public static class SomeInterfaceImpl3b implements SomeInterface3 {
// Overrides omitted for brevity
}
@Node
public static class ParentModel { (4)
@Id
@GeneratedValue
private Long id;
private SomeInterface3 related1; (5)
private SomeInterface3 related2;
}
1.在這種情況下需要顯式指定標識接口的標簽
2.這適用于第一個……
3.以及第二次實施
4.這是一個客戶端或父模型,SomeInterface3透明地用于兩個關(guān)系
5.沒有指定具體類型
所需的數(shù)據(jù)結(jié)構(gòu)如下面的測試所示。OGM 也會寫同樣的內(nèi)容:
Long id;
try (Session session = driver.session(bookmarkCapture.createSessionConfig()); Transaction transaction = session.beginTransaction()) {
id = transaction.run("" +
"CREATE (s:ParentModel{name:'s'}) " +
"CREATE (s)-[:RELATED_1]-> (:SomeInterface3:SomeInterface3b {name:'3b'}) " +
"CREATE (s)-[:RELATED_2]-> (:SomeInterface3:SomeInterface3a {name:'3a'}) " +
"RETURN id(s)")
.single().get(0).asLong();
transaction.commit();
}
Optional<Inheritance.ParentModel> optionalParentModel = transactionTemplate.execute(tx ->
template.findById(id, Inheritance.ParentModel.class));
assertThat(optionalParentModel).hasValueSatisfying(v -> {
assertThat(v.getName()).isEqualTo("s");
assertThat(v).extracting(Inheritance.ParentModel::getRelated1)
.isInstanceOf(Inheritance.SomeInterfaceImpl3b.class)
.extracting(Inheritance.SomeInterface3::getName)
.isEqualTo("3b");
assertThat(v).extracting(Inheritance.ParentModel::getRelated2)
.isInstanceOf(Inheritance.SomeInterfaceImpl3a.class)
.extracting(Inheritance.SomeInterface3::getName)
.isEqualTo("3a");
});
9.動態(tài)或“運行時”管理標簽
通過簡單類名隱式定義或通過@Node
注釋顯式定義的所有標簽都是靜態(tài)的。它們在運行時無法更改。如果您需要可以在運行時操作的其他標簽,您可以使用@DynamicLabels
.?@DynamicLabels
是字段級別的注釋,并將類型java.util.Collection<String>
(例如 aList
或Set
)的屬性標記為動態(tài)標簽的源。
如果存在此注釋,則節(jié)點上存在且未靜態(tài)映射的所有標簽@Node
和類名稱將在加載期間收集到該集合中。在寫入期間,節(jié)點的所有標簽將被替換為靜態(tài)定義的標簽加上集合的內(nèi)容。
@DynamicLabels
private Set<String> labels;
10.識別實例:@Id
@Node
在創(chuàng)建類和具有特定標簽的節(jié)點之間的映射的同時,我們還需要在該類的各個實例(對象)和節(jié)點的實例之間建立連接。
這就是@Id
發(fā)揮作用的地方。?@Id
將類的屬性標記為對象的唯一標識符。在最佳情況下,該唯一標識符是唯一的業(yè)務(wù)密鑰,或者換句話說,是自然密鑰。?@Id
可用于具有受支持的簡單類型的所有屬性。
然而自然鍵很難找到。例如,人們的名字很少是唯一的,會隨著時間的推移而改變,或更糟糕的是,不是每個人都有名字和姓氏。
因此,我們支持兩種不同類型的代理鍵。
long
對于或類型的屬性Long
,@Id
可以與 一起使用@GeneratedValue
。這會將 Neo4j 內(nèi)部 id(不是節(jié)點或關(guān)系上的屬性,通常不可見)映射到屬性,并允許 SDN 檢索該類的各個實例。
@GeneratedValue
提供屬性generatorClass
.?generatorClass
可用于指定實現(xiàn)IdGenerator
.?AnIdGenerator
是一個功能接口,它generateId
采用主標簽和實例來生成 Id。我們支持UUIDStringGenerator
作為一種開箱即用的實施方式。
@GeneratedValue
您還可以從via上的應(yīng)用程序上下文中指定 Spring Bean?generatorRef
。該 bean 還需要實現(xiàn)IdGenerator
,但可以利用上下文中的所有內(nèi)容,包括 Neo4j 客戶端或模板來與數(shù)據(jù)庫交互。
11.樂觀鎖:@Version
Spring Data Neo4j 通過@Version
在Long
類型化字段上使用注釋來支持樂觀鎖定。該屬性將在更新期間自動遞增,并且不得手動修改。
例如,如果不同線程中的兩個事務(wù)想要修改版本相同的對象x
,則第一個操作將成功持久化到數(shù)據(jù)庫。此時,版本字段會增加,因此是x+1
。第二個操作將失敗并顯示 a,因為它想要修改?數(shù)據(jù)庫中不再存在的OptimisticLockingFailureException
版本的對象。x
在這種情況下,需要重試該操作,首先從數(shù)據(jù)庫中重新獲取當前版本的對象。
如果使用業(yè)務(wù) ID?,則該@Version
屬性也是必需的。Spring Data Neo4j 將檢查此字段以確定該實體是新的還是之前已經(jīng)被持久化。
12.映射屬性:@Property
帶注釋的類的所有屬性都@Node
將作為 Neo4j 節(jié)點和關(guān)系的屬性保留。如果沒有進一步配置,Java 或 Kotlin 類中的屬性名稱將用作 Neo4j 屬性。
如果您正在使用現(xiàn)有的 Neo4j 架構(gòu)或只是想根據(jù)您的需求調(diào)整映射,則需要使用@Property
.?用于name
指定數(shù)據(jù)庫內(nèi)屬性的名稱。
13.連接節(jié)點:@Relationship
該@Relationship
注釋可用于所有非簡單類型的屬性。它適用于用其注釋的其他類型的屬性@Node
或其集合和映射。
或type
屬性value
允許配置關(guān)系的類型,direction
允許指定方向。SDN 中默認的方向是Relationship.Direction#OUTGOING
。
我們支持動態(tài)關(guān)系。動態(tài)關(guān)系表示為Map<String, AnnotatedDomainClass>
或Map<Enum, AnnotatedDomainClass>
。在這種情況下,與其他域類的關(guān)系類型由映射鍵給出,并且不能通過@Relationship
.
14.映射關(guān)系屬性
Neo4j 不僅支持在節(jié)點上定義屬性,還支持在關(guān)系上定義屬性。為了在模型中表達這些屬性,SDN 提供了@RelationshipProperties
應(yīng)用于簡單的 Java 類的方法。在屬性類中,必須有一個字段被標記為@TargetNode
定義關(guān)系所指向的實體?;蛘撸?code>INCOMING關(guān)系上下文中,是來自。
關(guān)系屬性類及其用法可能如下所示:
@RelationshipProperties
public class Roles {
@RelationshipId
private Long id;
private final List<String> roles;
@TargetNode
private final PersonEntity person;
public Roles(PersonEntity person, List<String> roles) {
this.person = person;
this.roles = roles;
}
public List<String> getRoles() {
return roles;
}
}
您必須為生成的內(nèi)部 ID ( ) 定義屬性@RelationshipId
,以便 SDN 可以在保存期間確定可以安全覆蓋哪些關(guān)系而不丟失屬性。如果SDN沒有找到存儲內(nèi)部節(jié)點id的字段,它將在啟動過程中失敗。
@Relationship(type = "ACTED_IN", direction = Direction.INCOMING)
private List<Roles> actorsAndRoles;
一般來說,創(chuàng)建查詢的關(guān)系/躍點沒有限制。SDN 解析來自建模節(jié)點的整個可達圖。
這就是說,當存在雙向映射關(guān)系的想法時,這意味著您在實體的兩端定義關(guān)系,您可能會得到比您期望的更多的東西。
考慮一個例子,其中電影有演員,并且您想要獲取某部電影及其所有演員。如果電影和演員之間的關(guān)系只是單向的,那么這不會有問題。在雙向場景中,SDN 將獲取特定的電影、其演員,以及根據(jù)關(guān)系定義為此演員定義的其他電影。在最壞的情況下,這將級聯(lián)以獲取單個實體的整個圖。
15.一個完整的例子
將所有這些放在一起,我們可以創(chuàng)建一個簡單的域。我們使用電影和扮演不同角色的人:
import java.util.ArrayList;
import java.util.List;
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.Relationship.Direction;
@Node("Movie") (1)
public class MovieEntity {
@Id (2)
private final String title;
@Property("tagline") (3)
private final String description;
@Relationship(type = "ACTED_IN", direction = Direction.INCOMING) (4)
private List<Roles> actorsAndRoles;
@Relationship(type = "DIRECTED", direction = Direction.INCOMING)
private List<PersonEntity> directors = new ArrayList<>();
public MovieEntity(String title, String description) { (5)
this.title = title;
this.description = description;
}
// Getters omitted for brevity
}
1.@Node用于將此類標記為托管實體。它還用于配置 Neo4j 標簽。如果您只是使用普通的,標簽?zāi)J為類的名稱@Node。
2.每個實體都必須有一個 id。我們使用電影的名稱作為唯一標識符。
3.這顯示@Property為字段使用與圖形屬性不同的名稱的一種方法。
4.這配置了與某人的傳入關(guān)系。
5.這是您的應(yīng)用程序代碼以及 SDN 使用的構(gòu)造函數(shù)。
人在這里被映射為兩個角色,actors
和directors
。域類是相同的:
import org.springframework.data.neo4j.core.schema.Id;
import org.springframework.data.neo4j.core.schema.Node;
@Node("Person")
public class PersonEntity {
@Id private final String name;
private final Integer born;
public PersonEntity(Integer born, String name) {
this.born = born;
this.name = name;
}
public Integer getBorn() {
return born;
}
public String getName() {
return name;
}
}
16.使用外部提供的代理鍵
注釋@GeneratedValue
可以將實現(xiàn)的類org.springframework.data.neo4j.core.schema.IdGenerator
作為參數(shù)。SDN 提供InternalIdGenerator
(默認)并且UUIDStringGenerator
開箱即用。后者為每個實體生成新的 UUID 并將其返回為java.lang.String
.?使用它的應(yīng)用程序?qū)嶓w將如下所示:
@Node("Movie")
public class MovieEntity {
@Id @GeneratedValue(UUIDStringGenerator.class)
private String id;
private String name;
}
17.沒有 Spring Boot
我們在自己的集成測試中大量使用抽象基類進行配置。它們可以這樣使用:
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.neo4j.driver.AuthTokens;
import org.neo4j.driver.Driver;
import org.neo4j.driver.GraphDatabase;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.neo4j.config.AbstractNeo4jConfig;
import org.springframework.data.neo4j.core.Neo4jTemplate;
import org.springframework.data.neo4j.repository.config.EnableNeo4jRepositories;
import org.springframework.test.context.junit.jupiter.SpringExtension;
import org.springframework.transaction.annotation.EnableTransactionManagement;
@ExtendWith(SpringExtension.class)
class YourIntegrationTest {
@Test
void thingsShouldWork(@Autowired Neo4jTemplate neo4jTemplate) {
// Add your test
}
@Configuration
@EnableNeo4jRepositories(considerNestedRepositories = true)
@EnableTransactionManagement
static class Config extends AbstractNeo4jConfig {
@Bean
public Driver driver() {
return GraphDatabase.driver("bolt://yourtestserver:7687", AuthTokens.none());
}
}
}
18.使用 Spring Boot 和@DataNeo4jTest
Spring Boot 提供@DataNeo4jTest
通過org.springframework.boot:spring-boot-starter-test
.?后者引入其中org.springframework.boot:spring-boot-test-autoconfigure
包含注釋和所需的基礎(chǔ)設(shè)施代碼。
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
@DataNeo4jTest
是一個Spring Boot測試片。測試切片為使用 Neo4j 進行測試提供了所有必要的基礎(chǔ)設(shè)施:事務(wù)管理器、客戶端、模板和聲明的存儲庫(其命令式或反應(yīng)式變體,具體取決于是否存在反應(yīng)式依賴項)。測試片已包含在內(nèi)@ExtendWith(SpringExtension.class)
,因此它可以與 JUnit 5 (JUnit Jupiter) 自動運行。
@DataNeo4jTest
默認情況下提供命令式和反應(yīng)式基礎(chǔ)設(shè)施,并且還添加了隱式基礎(chǔ)設(shè)施@Transactional
。?@Transactional
然而,在 Spring 測試中,始終意味著命令式事務(wù),因為聲明式事務(wù)需要方法的返回類型來決定是否需要命令式事務(wù)PlatformTransactionManager
或反應(yīng)式事務(wù)。ReactiveTransactionManager
要斷言反應(yīng)式存儲庫或服務(wù)的正確事務(wù)行為,您需要將 注入TransactionalOperator
?到測試中或?qū)⒂蜻壿嫲b在使用帶注釋的方法的服務(wù)中,該方法公開返回類型,使基礎(chǔ)設(shè)施可以選擇正確的事務(wù)管理器。
測試切片不會引入嵌入式數(shù)據(jù)庫或任何其他連接設(shè)置。您可以自行決定是否使用適當?shù)倪B接。
我們推薦以下兩個選項之一:使用Neo4j Testcontainers 模塊?或 Neo4j 測試工具。雖然 Testcontainers 是一個眾所周知的項目,擁有許多不同服務(wù)的模塊,但 Neo4j 測試工具卻相當陌生。它是一個嵌入式實例,在測試存儲過程時特別有用,如測試基于 Neo4j 的 Java 應(yīng)用程序中所述。然而,測試工具也可用于測試應(yīng)用程序。由于它在與您的應(yīng)用程序相同的 JVM 內(nèi)啟動數(shù)據(jù)庫,因此性能和計時可能與您的生產(chǎn)設(shè)置不同。
為了您的方便,我們提供了三種可能的場景:Neo4j 測試工具 3.5 和 4.0 以及 Testcontainers Neo4j。我們?yōu)?3.5 和 4.0 提供了不同的示例,因為測試工具在這些版本之間發(fā)生了變化。此外,4.0 需要 JDK 11。
19.?@DataNeo4jTest使用 Neo4j 測試工具 3.5
<dependency>
<groupId>org.neo4j.test</groupId>
<artifactId>neo4j-harness</artifactId>
<version>3.5.23</version>
<scope>test</scope>
</dependency>
import static org.assertj.core.api.Assertions.assertThat;
import java.util.Optional;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import org.neo4j.harness.ServerControls;
import org.neo4j.harness.TestServerBuilders;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.data.neo4j.DataNeo4jTest;
import org.springframework.data.neo4j.core.Neo4jClient;
import org.springframework.test.context.DynamicPropertyRegistry;
import org.springframework.test.context.DynamicPropertySource;
@DataNeo4jTest
class MovieRepositoryTest {
private static ServerControls embeddedDatabaseServer;
@BeforeAll
static void initializeNeo4j() {
embeddedDatabaseServer = TestServerBuilders.newInProcessBuilder() (1)
.newServer();
}
@AfterAll
static void stopNeo4j() {
embeddedDatabaseServer.close(); (2)
}
@DynamicPropertySource (3)
static void neo4jProperties(DynamicPropertyRegistry registry) {
registry.add("spring.neo4j.uri", embeddedDatabaseServer::boltURI);
registry.add("spring.neo4j.authentication.username", () -> "neo4j");
registry.add("spring.neo4j.authentication.password", () -> null);
}
@Test
public void findSomethingShouldWork(@Autowired Neo4jClient client) {
Optional<Long> result = client.query("MATCH (n) RETURN COUNT(n)")
.fetchAs(Long.class)
.one();
assertThat(result).hasValue(0L);
}
}
1.創(chuàng)建嵌入式 Neo4j 的入口點
2.這是一個 Spring Boot 注釋,允許動態(tài)注冊應(yīng)用程序?qū)傩?。我們覆蓋相應(yīng)的 Neo4j 設(shè)置。
3.所有測試后關(guān)閉 Neo4j。
20.@DataNeo4jTest使用 Neo4j 測試工具 4.x
<dependency>
<groupId>org.neo4j.test</groupId>
<artifactId>neo4j-harness</artifactId>
<version>{neo4j-version}</version>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-nop</artifactId>
</exclusion>
</exclusions>
</dependency>
Neo4j 4.x 企業(yè)版的依賴項可在com.neo4j.test:neo4j-harness-enterprise
適當?shù)拇鎯炫渲孟芦@得。文章來源:http://www.zghlxwxcb.cn/news/detail-808575.html
import static org.assertj.core.api.Assertions.assertThat;
import java.util.Optional;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import org.neo4j.harness.Neo4j;
import org.neo4j.harness.Neo4jBuilders;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.data.neo4j.DataNeo4jTest;
import org.springframework.data.neo4j.core.Neo4jClient;
import org.springframework.test.context.DynamicPropertyRegistry;
import org.springframework.test.context.DynamicPropertySource;
@DataNeo4jTest
class MovieRepositoryTest {
private static Neo4j embeddedDatabaseServer;
@BeforeAll
static void initializeNeo4j() {
embeddedDatabaseServer = Neo4jBuilders.newInProcessBuilder() (1)
.withDisabledServer() (2)
.build();
}
@DynamicPropertySource (3)
static void neo4jProperties(DynamicPropertyRegistry registry) {
registry.add("spring.neo4j.uri", embeddedDatabaseServer::boltURI);
registry.add("spring.neo4j.authentication.username", () -> "neo4j");
registry.add("spring.neo4j.authentication.password", () -> null);
}
@AfterAll
static void stopNeo4j() {
embeddedDatabaseServer.close(); (4)
}
@Test
public void findSomethingShouldWork(@Autowired Neo4jClient client) {
Optional<Long> result = client.query("MATCH (n) RETURN COUNT(n)")
.fetchAs(Long.class)
.one();
assertThat(result).hasValue(0L);
}
}
1.創(chuàng)建嵌入式 Neo4j 的入口點
2.禁用不需要的 Neo4j HTTP 服務(wù)器
3.這是一個 Spring Boot 注釋,允許動態(tài)注冊應(yīng)用程序?qū)傩?。我們覆蓋相應(yīng)的 Neo4j 設(shè)置。
4.所有測試后關(guān)閉 Neo4j。文章來源地址http://www.zghlxwxcb.cn/news/detail-808575.html
到了這里,關(guān)于springboot+neo4j的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!