小工具知识网

当前位置: 首页 > 行业知识 > 图数据库Neo4j——SpringBoot使用Neo4j & 简单增删改查 & 复杂查询初步

行业知识

图数据库Neo4j——SpringBoot使用Neo4j & 简单增删改查 & 复杂查询初步

2026-11-03 10:12:09 来源:互联网转载

前言


图形数据库是专门用于存储图形数据的数据库,它使用图形模型来存储数据,并且支持复杂的图形查询。常见的图形数据库有Neo4j、OrientDB等。

Neo4j是用Java实现的开源NoSQL图数据库,本篇博客介绍如何在SpringBoot中使用Neo4j图数据库,如何进行简单的增删改查,以及如何进行复杂的查询。

本篇博客相关代码的git网址如下:

https://gitee.com/pet365/spring-boot-neo4j

关于Neo4j的博客文章如下:

  • 图数据库Neo4j——Neo4j简介、数据结构 & Docker版本的部署安装 & Cypher语句的入门

    目录

    • 前言
    • 引出
    • springBoot整合
      • 1、引入依赖
      • 2、配置文件
      • 3、实体类定义
      • 4、dao继承Neo4jRepository
      • 复杂查询
        • 最短路径查询
        • 最小成本查询
        • 总结

          引出


          1.Neo4j是用Java实现的开源NoSQL图数据库;

          2.SpringBoot使用Neo4j,继承Neo4jRepository进行简单增删改查;

          3.使用Neo4jClient进行复杂的查询;

          springBoot整合

          1、引入依赖

                              org.springframework.boot            spring-boot-starter-data-neo4j        

          2、配置文件

          server:  port: 9902logging:  level:    org.springframework.data.neo4j: debugspring:  application:    name: spring-neo4j  data:    neo4j:      database: neo4j  neo4j:    authentication:      username: neo4j      password: neo4j123    uri: neo4j://192.168.150.101:7687

          3、实体类定义

          提取抽象类

          不同的节点类,网点、一级转运中心、二级转运中心

          4、dao继承Neo4jRepository

          进行自定义查询:

          KeywordSampleCypher snippet
          AfterfindByLaunchDateAfter(Date date)n.launchDate > date
          BeforefindByLaunchDateBefore(Date date)n.launchDate
          Containing (String)findByNameContaining(String namePart)n.name CONTAINS namePart
          Containing (Collection)findByEmailAddressesContains(Collection addresses) findByEmailAddressesContains(String address)ANY(collectionFields IN [addresses] WHERE collectionFields in n.emailAddresses) ANY(collectionFields IN address WHERE collectionFields in n.emailAddresses)
          InfindByNameIn(Iterable names)n.name IN names
          BetweenfindByScoreBetween(double min, double max) findByScoreBetween(Range range)n.score >= min AND n.score = min AND n.score min AND n.score
          StartingWithfindByNameStartingWith(String nameStart)n.name STARTS WITH nameStart
          EndingWithfindByNameEndingWith(String nameEnd)n.name ENDS WITH nameEnd
          ExistsfindByNameExists()EXISTS(n.name)
          TruefindByActivatedIsTrue()n.activated = true
          FalsefindByActivatedIsFalse()NOT(n.activated = true)
          IsfindByNameIs(String name)n.name = name
          NotNullfindByNameNotNull()NOT(n.name IS NULL)
          NullfindByNameNull()n.name IS NULL
          GreaterThanfindByScoreGreaterThan(double score)n.score > score
          GreaterThanEqualfindByScoreGreaterThanEqual(double score)n.score >= score
          LessThanfindByScoreLessThan(double score)n.score
          LessThanEqualfindByScoreLessThanEqual(double score)n.score /** * 根据bid 查询 * @param bid 业务id * @return 网点数据 */ AgencyEntity findByBid(Long bid); /** * 根据bid删除 * * @param bid 业务id * @return 删除的数据条数 */ Long deleteByBid(Long bid);}pimg src="https://m.xiaogongju.com.cn/uploads/allimg/20250421/-250421202943D8.png" //p h3复杂查询/h3 pimg src="https://m.xiaogongju.com.cn/uploads/allimg/20250421/-250421202944X5.png" //p h4最短路径查询/h4 pre //查询两个网点之间最短路径,查询深度最大为10MATCH path = shortestPath((n:AGENCY) -[*..10]->(m:AGENCY))WHERE n.name = "北京市昌平区定泗路" AND m.name = "上海市浦东新区南汇"RETURN path
          package com.tianju.mapper.impl;import cn.hutool.core.bean.BeanUtil;import cn.hutool.core.collection.CollUtil;import cn.hutool.core.map.MapUtil;import cn.hutool.core.util.StrUtil;import cn.hutool.db.meta.Column;import com.tianju.dto.OrganDTO;import com.tianju.dto.TransportLineNodeDTO;import com.tianju.entity.AgencyEntity;import com.tianju.enums.OrganTypeEnum;import com.tianju.mapper.TransportLineRepository;import org.neo4j.driver.internal.InternalPoint2D;import org.neo4j.driver.types.Path;import org.neo4j.driver.types.Relationship;import org.springframework.data.neo4j.core.schema.Node;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.data.neo4j.core.Neo4jClient;import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;import org.springframework.stereotype.Component;import java.util.Map;import java.util.Optional;@Componentpublic class TransportLineRepositoryImpl implements TransportLineRepository {    @Autowired    private Neo4jClient neo4jClient;    /**     * 查询最短路线     * @param start 开始网点     * @param end   结束网点     * @return     */    @Override    public TransportLineNodeDTO findShortestPath(AgencyEntity start, AgencyEntity end) {        // 获取网点数据在Neo4j中的类型 @Node("AGENCY") @Node("OLT")        String type = AgencyEntity.class.getAnnotation(Node.class).value()[0];        // 构造Sql语句 $startId//        String cql = "MATCH path = shortestPath((n:AGENCY) -[*..10]->(m:AGENCY))\n" +//                "WHERE n.bid = $startId AND m.bid = $endId\n" +//                "RETURN path";        String cql = StrUtil.format("MATCH path = shortestPath((n:{}) -[*..10]->(m:{})) " +                "WHERE n.bid = $startId AND m.bid = $endId " +                "RETURN path",type,type);        // 执行自定义查询        Neo4jClient.RecordFetchSpec recordFetchSpec = neo4jClient.query(cql)                .bind(start.getBid()).to("startId") // 替换 $startId                .bind(end.getBid()).to("endId")  // 替换 $endId                .fetchAs(TransportLineNodeDTO.class)   // 设置响应类型,指定为 TransportLineNodeDTO 类型                .mappedBy((typeSystem, record) -> {    // 设置结果集映射                    Path path = record.get(0).asPath();// 得到第一条路线                    TransportLineNodeDTO transportLineNodeDTO = new TransportLineNodeDTO();                    path.nodes().forEach(node -> { // 将每个节点信息封装成一个 OrganDto                        // 获得节点的 键值对 address: 上海市转运中心;bid:8002                        Map map = node.asMap();                        // {name=北京市昌平区定泗路,                        // location=Point{srid=4326, x=116.37212849638287, y=40.11765281246394},                        // address=北七家镇定泗路苍龙街交叉口, bid=100280, phone=010-86392987}                        System.out.println("map: "+map);                        // 把键值对转换成对象 OrganDTO                        OrganDTO organDTO = BeanUtil.toBeanIgnoreError(map, OrganDTO.class);                        // organDTO:                        // OrganDTO(id=100280, name=北京市昌平区定泗路, type=null, phone=010-86392987,                        // address=北七家镇定泗路苍龙街交叉口, latitude=null, longitude=null)                        // type,latitude,longitude 没有映射成功                        System.out.println("organDTO: "+organDTO);                        // 获得标签的名称 OLT,TLT,                        String first = CollUtil.getFirst(node.labels());                        // 根据OLT获得枚举类型 OLT(1, "一级转运中心"),                        OrganTypeEnum organTypeEnum = OrganTypeEnum.valueOf(first);                        // 再获得枚举类型的 code :1、2、3                        organDTO.setType(organTypeEnum.getCode()); // 设置类型的映射                        // 经纬度 "location": point({srid:4326, x:121.59815370294322, y:31.132409729356993})                        InternalPoint2D location = MapUtil.get(map, "location", InternalPoint2D.class); // 经纬度 BeanUtil.getProperty(map.get("location"),"x");                        organDTO.setLatitude(location.x());  // 设置经纬度映射                        organDTO.setLongitude(location.y()); // 经纬度映射                        // OrganDTO(id=100280, name=北京市昌平区定泗路, type=3,                        // phone=010-86392987, address=北七家镇定泗路苍龙街交叉口,                        // latitude=116.37212849638287, longitude=40.11765281246394)                        System.out.println("organDTO: "+organDTO);                        transportLineNodeDTO.getNodeList().add(organDTO);                    });                    System.out.println("transportLineNodeDTO: "+transportLineNodeDTO);                    path.relationships().forEach(relationship -> {                        // 路径下面的关系                        Map map = relationship.asMap();                        Double cost = MapUtil.get(map, "cost", Double.class);                        transportLineNodeDTO.setCost(cost + transportLineNodeDTO.getCost());                    });                    System.out.println("transportLineNodeDTO: "+transportLineNodeDTO);                    return transportLineNodeDTO;                });        Optional one = recordFetchSpec.one(); // Optional,1.8提供的,可以处理null的情况        return one.orElse(null); // 如果为null,就返回null,如果不是null,就返回结果    }}

          最小成本查询

          package com.tianju.mapper.impl;import cn.hutool.core.bean.BeanUtil;import cn.hutool.core.collection.CollUtil;import cn.hutool.core.map.MapUtil;import cn.hutool.core.util.StrUtil;import cn.hutool.db.meta.Column;import com.tianju.dto.OrganDTO;import com.tianju.dto.TransportLineNodeDTO;import com.tianju.entity.AgencyEntity;import com.tianju.enums.OrganTypeEnum;import com.tianju.mapper.TransportLineRepository;import org.neo4j.driver.internal.InternalPoint2D;import org.neo4j.driver.types.Path;import org.neo4j.driver.types.Relationship;import org.springframework.data.neo4j.core.schema.Node;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.data.neo4j.core.Neo4jClient;import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;import org.springframework.stereotype.Component;import java.util.Map;import java.util.Optional;@Componentpublic class TransportLineRepositoryImpl implements TransportLineRepository {    @Autowired    private Neo4jClient neo4jClient;    @Override    public TransportLineNodeDTO findCostLeastPath(AgencyEntity start, AgencyEntity end) {        String type = AgencyEntity.class.getAnnotation(Node.class).value()[0];        String cqlB = "MATCH path = (n:{}) -[*..10]->(m:{}) " +                "WHERE n.bid = $startId AND m.bid = $endId " +                "UNWIND relationships(path) AS r " +                "WITH sum(r.cost) AS cost, path " +                "RETURN path ORDER BY cost ASC, LENGTH(path) ASC LIMIT 1";        String cql = StrUtil.format(cqlB, type,type);        Optional one = neo4jClient.query(cql)                .bind(start.getBid()).to("startId")                .bind(end.getBid()).to("endId")                .fetchAs(TransportLineNodeDTO.class)                .mappedBy(((typeSystem, record) -> {                    Path path = record.get(0).asPath();                    TransportLineNodeDTO transportLineNodeDTO = new TransportLineNodeDTO();                    path.nodes().forEach(node -> {                        Map map = node.asMap();                        OrganDTO organDTO = BeanUtil.toBeanIgnoreError(map, OrganDTO.class);                        // 获得标签的名称 OLT,TLT,                        String first = CollUtil.getFirst(node.labels());                        // 根据OLT获得枚举类型 OLT(1, "一级转运中心"),                        OrganTypeEnum organTypeEnum = OrganTypeEnum.valueOf(first);                        // 再获得枚举类型的 code :1、2、3                        organDTO.setType(organTypeEnum.getCode()); // 设置类型的映射                        // 经纬度 "location": point({srid:4326, x:121.59815370294322, y:31.132409729356993})                        InternalPoint2D location = MapUtil.get(map, "location", InternalPoint2D.class); // 经纬度 BeanUtil.getProperty(map.get("location"),"x");                        organDTO.setLatitude(location.x());  // 设置经纬度映射                        organDTO.setLongitude(location.y()); // 经纬度映射                        transportLineNodeDTO.getNodeList().add(organDTO);                    });                    path.relationships().forEach(relationship -> {                        // 路径下面的关系                        Map map = relationship.asMap();                        Double cost = MapUtil.get(map, "cost", Double.class);                        transportLineNodeDTO.setCost(cost + transportLineNodeDTO.getCost());                    });                    return transportLineNodeDTO;                })).one();        return one.orElse(null);    }    private void findShortestPathMy(){        String cql = "MATCH path = shortestPath((n:AGENCY) -[*..10]->(m:AGENCY)) " +                "WHERE n.bid = 210127 AND m.bid = 100260 " +                "RETURN path";        // 执行自定义查询        Neo4jClient.UnboundRunnableSpec query = neo4jClient.query(cql);        ThreadPoolTaskExecutor threadPoolTaskExecutor = new ThreadPoolTaskExecutor();    }}

          总结

          1.Neo4j是用Java实现的开源NoSQL图数据库;

          2.SpringBoot使用Neo4j,继承Neo4jRepository进行简单增删改查;

          3.使用Neo4jClient进行复杂的查询;

上一篇:发件服务器怎么配置?

下一篇:宝塔面板源码上传与后台操作指南全解析