Spring Boot整合Neo4j实战

关于Spring Boot整合Neo4j的介绍很多,但自己上手参考的时候,仍然有些东西云里雾里有点晕。慢慢才摸出正道。

聊记,分享。

 

一、各组件直接的搭档配合和版本密切相关。版本不合适,配合不上。

   我是用的版本搭档是:

     Noe4j 3.5.8

     spring data Noe4j 5.1.3.RELEASE (主要包括OGM SUpport、 Spring Data Repository Support)

     利用maven进行jar包管理

    neo4j jar包引入这块容易晕,和neo4j相关的包很多,例如:

spring-boot-starter-data-neo4j 

neo4j 

spring-data-neo4j

neo4j-ogm-api

neo4j-ogm-bolt-driver
…………

每个是干什么的?引哪个,不引哪个?如何引?这是个技术活。需要根据各子项目的需要,搞清楚各个jar包的用途,在合适的位置引入。

å¨è¿éæå¥å¾çæè¿°

我们项目既没有直接引入neo4j包,更不是引入spring starter相关的neo4j组件,例如:

      
       
       
       
        
 




 而是在base模块引入neo4j-ogm-api组件,例:

       
            org.neo4j
            neo4j-ogm-api
            2.1.6
        

像JPA使用了ORM一样,Neo4j使用了对象-图形映射(Object-Graph Mapping,OGM)的方式来建模。

service模块引入:

        
        
            org.neo4j
            neo4j-ogm-bolt-driver
            2.1.6
        

Neo4j总共有三种连接方式。常用的有两种,一种是http的连接方式【端口:7474】,一种是Bolt的连接方式【端口:7687】

 

dao模块引入:

  
        
            org.springframework.data
            spring-data-neo4j
            4.2.11.RELEASE
        
    

Spring DATA Neo4j存储库提供了不同的API来支持不同的场景

  • GraphRepository
  • GraphTemplate
  • CrudRepository
  • PaginationAndSortingRepository

这些是Java类。 每个具有执行Neo4j数据库操作的特定目的

S.No. Spring 数据 Neo4j 类 用法
1。 GraphRepository 它用于执行Basic Neo4j DB操作。
2。 GraphTemplate 像其他模块一样,它是执行Neo4j DB操作的Spring模板。
3。 CrudRepository 它用于使用Cypher查询语言(CQL)执行Neo4j CRUD操作。
4。 PaginationAndSortingRepository 它用于执行Neo4j CQL查询结果的分页和排序。

我们只需要使接口继承Neo4jRepository就可以使用该接口提供的一些基础的增删改查方法。

@Repository
public interface BotRepository extends Neo4jRepository {
    BotNode findAllByName(String name);

}

对于复杂的查询我们可以参照上面讲到的CQL语句执行。

@Repository
public interface BotRelationRepository extends Neo4jRepository {
    //返回节点n以及n指向的所有节点与关系
    @Query("MATCH p=(n:Bot)-[r:BotRelation]->(m:Bot) WHERE id(n)={0} RETURN p")
    List findAllByBotNode(BotNode botNode);

    //返回节点n以及n指向或指向n的所有节点与关系
    @Query("MATCH p=(n:Bot)<-[r:BotRelation]->(m:Bot) WHERE m.name={name} RETURN p")
    List findAllBySymptom(@Param("name") String name);

    //返回节点n以及n指向或指向n的所有节点以及这些节点间的所有关系
    @Query("MATCH p=(n:Bot)<-[r:BotRelation]->(m:Bot)<-[:BotRelation]->(:Bot)<-[:BotRelation]->(n:Bot) WHERE n.name={name} RETURN p")
    List findAllByStartNode(@Param("name") String name);


}

二、 application.yml配置neo4j库访问信息

neo4j默认密码为neo4j登录时会提示修改密码 此为修改后的密码

  data:
    neo4j:
      uri: bolt://10.143.151.27:7687
      username: neo4j
      password: xxxneo4j

三、Neo4j实战遇到的一些问题解决记录

(1)Neo4j删除节点和关系、彻底删除节点标签名

此处给出原文总结:

【1】先删关系,再删节点

【2】当记不得关系名时,type(r)可以查到关系名

【3】彻底删除节点标签名,需要删除前期对该标签名建立的索引

具体参考:https://www.jianshu.com/p/59bd829de0de

 

(2)怎么合并相同节点?Neo4j图数据库为什么可以重复插入同一条数据,怎么可以不重复插入

CREATE (ww:DatabaseConnection { ConnectionId:'c338df71cdcf85ebadac1aab31e25b3f',ConnectionHost:'localhost',ConnectionPort:'1521',ConnectionSeverName:'TESTUSE',ConnectionUserName:'system',ConnectionPassword:'orcl'})

比如这样一条语句,我执行两次会产生两个一样的节点

使用merge 关键字
merge  (n:person{id:1})  set n+={id:1,name:'lz',age:18} return n

当person节点属性 id=1 匹配时 ,更新该节点,若不存在则创建该节点。  

具体参考:http://neo4j.com.cn/topic/595229bf4ee6742c0459236e

(3)@JsonIdentityInfo的使用:

