【Neo4j权威指南】SpringBoot 集成 Neo4j 教程SDN

一、Neo4j 介绍

Neo4j 采用 JAVA 语言开发,是一个高性能的图形数据库,NOSQL 中的一种,它将结构化数据存储在网络上而不是表中。它是一个嵌入式的、基于磁盘的、具备完全的事务特性的Java持久化引擎。

二、Neo4j 安装

单机版的 Neo4j 安装简单,从官网下载安装包,在 conf 目录下找到 neo4j.conf 配置修改如下配置信息,将注释去掉即可:

dbms.default_listen_address=0.0.0.0
dbms.connector.bolt.enabled=true
dbms.connector.bolt.listen_address=:7687
dbms.connector.http.enabled=true
dbms.connector.http.listen_address=:7474

运行命令启动服务:

./bin/neo4j start    # 启动
./bin/neo4j stop     # 停止服务
./bin/neo4j restart  # 重启服务
./bin/neo4j status   # 查看状态

三、SpringBoot 整合

  1. 引入关键的 Neo4j Maven 坐标
<dependency>
   <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-neo4j</artifactId>
    <exclusions>
        <exclusion>
            <artifactId>neo4j-java-driver</artifactId>
            <groupId>org.neo4j.driver</groupId>
        </exclusion>
    </exclusions>
</dependency>
<dependency>
    <groupId>org.neo4j.driver</groupId>
    <artifactId>neo4j-java-driver</artifactId>
    <version>4.4.5</version>
</dependency>
  1. 编写实体和关系类

这里主要用到注解 @Node@Relationship@Property@RelationshipProperties@Id@TargetNode,分别对应的含义是,定义节点、定义关系、定义节点或关系属性、定义关系属性集合、定义SDN的主键、定义关系的另一端节点对象。如下所示:

/**
 * 电影实体类
 * @author ouyangrongtao
 * @since 2022-05-04 12:17
 */
@Data
@NoArgsConstructor
@Node("Movie")
public class MovieEntity {
    @Id
    private String title;

    @Property("description")
    private String description;

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

    @Relationship(type = "DIRECTED", direction = Relationship.Direction.INCOMING)
    private List<PersonEntity> directors;

    public MovieEntity(String title, String description) {
        this.title = title;
        this.description = description;
    }
}

/**
 * 人员实体类
 * @author ouyangrongtao
 * @since 2022-05-04 12:17
 */
@Data
@NoArgsConstructor
@Node("Person")
public class PersonEntity {
    @Id
    private String name;

    @Property("born")
    private Integer born;

    public PersonEntity(Integer born, String name) {
        this.born = born;
        this.name = name;
    }
}

/**
 * 电影-人员关系类
 * @author ouyangrongtao
 * @since 2022-05-04 12:13
 */
@Data
@NoArgsConstructor
@RelationshipProperties
public class Roles {
    @Property("id")
    private Long id;

    @Property("roles")
    private List<String> roleList;

    @TargetNode
    private PersonEntity person;

    public Roles(Long id, PersonEntity person, List<String> roleList) {
        this.id = id;
        this.person = person;
        this.roleList = roleList;
    }
}
  1. 编写repository
    repository 优点类似 jpa,就是继承的类不同。
/**
 * @author ouyangrongtao
 * @since 2022-05-04 12:30
 */
@Repository
public interface MovieRepository extends Neo4jRepository<MovieEntity, String> {

}
  1. 使用 repository 进行操作

/**
 * @author ouyangrongtao
 * @since 2022-05-04 12:50
 */
public interface MovieService {
    void add();
    
    MovieEntity findMovie();
    
    MovieEntity testNeo4jTemplate();
    
    Long count();
}
/**
 * @autho
 * r ouyangrongtao
 * @since 2022-05-04 12:51
 */
@Service
public class MovieServiceImpl implements MovieService {

    private final MovieRepository movieRepository;

    private final Neo4jTemplate neo4jTemplate;

    @Autowired
    public MovieServiceImpl(MovieRepository movieRepository, Neo4jTemplate neo4jTemplate) {
        this.movieRepository = movieRepository;
        this.neo4jTemplate = neo4jTemplate;
    }


