springboot+neo4j

1.说明

请通过依赖项管理包含启动器模块并配置要使用的 Bolt URL,例如spring.neo4j.uri=bolt://localhost:7687。启动器假设服务器已禁用身份验证。由于 SDN 启动器依赖于 Java 驱动程序的启动器,因此此处所说的有关配置的所有内容也适用于此处。有关可用属性的参考,请在spring.neo4j命名空间中使用 IDE 自动完成功能。

SDN支持

这些都包含在同一个二进制文件中。响应式编程模型在数据库端需要 4+ Neo4j 服务器,而在另一方面则需要响应式 Spring。

2.引入依赖

引入maven依赖

<dependency>
   <groupId>org.springframework.boot</groupId>
   <artifactId>spring-boot-starter-data-neo4j</artifactId>
 </dependency>

3.安装数据库(如果已经安装跳过此步骤)

如果您没有正在运行的数据库,但安装了 Docker,请运行:

先下载对应版本的docker 下述命令替换对应版本号

docker run --publish=7474:7474 --publish=7687:7687 -e 'NEO4J_AUTH=neo4j/secret' neo4j:4.3.6

您现在可以访问http://localhost:7474。上述命令将服务器的密码设置为secret。请注意提示符 ( ) 中准备运行的命令:play movies。执行它以用一些测试数据填充您的数据库。

4.配置文件

spring.neo4j.uri=bolt://localhost:7687
spring.neo4j.authentication.username=neo4j
spring.neo4j.authentication.password=secret

5.示例节点实体

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 标签。如果您只是使用普通的,标签默认为类的名称@Node。
2.每个实体都必须有一个 id。此处显示的电影类使用该属性title作为唯一的业务键。如果您没有这样的唯一密钥,您可以使用@Id和的组合@GeneratedValue 来配置 SDN 以使用 Neo4j 的内部 id。我们还提供 UUID 生成器。
3.显示@Property为字段使用与图形属性不同的名称的一种方法。
4.定义了与类型类PersonEntity和关系类型的关系ACTED_IN
5.这是您的应用程序代码要使用的构造函数。
一般而言:使用内部生成的 id 的不可变实体有点矛盾,因为 SDN 需要一种方法来使用数据库生成的值来设置字段。

如果您没有找到好的业务密钥或者不想使用 ID 生成器,这里是使用内部生成的 id 以及常规构造函数和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.这是您的应用程序代码要使用的构造函数。它将 id 设置为 null,因为包含内部 id 的字段永远不应该被操作。

2.创建一个新实体并相应地设置字段,而不修改原始实体,从而使其不可变

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:在类级别应用以指示该类是映射到数据库的候选类。

  • @Id:应用于字段级别,标记用于身份目的的字段。

  • @GeneratedValue:在字段级别应用,并@Id指定如何生成唯一标识符。

  • @Property:应用于字段级别以修改从属性到属性的映射。

  • @CompositeProperty:在字段级别应用于应作为组合读回的 Map 类型的属性。请参阅复合属性

  • @Relationship:应用于字段级别以指定关系的详细信息。

  • @DynamicLabels:应用于字段级别,指定动态标签的来源。

  • @RelationshipProperties:应用于类级别以指示此类作为关系属性的目标。

  • @TargetNode:应用于用 注释的类的字段,@RelationshipProperties从另一端的角度标记该关系的目标。

以下注释用于指定转换并确保与 OGM 的向后兼容性。

  • @DateLong

  • @DateString

  • @ConvertWith

来自 Spring Data commons

  • @org.springframework.data.annotation.Id与 SDN 中的相同@Id,实际上@Id是使用 Spring Data Common 的 Id-annotation 进行注释。

  • @CreatedBy:应用于字段级别,指示节点的创建者。

  • @CreatedDate:应用于字段级别,指示节点的创建日期。

  • @LastModifiedBy:应用于字段级别,指示节点最后更改的作者。

  • @LastModifiedDate:应用于字段级别,指示节点的最后修改日期。

  • @PersistenceCreator:应用于一个构造函数以将其标记为读取实体时的首选构造函数。

  • @Persistent:在类级别应用以指示该类是映射到数据库的候选类。

  • @Version:应用于字段级别,用于乐观锁定并检查保存操作的修改。初始值为零,每次更新时都会自动增加。

  • @ReadOnlyProperty:应用于字段级别,将属性标记为只读。该属性将在数据库读取期间被水化,但不会受到写入的影响。当用于关系时,请注意,如果不相关,则该集合中的任何相关实体都不会被持久化。

 7.基本构建块:@Node

注释@Node用于将类标记为托管域类,并接受映射上下文的类路径扫描。

要将对象映射到图中的节点(反之亦然),我们需要一个标签来标识要映射到和映射的类。

@Node有一个属性labels,允许您配置在读取和写入带注释的类的实例时使用的一个或多个标签。该value属性是 的别名labels。如果您不指定标签,则简单类名称将用作主标签。如果您想提供多个标签,您可以:

  1. 向属性提供一个数组labels。数组中的第一个元素将被视为主标签。

  2. 提供一个值primaryLabel并将附加标签放入其中labels

