【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 整合
- 引入关键的 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>
- 编写实体和关系类
这里主要用到注解 @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;
}
}
- 编写repository
repository 优点类似 jpa,就是继承的类不同。
/**
* @author ouyangrongtao
* @since 2022-05-04 12:30
*/
@Repository
public interface MovieRepository extends Neo4jRepository<MovieEntity, String> {
}
- 使用 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();
}
}
四、整合测试
- 使用接口进行测试
@GetMapping("/addMovie")
public void addMovie() {
movieService.add();
}
- 单元测试
@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);
}
}
五、结果验证
- 查看 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 。
|