    @Override
    public void add() {
        MovieEntity movie = new MovieEntity("我爱我的祖国", "该片讲述了新中国成立70年间普通百姓与共和国息息相关的故事");

        List<Roles> rolesList = new ArrayList<>(8);
        rolesList.add(new Roles(1L, new PersonEntity(1974, "黄渤"), List.of("林治远")));
        rolesList.add(new Roles(2L, new PersonEntity(1978, "张译"), List.of("高远")));
        rolesList.add(new Roles(3L, new PersonEntity(1974, "吴京"), List.of("陈冬冬")));
        rolesList.add(new Roles(4L, new PersonEntity(1985, "杜江"), List.of("朱涛")));
        rolesList.add(new Roles(5L, new PersonEntity(1957, "葛优"), List.of("张北京")));
        rolesList.add(new Roles(6L, new PersonEntity(1997, "刘昊然"), List.of("沃德乐")));

        movie.setRoles(rolesList);
        movie.setDirectors(Collections.singletonList(new PersonEntity(1952, "陈凯歌")));

        movieRepository.save(movie);
    }

    @Override
    public MovieEntity findMovie() {
        return movieRepository.findById("我爱我的祖国").orElse(null);
    }

    @Override
    public MovieEntity testNeo4jTemplate() {
        return neo4jTemplate.findById("我爱我的祖国", MovieEntity.class).orElse(null);
    }

    @Override
    public Long count() {
        return movieRepository.count();
    }
}

四、整合测试

  1. 使用接口进行测试
@GetMapping("/addMovie")
public void addMovie() {
    movieService.add();
}
  1. 单元测试
@SpringBootTest
class MovieServiceImplTest {

    @Autowired
    private MovieService movieService;

    @Test
    void add() {
        movieService.add();
    }

    @Test
    void count() {
        Long count = movieService.count();
        Assertions.assertTrue(count > 0);
    }

    @Test
    void findMovie() {
        MovieEntity movie = movieService.findMovie();
        Assertions.assertNotNull(movie);
    }

    @Test
    void testNeo4jTemplate() {
        MovieEntity movie = movieService.testNeo4jTemplate();
        Assertions.assertNotNull(movie);
    }
}

五、结果验证

  1. 查看 neo4j 数据库结果如下,从图可以看出插入正常;测试用例执行正常表示断言正确,查询返回的内容是期望的。
    在这里插入图片描述

六、过程中遇到的问题记录