主标签应该始终是反映您的域类的最具体的标签。

对于通过存储库或 Neo4j 模板编写的带注释类的每个实例,将写入图中至少具有主标签的一个节点。反之亦然,所有具有主标签的节点都将映射到带注释的类的实例。

8.关于类层次结构的注释

@Node注释不是从超类型和接口继承的。然而,您可以在每个继承级别单独注释您的域类。这允许多态查询:您可以传入基类或中间类并检索节点的正确的具体实例。仅支持用 注释的抽象基@Node。在此类上定义的标签将与具体实现的标签一起用作附加标签。

对于某些场景,还支持域类层次结构中的接口:

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了实现类,该类可能位于另一个模块中。请注意,该值与实现的接口名称完全相同。重命名是不可能的。

也可以使用不同的主标签代替接口名称:

@Node("PrimaryLabelWN") (1)
public interface SomeInterface2 {

    String getName();

    SomeInterface2 getRelated();
}

public static class SomeInterfaceEntity2 implements SomeInterface2 {

    // Overrides omitted for brevity
}

1.将@Node注解放在接口上

还可以使用接口的不同实现并拥有多态域模型。这样做时,至少需要两个标签:一个确定接口的标签,一个确定具体类的标签:

@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透明地用于两个关系
5.没有指定具体类型

所需的数据结构如下面的测试所示。OGM 也会写同样的内容:

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.动态或“运行时”管理标签

通过简单类名隐式定义或通过@Node注释显式定义的所有标签都是静态的。它们在运行时无法更改。如果您需要可以在运行时操作的其他标签,您可以使用@DynamicLabels@DynamicLabels是字段级别的注释,并将类型java.util.Collection<String>(例如 aListSet)的属性标记为动态标签的源。

如果存在此注释,则节点上存在且未静态映射的所有标签@Node和类名称将在加载期间收集到该集合中。在写入期间,节点的所有标签将被替换为静态定义的标签加上集合的内容。

@DynamicLabels
private Set<String> labels;

10.识别实例:@Id

@Node在创建类和具有特定标签的节点之间的映射的同时,我们还需要在该类的各个实例(对象)和节点的实例之间建立连接。

这就是@Id发挥作用的地方。 @Id将类的属性标记为对象的唯一标识符。在最佳情况下,该唯一标识符是唯一的业务密钥,或者换句话说,是自然密钥。 @Id可用于具有受支持的简单类型的所有属性。

然而自然键很难找到。例如,人们的名字很少是唯一的,会随着时间的推移而改变,或更糟糕的是,不是每个人都有名字和姓氏。

因此,我们支持两种不同类型的代理键

long对于或类型的属性Long@Id可以与 一起使用@GeneratedValue。这会将 Neo4j 内部 id(不是节点或关系上的属性,通常不可见)映射到属性,并允许 SDN 检索该类的各个实例。

@GeneratedValue提供属性generatorClassgeneratorClass可用于指定实现IdGenerator. AnIdGenerator是一个功能接口,它generateId采用主标签和实例来生成 Id。我们支持UUIDStringGenerator作为一种开箱即用的实施方式。

@GeneratedValue您还可以从via上的应用程序上下文中指定 Spring Bean generatorRef。该 bean 还需要实现IdGenerator,但可以利用上下文中的所有内容,包括 Neo4j 客户端或模板来与数据库交互。

11.乐观锁:@Version

Spring Data Neo4j 通过@VersionLong类型化字段上使用注释来支持乐观锁定。该属性将在更新期间自动递增,并且不得手动修改。

例如,如果不同线程中的两个事务想要修改版本相同的对象x,则第一个操作将成功持久化到数据库。此时,版本字段会增加,因此是x+1。第二个操作将失败并显示 a,因为它想要修改 数据库中不再存在的OptimisticLockingFailureException版本的对象。x在这种情况下,需要重试该操作,首先从数据库中重新获取当前版本的对象。

如果使用业务 ID ,则该@Version属性也是必需的。Spring Data Neo4j 将检查此字段以确定该实体是新的还是之前已经被持久化。

12.映射属性:@Property

带注释的类的所有属性都@Node将作为 Neo4j 节点和关系的属性保留。如果没有进一步配置,Java 或 Kotlin 类中的属性名称将用作 Neo4j 属性。

如果您正在使用现有的 Neo4j 架构或只是想根据您的需求调整映射,则需要使用@Property. 用于name指定数据库内属性的名称。

13.连接节点:@Relationship

@Relationship注释可用于所有非简单类型的属性。它适用于用其注释的其他类型的属性@Node或其集合和映射。

type属性value允许配置关系的类型,direction允许指定方向。SDN 中默认的方向是Relationship.Direction#OUTGOING

