当前位置: 首页 > news >正文

Neo4j入门第二期(Spring Data Neo4j的使用)

强烈建议先看第一期:

Neo4j入门第一期(Cypher入门)-CSDN博客

SDN快速入门

Spring Data Neo4j简称SDN,是Spring对Neo4j数据库操作的封装,其底层基于neo4j-java-driver实现。

创建工程 

<?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><parent><groupId>com.sl-express</groupId><artifactId>sl-express-parent</artifactId><version>1.4</version></parent><groupId>com.sl-express.sdn</groupId><artifactId>sl-express-sdn</artifactId><version>1.0-SNAPSHOT</version><properties><maven.compiler.source>11</maven.compiler.source><maven.compiler.target>11</maven.compiler.target><sl-express-common.version>1.2-SNAPSHOT</sl-express-common.version></properties><dependencies><dependency><groupId>com.sl-express.common</groupId><artifactId>sl-express-common</artifactId><version>${sl-express-common.version}</version></dependency><!--SDN依赖--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-neo4j</artifactId></dependency></dependencies></project>

基础代码

SDNApplication

编写启动类:

package com.sl.sdn;import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;@SpringBootApplication
public class SDNApplication {public static void main(String[] args) {SpringApplication.run(SDNApplication.class, args);}
}

Entity

编写实体,在物流中,会存在网点、二级转运中心、一级转运中心,我们分别用Agency、TLT、OLT表示。

由于以上三个机构的属性是相同的,但在Neo4j中的标签是不一样的,所以既要保证不同的类,也有相同的属性,这种场景比较适合将属性写到父类中,自己继承父类来实现,这里我们采用抽象类的来实现。

package com.sl.sdn.entity.node;import com.sl.sdn.enums.OrganTypeEnum;
import io.swagger.annotations.ApiModelProperty;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.experimental.SuperBuilder;
import org.springframework.data.geo.Point;
import org.springframework.data.neo4j.core.schema.GeneratedValue;
import org.springframework.data.neo4j.core.schema.Id;@Data
@SuperBuilder(toBuilder = true)
@NoArgsConstructor
@AllArgsConstructor
public abstract class BaseEntity {@Id@GeneratedValue@ApiModelProperty(value = "Neo4j ID", hidden = true)private Long id;@ApiModelProperty(value = "业务id", required = true)private Long bid;@ApiModelProperty(value = "名称", required = true)private String name;@ApiModelProperty(value = "电话", required = true)private String phone;@ApiModelProperty(value = "地址", required = true)private String address;@ApiModelProperty(value = "位置坐标, x: 纬度,y: 经度", required = true)private Point location;//机构类型public abstract OrganTypeEnum getAgencyType();}