org.neo4j.driver.exceptions.DatabaseException: Index traversal aborted due to being stuck in infinite loop. This is most likely caused by an inconsistency in the index. Loop occurred when restarting search from root from page 5. | GB+Tree[file:/mnt/d/tmp/neo4j-community-4.4.6/data/databases/neo4j/neostore.labelscanstore.db, layout:TokenScanLayout[version:0.1, identifier:21483684112629824, fixedSize:true], generation:22/23]
	at org.neo4j.driver.internal.util.Futures.blockingGet(Futures.java:143)
	at org.neo4j.driver.internal.InternalResult.blockingGet(InternalResult.java:128)
	at org.neo4j.driver.internal.InternalResult.hasNext(InternalResult.java:64)
	at org.springframework.data.neo4j.core.DefaultNeo4jClient$DefaultRecordFetchSpec.one(DefaultNeo4jClient.java:329)
	... 98 more
	Suppressed: org.neo4j.driver.internal.util.ErrorUtil$InternalExceptionCause
		at org.neo4j.driver.internal.util.ErrorUtil.newNeo4jError(ErrorUtil.java:85)
		at org.neo4j.driver.internal.async.inbound.InboundMessageDispatcher.handleFailureMessage(InboundMessageDispatcher.java:105)
		at org.neo4j.driver.internal.messaging.common.CommonMessageReader.unpackFailureMessage(CommonMessageReader.java:83)
		at org.neo4j.driver.internal.messaging.common.CommonMessageReader.read(CommonMessageReader.java:59)
		at org.neo4j.driver.internal.async.inbound.InboundMessageHandler.channelRead0(InboundMessageHandler.java:83)
		at org.neo4j.driver.internal.async.inbound.InboundMessageHandler.channelRead0(InboundMessageHandler.java:35)
		at org.neo4j.driver.internal.shaded.io.netty.channel.SimpleChannelInboundHandler.channelRead(SimpleChannelInboundHandler.java:99)
		at org.neo4j.driver.internal.shaded.io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:379)
		at org.neo4j.driver.internal.shaded.io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:365)
		at org.neo4j.driver.internal.shaded.io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:357)
		at org.neo4j.driver.internal.shaded.io.netty.handler.codec.ByteToMessageDecoder.fireChannelRead(ByteToMessageDecoder.java:324)
		at org.neo4j.driver.internal.shaded.io.netty.handler.codec.ByteToMessageDecoder.channelRead(ByteToMessageDecoder.java:296)
		at org.neo4j.driver.internal.async.inbound.MessageDecoder.channelRead(MessageDecoder.java:47)
		at org.neo4j.driver.internal.shaded.io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:379)
		at org.neo4j.driver.internal.shaded.io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:365)
		at org.neo4j.driver.internal.shaded.io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:357)
		at org.neo4j.driver.internal.shaded.io.netty.handler.codec.ByteToMessageDecoder.fireChannelRead(ByteToMessageDecoder.java:324)
		at org.neo4j.driver.internal.shaded.io.netty.handler.codec.ByteToMessageDecoder.channelRead(ByteToMessageDecoder.java:296)
		at org.neo4j.driver.internal.shaded.io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:379)
		at org.neo4j.driver.internal.shaded.io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:365)
		at org.neo4j.driver.internal.shaded.io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:357)
		at org.neo4j.driver.internal.shaded.io.netty.channel.DefaultChannelPipeline$HeadContext.channelRead(DefaultChannelPipeline.java:1410)
		at org.neo4j.driver.internal.shaded.io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:379)
		at org.neo4j.driver.internal.shaded.io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:365)
		at org.neo4j.driver.internal.shaded.io.netty.channel.DefaultChannelPipeline.fireChannelRead(DefaultChannelPipeline.java:919)
		at org.neo4j.driver.internal.shaded.io.netty.channel.nio.AbstractNioByteChannel$NioByteUnsafe.read(AbstractNioByteChannel.java:166)
		at org.neo4j.driver.internal.shaded.io.netty.channel.nio.NioEventLoop.processSelectedKey(NioEventLoop.java:719)
		at org.neo4j.driver.internal.shaded.io.netty.channel.nio.NioEventLoop.processSelectedKeysOptimized(NioEventLoop.java:655)
		at org.neo4j.driver.internal.shaded.io.netty.channel.nio.NioEventLoop.processSelectedKeys(NioEventLoop.java:581)
		at org.neo4j.driver.internal.shaded.io.netty.channel.nio.NioEventLoop.run(NioEventLoop.java:493)
		at org.neo4j.driver.internal.shaded.io.netty.util.concurrent.SingleThreadEventExecutor$4.run(SingleThreadEventExecutor.java:989)
		at org.neo4j.driver.internal.shaded.io.netty.util.internal.ThreadExecutorMap$2.run(ThreadExecutorMap.java:74)
		at org.neo4j.driver.internal.shaded.io.netty.util.concurrent.FastThreadLocalRunnable.run(FastThreadLocalRunnable.java:30)
		at java.base/java.lang.Thread.run(Thread.java:834)
