springboot整合neo4j-使用原生cypher Java API

该文的实现有更简单的方式,详见我的另一篇博客springboot整合neo4j–采用Neo4jClient和Neo4jTemplate方式

1.背景

Neo4j 提供 JAVA API 以编程方式执行所有数据库操作。它支持三种类型的API:
1、Neo4j 原生的 Java API
原生 Java API 是一种低级别的纯 JAVA API,用于执行数据库操作。
2、Neo4j Cypher Java API
Cypher Java API 是简单而强大的 JAVA API,用于执行所有CQL命令以执行数据库操作。
3、Neo4j OGM JAVA API
OGM Java API和Mybatis、JPA类似可以直接和SpringData整合

2.分析

目前很多项目针对neo4j的操作采用Neo4j OGM JAVA API,该方案与springboot整合之后可以使用@Query注解很方便的编写查询接口,比如这篇SpringBoot集成neo4j实战
但是这种方式对图数据库的增删改查均依赖业务数据对象(就是你的Java bean对象,比如People、Company),如果我们数据类型很多且不断变化,那这种方式就不再适合。
Neo4j Cypher Java API对neo4j的操作是业务数据对象无关的,不管是什么样的节点数据或关系数据均可以操作。因为它是直接使用session执行cyhper语句。具体区别可自行查看下方代码。

3.代码实战

1.依赖

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>org.example</groupId>
    <artifactId>chaos</artifactId>
    <version>1.0-SNAPSHOT</version>

    <properties>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </properties>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.6.0</version>
        <relativePath/>
    </parent>

    <dependencies>
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-api</artifactId>
            <version>1.7.30</version>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <scope>runtime</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-aspects</artifactId>
            <version>5.1.9.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>2.1.3</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>cn.hutool</groupId>
            <artifactId>hutool-all</artifactId>
            <version>5.3.2</version>
        </dependency>
        <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.2.0</version>
        </dependency>-->
        
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <configuration>
                    <excludes>
                        <exclude>
                            <groupId>org.projectlombok</groupId>
                            <artifactId>lombok</artifactId>
                        </exclude>
                    </excludes>
                </configuration>
            </plugin>
        </plugins>
        <resources>
            <resource>
                <filtering>true</filtering>
                <directory>src/main/resources</directory>
                <excludes>
                    <!--<exclude>**/*</exclude>-->
                </excludes>
            </resource>
        </resources>

    </build>



</project>

1.Neo4jClientConfig

package com.dbs.neo4j.config;

import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;

@Data
@Configuration
@ConfigurationProperties(prefix = "com.dbs.neo4j")
public class Neo4jClientConfig {
    private String uri;
    private String username;
    private String password;
}

**2.Neo4jClientService **

package com.win.chaos.service;

import com.win.chaos.config.Neo4jConfig;
import com.win.chaos.exception.Neo4jException;
import lombok.extern.slf4j.Slf4j;
import org.neo4j.driver.*;
import org.springframework.stereotype.Component;

@Slf4j
@Component
public class Neo4jClientService {

    private final Driver driver;

    public Neo4jClientService(Neo4jConfig neo4jConfig) {
        this.driver = GraphDatabase.driver(neo4jConfig.getUri(), AuthTokens.basic(neo4jConfig.getUsername(), neo4jConfig.getPassword()));
    }

    /**
     * 适用执行写逻辑
     * @param cypher cypher
     * @return Result 由于return之前session已经被关闭,该result不能被消费
     * @throws Neo4jException 执行cypher出现异常
     */
    public Result run(String cypher) throws Neo4jException {
        Session session = driver.session();
        Transaction ts = session.beginTransaction();
        try {
            Result result = ts.run(cypher);
            ts.commit();
            return result;
        } catch (Exception e) {
            ts.rollback();
            log.error("run neo4j cypher error with ", e);
            throw new Neo4jException("run " + cypher + " error with" + e.getMessage());
        }finally {
            ts.close();
            session.close();
        }
    }

    /**
     * 用于执行读或写cypher语句
     * @param gql cypher
     * @return Result
     * @throws Neo4jException 执行cypher出现异常
     */
    public Result exec(String gql) throws Neo4jException {
        try {
            Session session = driver.session();
            log.info("exec {}", gql);
            return session.run(gql);
        } catch (Exception e) {
            log.error("execute gql {} error ", gql, e);
            throw new Neo4jException("execute " + gql + " error with" + e.getMessage());
        }
    }
}

3.Controller

	@Resource 
	private Neo4jClientService  neo4jClient;

	//查询节点
    @GetMapping("/getNode")
    public Neo4jGraph getNode() throws Neo4jException {
        String ql = "MATCH (n:`公司`) RETURN n LIMIT 25";
        Result result = neo4jClient.run(ql);
        return Neo4jGraph.parse(result);
    }
    //查询路径
    @GetMapping("/matchPath")
    public Neo4jGraph matchPath() throws Neo4jException {
        String ql = "MATCH p=()-[r:`持股`]->() RETURN p LIMIT 25";
        Result result = neo4jClient.run(ql);
        return Neo4jGraph.parse(result);
    }
    //测试创建和删除
    @GetMapping("/test")
    public void add() throws Neo4jException {
        String addQL = "CREATE (o:people {name:\"里斯\",id:32435})";
        Result addResult = neo4jClient.run(addQL);

        String delQL = "match (n:people) delete n";
        Result delResult = neo4jClient.run(delQL);
    }