使用注解@JsonIdentityInfo是防止查询数据时引发递归访问效应,注解@NodeEntity标志这个类是一个节点实体,注解@GraphId定义了节点的一个唯一性标识,它将在创建节点时由系统自动生成,所以它是不可缺少的。

@JsonIdentityInfo(generator=JSOGGenerator.class)
@NodeEntity
public class Actor {
    @GraphId Long id;
    private String name;
    private int born;

    public Actor() { }

代码清单2-22是电影节点实体建模,注解@Relationship表示List是一个关系列表,其中type设定了关系的类型,direction设定这个关系的方向,Relationship.INCOMING表示以这个节点为终点。addRole定义了增加一个关系的方法。
代码清单2-22 电影节点实体建模

@JsonIdentityInfo(generator=JSOGGenerator.class)
@NodeEntity
public class Movie {
    @GraphId Long id;
    String title;
    String year;
    String tagline;
    @Relationship(type="ACTS_IN", direction = Relationship.INCOMING)
    List roles = new ArrayList<>();
public Role addRole(Actor actor, String name){
    Role role = new Role(actor,this,name);
    this.roles.add(role);
    return role;
}

public Movie() { }

代码清单2-23是角色的关系实体建模,注解@RelationshipEntity表明这个类是一个关系实体,并用type指定了关系的类型,其中@StartNode指定起始节点的实体,
@EndNode指定终止节点的实体,这说明了图中一条有向边的起点和终点的定义。其中定义了一个创建关系的构造函数Role(Actor actor,Movie movie,String name),这里的name参数用来指定这个关系的属性。
代码清单2-23 角色关系实体建模

@JsonIdentityInfo(generator=JSOGGenerator.class)
@RelationshipEntity(type = "ACTS_IN")
public class Role {
    @GraphId
    Long id;
    String role;
    @StartNode
    Actor actor;
    @EndNode
    Movie movie;

    public Role() {
    }

    public Role(Actor actor, Movie movie, String name) {
        this.actor = actor;
        this.movie = movie;
        this.role = name;
    }

(4)如果你是使用Spring boot2.0以上,在你创建项目完成后,启动程序报错:

Caused by: java.lang.ClassNotFoundException: org.neo4j.ogm.drivers.http.driver.HttpDriver
    at java.net.URLClassLoader.findClass(URLClassLoader.java:381) ~[na:1.8.0_111]
    at java.lang.ClassLoader.loadClass(ClassLoader.java:424) ~[na:1.8.0_111]
    at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:331) ~[na:1.8.0_111]
    at java.lang.ClassLoader.loadClass(ClassLoader.java:357) ~[na:1.8.0_111]
    at java.lang.Class.forName0(Native Method) ~[na:1.8.0_111]
    at java.lang.Class.forName(Class.java:264) ~[na:1.8.0_111]
    at org.neo4j.ogm.session.SessionFactory.newDriverInstance(SessionFactory.java:92) ~[neo4j-ogm-core-3.1.0.jar:3.1.0]
    ... 45 common frames omitted

 

原因是缺少依赖,解决方法是导入缺少的依赖:


            org.neo4j
            neo4j-ogm-http-driver
        

 

(5)新建节点类,id的属性为Long而不能为long

还需要注意的是在Spring boot1.5中修饰id属性的注释为@GraphIdorg.neo4j.ogm.annotation.Id不存在,效果一样,都是Neo4j数据库自动创建的ID值。

 @Id
    @GeneratedValue
    private Long id; //id

(6)测试更新数据:

    @Test
    public void updata(){
        Movie movie = movieRepository.findAllById(8183l);
        movie.setName("《迪迦》");
        movieRepository.save(movie);
        System.out.println(movieRepository.findAll());
    }

执行程序,报错:

java.lang.NullPointerException

我们看到程序执行的CQL语句为:

MATCH (n:`Movie`) WHERE n.`id` = { `id_0` } WITH n RETURN n, ID(n)

然后我们在Neo4j浏览器控制台执行查询语句:

Spring Boot整合Neo4j实战_第1张图片

这是为什么呢?在Neo4j中,根据Id查询节点的语句为:

MATCH (n:Movie) where id(n)=8183  RETURN n

我们修改Repository层的查询方法:

@Repository
public interface MovieRepository extends Neo4jRepository {
    @Query("MATCH (n:Movie) where id(n)={id}  RETURN n")
    Movie findAllById(@Param("id") Long id);
}

再次执行更新程序,结果为:

[Movie{id=8183, name='《迪迦》'}]

 

四、更多参考

https://www.2cto.com/database/201801/713556.html

https://blog.csdn.net/zt15732625878/article/details/98797467

https://www.jianshu.com/p/df99fe312c04

https://yq.aliyun.com/articles/89699/

https://docs.spring.io/spring-data/neo4j/docs/5.1.3.RELEASE/reference/html/

Cypher语言

https://blog.csdn.net/ainuser/article/details/72268344

关于Neo4j和Cypher批量更新和批量插入优化的5个建议

https://blog.csdn.net/hwz2311245/article/details/60963383

你可能感兴趣的:(工作纪实)