org.neo4j.driver.exceptions.ServiceUnavailableException: Connection to the database terminated. Please ensure that your database is listening on the correct host and port and that you have compatible encryption settings both on Neo4j server and driver. Note that the default encryption setting has changed in Neo4j 4.0.
		at org.neo4j.driver.internal.util.ErrorUtil.newConnectionTerminatedError(ErrorUtil.java:56) ~[neo4j-java-driver-4.4.0.jar:4.4.0-a44346cddc3aac335fd3d98cf18e489b3790ebee]
		at org.neo4j.driver.internal.util.ErrorUtil.newConnectionTerminatedError(ErrorUtil.java:49) ~[neo4j-java-driver-4.4.0.jar:4.4.0-a44346cddc3aac335fd3d98cf18e489b3790ebee]
		at org.neo4j.driver.internal.async.inbound.ChannelErrorHandler.channelInactive(ChannelErrorHandler.java:74) ~[neo4j-java-driver-4.4.0.jar:4.4.0-a44346cddc3aac335fd3d98cf18e489b3790ebee]
		at org.neo4j.driver.internal.shaded.io.netty.channel.AbstractChannelHandlerContext.invokeChannelInactive(AbstractChannelHandlerContext.java:262) ~[neo4j-java-driver-4.4.0.jar:4.4.0-a44346cddc3aac335fd3d98cf18e489b3790ebee]
		at org.neo4j.driver.internal.shaded.io.netty.channel.AbstractChannelHandlerContext.invokeChannelInactive(AbstractChannelHandlerContext.java:248) ~[neo4j-java-driver-4.4.0.jar:4.4.0-a44346cddc3aac335fd3d98cf18e489b3790ebee]
		at org.neo4j.driver.internal.shaded.io.netty.channel.AbstractChannelHandlerContext.fireChannelInactive(AbstractChannelHandlerContext.java:241) ~[neo4j-java-driver-4.4.0.jar:4.4.0-a44346cddc3aac335fd3d98cf18e489b3790ebee]
		at org.neo4j.driver.internal.shaded.io.netty.handler.codec.ByteToMessageDecoder.channelInputClosed(ByteToMessageDecoder.java:389) ~[neo4j-java-driver-4.4.0.jar:4.4.0-a44346cddc3aac335fd3d98cf18e489b3790ebee]
		at org.neo4j.driver.internal.shaded.io.netty.handler.codec.ByteToMessageDecoder.channelInactive(ByteToMessageDecoder.java:354) ~[neo4j-java-driver-4.4.0.jar:4.4.0-a44346cddc3aac335fd3d98cf18e489b3790ebee]
		at org.neo4j.driver.internal.shaded.io.netty.channel.AbstractChannelHandlerContext.invokeChannelInactive(AbstractChannelHandlerContext.java:262) ~[neo4j-java-driver-4.4.0.jar:4.4.0-a44346cddc3aac335fd3d98cf18e489b3790ebee]
		at org.neo4j.driver.internal.shaded.io.netty.channel.AbstractChannelHandlerContext.invokeChannelInactive(AbstractChannelHandlerContext.java:248) ~[neo4j-java-driver-4.4.0.jar:4.4.0-a44346cddc3aac335fd3d98cf18e489b3790ebee]
		at org.neo4j.driver.internal.shaded.io.netty.channel.AbstractChannelHandlerContext.fireChannelInactive(AbstractChannelHandlerContext.java:241) ~[neo4j-java-driver-4.4.0.jar:4.4.0-a44346cddc3aac335fd3d98cf18e489b3790ebee]
		at org.neo4j.driver.internal.shaded.io.netty.handler.codec.ByteToMessageDecoder.channelInputClosed(ByteToMessageDecoder.java:389) ~[neo4j-java-driver-4.4.0.jar:4.4.0-a44346cddc3aac335fd3d98cf18e489b3790ebee]
		at org.neo4j.driver.internal.shaded.io.netty.handler.codec.ByteToMessageDecoder.channelInactive(ByteToMessageDecoder.java:354) ~[neo4j-java-driver-4.4.0.jar:4.4.0-a44346cddc3aac335fd3d98cf18e489b3790ebee]
		at org.neo4j.driver.internal.shaded.io.netty.channel.AbstractChannelHandlerContext.invokeChannelInactive(AbstractChannelHandlerContext.java:262) ~[neo4j-java-driver-4.4.0.jar:4.4.0-a44346cddc3aac335fd3d98cf18e489b3790ebee]
		at org.neo4j.driver.internal.shaded.io.netty.channel.AbstractChannelHandlerContext.invokeChannelInactive(AbstractChannelHandlerContext.java:248) ~[neo4j-java-driver-4.4.0.jar:4.4.0-a44346cddc3aac335fd3d98cf18e489b3790ebee]
		at org.neo4j.driver.internal.shaded.io.netty.channel.AbstractChannelHandlerContext.fireChannelInactive(AbstractChannelHandlerContext.java:241) ~[neo4j-java-driver-4.4.0.jar:4.4.0-a44346cddc3aac335fd3d98cf18e489b3790ebee]
		at org.neo4j.driver.internal.shaded.io.netty.channel.DefaultChannelPipeline$HeadContext.channelInactive(DefaultChannelPipeline.java:1405) ~[neo4j-java-driver-4.4.0.jar:4.4.0-a44346cddc3aac335fd3d98cf18e489b3790ebee]
		at org.neo4j.driver.internal.shaded.io.netty.channel.AbstractChannelHandlerContext.invokeChannelInactive(AbstractChannelHandlerContext.java:262) ~[neo4j-java-driver-4.4.0.jar:4.4.0-a44346cddc3aac335fd3d98cf18e489b3790ebee]
		at org.neo4j.driver.internal.shaded.io.netty.channel.AbstractChannelHandlerContext.invokeChannelInactive(AbstractChannelHandlerContext.java:248) ~[neo4j-java-driver-4.4.0.jar:4.4.0-a44346cddc3aac335fd3d98cf18e489b3790ebee]
		at org.neo4j.driver.internal.shaded.io.netty.channel.DefaultChannelPipeline.fireChannelInactive(DefaultChannelPipeline.java:901) ~[neo4j-java-driver-4.4.0.jar:4.4.0-a44346cddc3aac335fd3d98cf18e489b3790ebee]
		at org.neo4j.driver.internal.shaded.io.netty.channel.AbstractChannel$AbstractUnsafe$8.run(AbstractChannel.java:831) ~[neo4j-java-driver-4.4.0.jar:4.4.0-a44346cddc3aac335fd3d98cf18e489b3790ebee]
		at org.neo4j.driver.internal.shaded.io.netty.util.concurrent.AbstractEventExecutor.safeExecute(AbstractEventExecutor.java:164) ~[neo4j-java-driver-4.4.0.jar:4.4.0-a44346cddc3aac335fd3d98cf18e489b3790ebee]
		at org.neo4j.driver.internal.shaded.io.netty.util.concurrent.SingleThreadEventExecutor.runAllTasks(SingleThreadEventExecutor.java:469) ~[neo4j-java-driver-4.4.0.jar:4.4.0-a44346cddc3aac335fd3d98cf18e489b3790ebee]
		at org.neo4j.driver.internal.shaded.io.netty.channel.nio.NioEventLoop.run(NioEventLoop.java:497) ~[neo4j-java-driver-4.4.0.jar:4.4.0-a44346cddc3aac335fd3d98cf18e489b3790ebee]
		at org.neo4j.driver.internal.shaded.io.netty.util.concurrent.SingleThreadEventExecutor$4.run(SingleThreadEventExecutor.java:986) ~[neo4j-java-driver-4.4.0.jar:4.4.0-a44346cddc3aac335fd3d98cf18e489b3790ebee]
		at org.neo4j.driver.internal.shaded.io.netty.util.internal.ThreadExecutorMap$2.run(ThreadExecutorMap.java:74) ~[neo4j-java-driver-4.4.0.jar:4.4.0-a44346cddc3aac335fd3d98cf18e489b3790ebee]
		at org.neo4j.driver.internal.shaded.io.netty.util.concurrent.FastThreadLocalRunnable.run(FastThreadLocalRunnable.java:30) ~[neo4j-java-driver-4.4.0.jar:4.4.0-a44346cddc3aac335fd3d98cf18e489b3790ebee]
		... 1 common frames omitted