 机构枚举:

package com.sl.sdn.enums;import cn.hutool.core.util.EnumUtil;
import com.sl.transport.common.enums.BaseEnum;/*** 机构类型枚举*/
public enum OrganTypeEnum implements BaseEnum {OLT(1, "一级转运中心"),TLT(2, "二级转运中心"),AGENCY(3, "网点");/*** 类型编码*/private final Integer code;/*** 类型值*/private final String value;OrganTypeEnum(Integer code, String value) {this.code = code;this.value = value;}public Integer getCode() {return code;}public String getValue() {return value;}public static OrganTypeEnum codeOf(Integer code) {return EnumUtil.getBy(OrganTypeEnum::getCode, code);}
}
package com.sl.sdn.entity.node;import com.sl.sdn.enums.OrganTypeEnum;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.ToString;
import lombok.experimental.SuperBuilder;
import org.springframework.data.neo4j.core.schema.Node;/*** 网点实体*/
@Node("AGENCY")
@Data
@ToString(callSuper = true)
@SuperBuilder(toBuilder = true)
@NoArgsConstructor
public class AgencyEntity extends BaseEntity {@Overridepublic OrganTypeEnum getAgencyType() {return OrganTypeEnum.AGENCY;}
}
package com.sl.sdn.entity.node;import com.sl.sdn.enums.OrganTypeEnum;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.ToString;
import lombok.experimental.SuperBuilder;
import org.springframework.data.neo4j.core.schema.Node;/*** 一级转运中心实体 (OneLevelTransportEntity)*/
@Node("OLT")
@Data
@ToString(callSuper = true)
@SuperBuilder(toBuilder = true)
@NoArgsConstructor
public class OLTEntity extends BaseEntity {@Overridepublic OrganTypeEnum getAgencyType() {return OrganTypeEnum.OLT;}
}
package com.sl.sdn.entity.node;import com.sl.sdn.enums.OrganTypeEnum;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.ToString;
import lombok.experimental.SuperBuilder;
import org.springframework.data.neo4j.core.schema.Node;/*** 二级转运中心实体(TwoLevelTransportEntity)*/
@Node("TLT")
@Data
@ToString(callSuper = true)
@SuperBuilder(toBuilder = true)
@NoArgsConstructor
public class TLTEntity extends BaseEntity {@Overridepublic OrganTypeEnum getAgencyType() {return OrganTypeEnum.TLT;}
}
package com.sl.sdn.entity.line;import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;/*** 运输路线实体*/
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class TransportLine {private Long id;private Double cost; //成本}

DTO

DTO用于服务间的数据传输,会用到OrganDTOTransportLineNodeDTO

package com.sl.sdn.dto;import cn.hutool.core.annotation.Alias;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;import javax.validation.constraints.NotNull;/*** 机构数据对象,网点、一级转运、二级转运都是看作是机构* BaseEntity中的location无法序列化,需要将经纬度拆开封装对象*/
@Data
public class OrganDTO {@Alias("bid") //业务id作为id进行封装@ApiModelProperty(value = "机构id", required = true)private Long id;@ApiModelProperty(value = "名称", required = true)private String name;@ApiModelProperty(value = "类型,1:一级转运,2:二级转运,3:网点", required = true)private Integer type;@ApiModelProperty(value = "电话", required = true)private String phone;@ApiModelProperty(value = "地址", required = true)private String address;@ApiModelProperty(value = "纬度", required = true)private Double latitude;@ApiModelProperty(value = "经度", required = true)private Double longitude;}
package com.sl.sdn.dto;import io.swagger.annotations.ApiModelProperty;
import lombok.Data;import java.util.ArrayList;
import java.util.List;/*** 运输路线对象*/
@Data
public class TransportLineNodeDTO {@ApiModelProperty(value = "节点列表", required = true)private List<OrganDTO> nodeList = new ArrayList<>();@ApiModelProperty(value = "路线成本", required = true)private Double cost = 0d;}

Repository

SDN也是遵循了Spring Data规范,同时也提供了Neo4jRepository,该接口中提供了基本的CRUD操作,我们定义Repository需要继承该接口。

package com.sl.sdn.repository;import com.sl.sdn.entity.node.AgencyEntity;
import org.springframework.data.neo4j.repository.Neo4jRepository;/*** 网点操作*/
public interface AgencyRepository extends Neo4jRepository<AgencyEntity, Long> {/*** 根据bid查询** @param bid 业务id* @return 网点数据*/AgencyEntity findByBid(Long bid);/*** 根据bid删除** @param bid 业务id* @return 删除的数据条数*/Long deleteByBid(Long bid);}

OLTRepository

package com.sl.sdn.repository;import com.sl.sdn.entity.node.OLTEntity;
import org.springframework.data.neo4j.repository.Neo4jRepository;/*** 一级转运中心数据操作*/
public interface OLTRepository extends Neo4jRepository<OLTEntity, Long> {/*** 根据bid查询** @param bid 业务id* @return 一级转运中心数据*/OLTEntity findByBid(Long bid);/*** 根据bid删除** @param bid 业务id* @return 删除的数据条数*/Long deleteByBid(Long bid);}

复杂查询

通过继承Neo4jRepository实现简单的查询是非常方便的,如果要实现复杂的查询就需要定义Cypher查询实现了,需要通过Neo4jClient进行查询操作,下面我们以查询两个网点间最短运输路线为例进行查询。

定义Repository 
package com.sl.sdn.repository;import com.sl.sdn.dto.TransportLineNodeDTO;
import com.sl.sdn.entity.node.AgencyEntity;/*** 运输路线相关操作*/
public interface TransportLineRepository {/*** 查询两个网点之间最短的路线,查询深度为:10** @param start 开始网点* @param end   结束网点* @return 路线*/TransportLineNodeDTO findShortestPath(AgencyEntity start, AgencyEntity end);}
编写实现
package com.sl.sdn.repository.impl;import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.stream.StreamUtil;
import cn.hutool.core.util.NumberUtil;
import cn.hutool.core.util.StrUtil;
import com.sl.sdn.dto.OrganDTO;
import com.sl.sdn.dto.TransportLineNodeDTO;
import com.sl.sdn.entity.node.AgencyEntity;
import com.sl.sdn.enums.OrganTypeEnum;
import com.sl.sdn.repository.TransportLineRepository;
import org.neo4j.driver.internal.value.PathValue;
import org.neo4j.driver.types.Path;
import org.springframework.data.neo4j.core.Neo4jClient;
import org.springframework.data.neo4j.core.schema.Node;
import org.springframework.stereotype.Component;import javax.annotation.Resource;
import java.util.List;
import java.util.Map;
import java.util.Optional;@Component // 将该类注册为 Spring Bean
public class TransportLineRepositoryImpl implements TransportLineRepository {@Resource // 注入 Neo4j 客户端用于执行 Cypher 查询private Neo4jClient neo4jClient;/*** 查找从一个网点到另一个网点的最短路径。** @param start 起始网点实体* @param end   目标网点实体* @return 包含最短路径信息的 DTO 对象*/@Overridepublic TransportLineNodeDTO findShortestPath(AgencyEntity start, AgencyEntity end) {// 获取网点数据在 Neo4j 中的标签名称(类型)String type = AgencyEntity.class.getAnnotation(Node.class).value()[0];// 构造 Cypher 查询语句,寻找从起点到终点的最短路径String cypherQuery = StrUtil.format("MATCH path = shortestPath((start:{}) -[*..10]-> (end:{}))\n" +"WHERE start.bid = $startId AND end.bid = $endId \n" +"RETURN path", type, type);// 执行查询,并处理结果Optional<TransportLineNodeDTO> optional = this.neo4jClient.query(cypherQuery).bind(start.getBid()).to("startId") // 绑定起始节点 ID 到查询参数 'startId'.bind(end.getBid()).to("endId") // 绑定目标节点 ID 到查询参数 'endId'.fetchAs(TransportLineNodeDTO.class) // 设置返回数据类型为 TransportLineNodeDTO.mappedBy((typeSystem, record) -> { // 自定义映射逻辑,将记录转换为 DTO 对象PathValue pathValue = (PathValue) record.get(0); // 从记录中获取路径对象Path path = pathValue.asPath(); // 将 PathValue 转换为 Path 对象// 创建 TransportLineNodeDTO 实例TransportLineNodeDTO dto = new TransportLineNodeDTO();// 处理路径中的节点,将其转换为 OrganDTO 列表List<OrganDTO> nodeList = StreamUtil.of(path.nodes()) // 遍历路径中的所有节点.map(node -> {Map<String, Object> map = node.asMap(); // 将节点转换为 MapOrganDTO organDTO = BeanUtil.toBeanIgnoreError(map, OrganDTO.class); // 将 Map 转换为 OrganDTO// 取第一个标签作为类型OrganTypeEnum organTypeEnum = OrganTypeEnum.valueOf(CollUtil.getFirst(node.labels()));organDTO.setType(organTypeEnum.getCode()); // 设置类型代码// 查询出来的数据,x:经度,y:纬度organDTO.setLatitude(BeanUtil.getProperty(map.get("location"), "y")); // 设置纬度organDTO.setLongitude(BeanUtil.getProperty(map.get("location"), "x")); // 设置经度return organDTO; // 返回 OrganDTO 实例}).collect(Collectors.toList()); // 收集所有 OrganDTO 实例到列表中dto.setNodeList(nodeList); // 设置节点列表到 DTO 对象// 提取关系中的 cost 数据,进行求和计算,算出该路线的总成本double cost = StreamUtil.of(path.relationships()) // 遍历路径中的所有关系.mapToDouble(relationship -> {Map<String, Object> objectMap = relationship.asMap(); // 将关系转换为 Mapreturn Convert.toDouble(objectMap.get("cost"), 0d); // 获取 cost 值并转换为 double,如果不存在则默认为 0}).sum(); // 计算所有关系的 cost 总和dto.setCost(cost); // 设置总成本到 DTO 对象// 取2位小数dto.setCost(NumberUtil.round(dto.getCost(), 2).doubleValue());return dto; // 返回最终的 DTO 对象}).one(); // 获取单个结果return optional.orElse(null); // 如果没有找到结果,则返回 null}
}

相关文章:

  • 02-jenkins学习之旅-基础配置
  • Android计算机网络学习总结
  • Flink初始及搭建集群环境(技术选型与实战详解)
  • 音视频之视频压缩及数字视频基础概念
  • 【AI大模型研究报告】2024年中国工业大模型行业发展研究报告
  • 【信息系统项目管理师】第20章:高级项目管理 - 28个经典题目及详解
  • 2025 ICPC 南昌全国邀请赛暨江西省赛(8题题解)
  • PCB行业标准与专利竞争:高频材料与工艺壁垒
  • 什么是下一代DNS
  • 虚拟机下ubuntu分区挂载实验
  • B2C商城架构对比:ZKmall模板商城为何选择 Spring Cloud
  • C++ vector 深度解析:从原理到实战的全方位指南
  • 211. 添加与搜索单词 - 数据结构设计
  • Axure高保真CRM客户关系管理系统原型
  • Vue常用自定义指令-积累的魅力【VUE】
  • 如何备份和恢复Linux系统?
  • 生产环境CPU飙升问题排查与优化实战
  • 超越感官的实相:声、光、气味的科学与哲学探微
  • 文章记单词 | 第103篇(六级)
  • MYSQL优化(1)
  • 品牌建设新时代/利于seo的建站系统有哪些
  • 电子商务官方网站/百度网络优化推广公司
  • 网站建设合同 完整版/东莞网站建设市场
  • 如何在网站做旅游产品/广州营销seo
  • 西宁网站建设君博首选/如何让自己的网站排名靠前
  • 海阔天空网站建设/如何制作网页广告