以上代码实现了springboot整合neo4j原生cypher,可以进行业务数据对象无关的操作。
代码中的Neo4jGraph.parse为解析查询对象接口。如有需要请看下面代码。
4.Neo4jGraph

package com.win.chaos.model.neo4j;

import cn.hutool.core.collection.ConcurrentHashSet;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.neo4j.driver.Record;
import org.neo4j.driver.Result;
import org.neo4j.driver.Value;
import org.neo4j.driver.internal.types.TypeConstructor;
import org.neo4j.driver.types.Node;
import org.neo4j.driver.types.Path;
import org.neo4j.driver.types.Relationship;
import org.neo4j.driver.types.Type;
import java.util.List;
import java.util.Set;

@Data
@Slf4j
public class Neo4jGraph {
    private Set<Neo4jNode> nodes = new ConcurrentHashSet<>();
    private Set<Neo4jEdge> edges = new ConcurrentHashSet<>();

    /**
     * 解析查询结果,目前仅解析节点(Node)、关系(Relationship)、路径(Path)三种查询结果类型
     * @param result 查询结果集
     * @return Neo4jGraph
     */
    public static Neo4jGraph parse(Result result) {
        if (result == null) return null;
        Neo4jGraph graph = new Neo4jGraph();
        List<Record> records = result.list();
        for (Record record:records){
            List<Value> values = record.values();
            for (Value value : values) {
                Type type = value.type();
                if (type.name().equals(TypeConstructor.NODE.name())) {
                    Node node = value.asNode();
                    graph.addNeo4jNode(new Neo4jNode(node));
                }else if (type.name().equals(TypeConstructor.PATH.name())) {
                    Path path = value.asPath();
                    path.nodes().forEach(node -> graph.addNeo4jNode(new Neo4jNode(node)));
                    path.relationships().forEach(relationship -> graph.addNeo4jEdge(new Neo4jEdge(relationship)));
                }else if (type.name().equals(TypeConstructor.RELATIONSHIP.name())) {
                    Relationship relationship = value.asRelationship();
                    graph.addNeo4jEdge(new Neo4jEdge(relationship));
                }else {
                    log.error("目前不支持{}类型的查询数据解析。", type.name());
                }
            }
        }
        return graph;
    }

    public void addNeo4jNode(Neo4jNode neo4jNode) {
        nodes.add(neo4jNode);
    }

    public void addNeo4jEdge(Neo4jEdge neo4jEdge) {
        edges.add(neo4jEdge);
    }

}

5.Neo4jNode

package com.win.chaos.model.neo4j;

import lombok.Data;
import org.neo4j.driver.types.Node;

import java.util.*;

@Data
public class Neo4jNode {

    private Object id;

    private List<String> labels = new ArrayList<>();

    private Map<String, Object> props = new HashMap<>();

    public Neo4jNode(Node node) {
        id = node.id();
        parseLabels(node);
        parseProp(node);
    }


    public void parseLabels(Node node){
        if (node.labels() != null){
            node.labels().forEach(labels::add);
        }
    }

    public void parseProp(Node node) {
        Map<String, Object> propsMap = node.asMap();
        Set<String> keys = propsMap.keySet();
        for (String key : keys){
            props.put(key, propsMap.get(key));
        }
    }


    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Neo4jNode vertex = (Neo4jNode) o;
        return id.equals(vertex.id);
    }

    @Override
    public int hashCode() {
        return id.hashCode();
    }
}

6.Neo4jEdge

package com.win.chaos.model.neo4j;

import lombok.Data;
import org.neo4j.driver.types.Relationship;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;

@Data
public class Neo4jEdge {

    private long edgeId;

    private Object srcId;

    private Object dstId;

    private String label;

    private Map<String, Object> props = new HashMap<>();

    public Neo4jEdge(Relationship relationship) {
        srcId = relationship.startNodeId();
        dstId = relationship.endNodeId();
        edgeId = relationship.id();
        label = relationship.type();
        parseProps(relationship);
    }

    private void parseProps(Relationship relationship) {
        Map<String, Object> data = relationship.asMap();
        Set<String> keys = data.keySet();
        for (String key : keys) {
            props.put(key, data.get(key));
        }
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Neo4jEdge that = (Neo4jEdge) o;
        //if (!edgeId.equals(that.edgeId)) return false;
        if (!srcId.equals(that.srcId)) return false;
        if (!dstId.equals(that.dstId)) return false;
        return label.equals(that.label);
    }

    @Override
    public int hashCode() {
        int result = srcId.hashCode();
        result = 31 * result + dstId.hashCode();
        result = 31 * result + label.hashCode();
        result = 31 * result + (int) (edgeId ^ (edgeId >>> 32));
        return result;
    }
}


完整代码:springboot_neo4j_hanlp

参考

图数据库 Neo4j Java Api 的使用

  • 3
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值