在集成的过程中遇到如上的报错信息,特别是org.neo4j.driver.exceptions.DatabaseException: Index traversal aborted due to being stuck in infinite loop. This is most likely caused by an inconsistency in the index 异常,在查询问题所在时发现大家遇到这个问题的人几乎没有,也没有解决方案,但实际是因为neo4j驱动 neo4j-java-driver 的版本与数据库的版本不兼容导致;在遇到第二个问题的时候,也是因为驱动版本的原因,但需要注意配置 spring.neo4j.security.encrypted 需要等于 false

  • 5
    点赞
  • 34
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 7
    评论
在Spring Boot中集成Neo4j需要进行以下步骤: 1. 添加依赖:在`pom.xml`文件中添加Neo4j的依赖。 ```xml <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-neo4j</artifactId> </dependency> ``` 2. 配置数据源:在`application.properties`文件中配置Neo4j的连接信息。 ```properties spring.data.neo4j.uri=bolt://localhost:7687 spring.data.neo4j.username=neo4j spring.data.neo4j.password=password ``` 3. 创建实体类:创建表示节点以及关系的实体类。 ```java @NodeEntity public class Person { @Id @GeneratedValue private Long id; private String name; // 省略getter和setter方法 } @RelationshipEntity(type = "FRIEND") public class Friendship { @Id @GeneratedValue private Long id; @StartNode private Person person1; @EndNode private Person person2; // 省略getter和setter方法 } ``` 4. 创建Repository:创建用于操作数据库的Repository接口。 ```java public interface PersonRepository extends Neo4jRepository<Person, Long> { // 自定义查询方法 } ``` 5. 使用Repository:在需要使用Neo4j的地方注入Repository,进行CRUD操作。 ```java @Service public class PersonService { private final PersonRepository personRepository; public PersonService(PersonRepository personRepository) { this.personRepository = personRepository; } public void savePerson(Person person) { personRepository.save(person); } // 其他操作方法 } ``` 这样就完成了Spring Boot与Neo4j集成。你可以根据需要自定义Repository的方法,也可以使用Spring Data Neo4j提供的方法进行操作。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

lytao123

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值