我们支持动态关系。动态关系表示为Map<String, AnnotatedDomainClass>Map<Enum, AnnotatedDomainClass>。在这种情况下,与其他域类的关系类型由映射键给出,并且不能通过@Relationship.

14.映射关系属性

Neo4j 不仅支持在节点上定义属性,还支持在关系上定义属性。为了在模型中表达这些属性,SDN 提供了@RelationshipProperties应用于简单的 Java 类的方法。在属性类中,必须有一个字段被标记为@TargetNode定义关系所指向的实体。或者,在INCOMING关系上下文中,是来自。

关系属性类及其用法可能如下所示:

@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;
	}
}

您必须为生成的内部 ID ( ) 定义属性@RelationshipId,以便 SDN 可以在保存期间确定可以安全覆盖哪些关系而不丢失属性。如果SDN没有找到存储内部节点id的字段,它将在启动过程中失败。

@Relationship(type = "ACTED_IN", direction = Direction.INCOMING)
private List<Roles> actorsAndRoles;

一般来说,创建查询的关系/跃点没有限制。SDN 解析来自建模节点的整个可达图。

这就是说,当存在双向映射关系的想法时,这意味着您在实体的两端定义关系,您可能会得到比您期望的更多的东西。

考虑一个例子,其中电影演员,并且您想要获取某部电影及其所有演员。如果电影演员之间的关系只是单向的,那么这不会有问题。在双向场景中,SDN 将获取特定的电影、其演员,以及根据关系定义为此演员定义的其他电影。在最坏的情况下,这将级联以获取单个实体的整个图。

15.一个完整的例子

将所有这些放在一起,我们可以创建一个简单的域。我们使用电影和扮演不同角色的人:

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 标签。如果您只是使用普通的,标签默认为类的名称@Node。
2.每个实体都必须有一个 id。我们使用电影的名称作为唯一标识符。
3.这显示@Property为字段使用与图形属性不同的名称的一种方法。
4.这配置了与某人的传入关系。
5.这是您的应用程序代码以及 SDN 使用的构造函数。

人在这里被映射为两个角色,actorsdirectors。域类是相同的:

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可以将实现的类org.springframework.data.neo4j.core.schema.IdGenerator作为参数。SDN 提供InternalIdGenerator(默认)并且UUIDStringGenerator开箱即用。后者为每个实体生成新的 UUID 并将其返回为java.lang.String. 使用它的应用程序实体将如下所示:

@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包含注释和所需的基础设施代码。

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-test</artifactId>
    <scope>test</scope>
</dependency>

@DataNeo4jTest是一个Spring Boot测试片。测试切片为使用 Neo4j 进行测试提供了所有必要的基础设施:事务管理器、客户端、模板和声明的存储库(其命令式或反应式变体,具体取决于是否存在反应式依赖项)。测试片已包含在内@ExtendWith(SpringExtension.class),因此它可以与 JUnit 5 (JUnit Jupiter) 自动运行。

@DataNeo4jTest默认情况下提供命令式和反应式基础设施,并且还添加了隐式基础设施@Transactional。 @Transactional然而,在 Spring 测试中,始终意味着命令式事务,因为声明式事务需要方法的返回类型来决定是否需要命令式事务PlatformTransactionManager或反应式事务。ReactiveTransactionManager

要断言反应式存储库或服务的正确事务行为,您需要将 注入TransactionalOperator 到测试中或将域逻辑包装在使用带注释的方法的服务中,该方法公开返回类型,使基础设施可以选择正确的事务管理器。

测试切片不会引入嵌入式数据库或任何其他连接设置。您可以自行决定是否使用适当的连接。

我们推荐以下两个选项之一:使用Neo4j Testcontainers 模块 或 Neo4j 测试工具。虽然 Testcontainers 是一个众所周知的项目,拥有许多不同服务的模块,但 Neo4j 测试工具却相当陌生。它是一个嵌入式实例,在测试存储过程时特别有用,如测试基于 Neo4j 的 Java 应用程序中所述。然而,测试工具也可用于测试应用程序。由于它在与您的应用程序相同的 JVM 内启动数据库,因此性能和计时可能与您的生产设置不同。

为了您的方便,我们提供了三种可能的场景:Neo4j 测试工具 3.5 和 4.0 以及 Testcontainers Neo4j。我们为 3.5 和 4.0 提供了不同的示例,因为测试工具在这些版本之间发生了变化。此外,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.创建嵌入式 Neo4j 的入口点
2.这是一个 Spring Boot 注释,允许动态注册应用程序属性。我们覆盖相应的 Neo4j 设置。
3.所有测试后关闭 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 企业版的依赖项可在com.neo4j.test:neo4j-harness-enterprise适当的存储库配置下获得。

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.创建嵌入式 Neo4j 的入口点
2.禁用不需要的 Neo4j HTTP 服务器
3.这是一个 Spring Boot 注释,允许动态注册应用程序属性。我们覆盖相应的 Neo4j 设置。
4.所有测试后关闭 Neo4j。

  • 20
    点赞
  • 27
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值