SpringBoot该怎么使用Neo4j - 优化篇

news2024/12/26 21:18:20

文章目录

  • 前言
  • 实体工具
  • 使用

前言

上一篇中,我们的Cypher都用的是字符串,字符串拼接简单,但存在写错的风险,对于一些比较懒的开发者,甚至觉得之间写字符串还更自在快速,也确实,但如果在后期需要修改,如更高字段名或者一些级联的变动,会导致维护难,所以,这里这里我们模仿Mybatis-Plus写一个实体字段工具之间替换哪些字符串,以提高项目可维护性。

实体工具

我们以这篇的工具为基础进行下面的开发:Lambda表达式提取字段名-CSDN博客

然后我们再增加对于与Neo4j的实体工具:

  1. 实体缓存对象信息,保存实体必要信息

    public class EntityCache {
    
        private String className;
    
        private List<String> labels;
    
        private Map<String, String> fieldNameMap;
    
        public String getClassName() {
            return className;
        }
    
        public void setClassName(String className) {
            this.className = className;
        }
    
        public List<String> getLabels() {
            return labels;
        }
    
        public void setLabels(List<String> labels) {
            this.labels = labels;
        }
    
        public Map<String, String> getFieldNameMap() {
            return fieldNameMap;
        }
    
        public void setFieldNameMap(Map<String, String> fieldNameMap) {
            this.fieldNameMap = fieldNameMap;
        }
    }
    
    
  2. 解析工具,也就是反射解析

    public class EntityUtil {
    
        /**
         * 实体缓存
         */
        private static final Map<String, EntityCache> ENTITY_MAP = new ConcurrentHashMap<>();
    
        public static void main(String[] args) {
            Job job = new Job();
            String column = column(Job::getName);
            System.out.println(column);
            System.out.println(label(Job.class));
        }
    
        /**
         * 从lambda转换出字段名
         */
        public static <T, R> String column(CusFunction<T, R> column) {
            SerializedLambda resolve = LambdaUtils.resolve(column);
            return getColumn(LambdaUtils.getClass(resolve), LambdaUtils.getMethodName(resolve) , true);
        }
    
        /**
         * 从实体class解析出标签
         */
        public static <T> String label(Class<T> clazz) {
            EntityCache info = ENTITY_MAP.putIfAbsent(clazz.getTypeName(), resolve(clazz));
            return info.getLabels().get(0);
        }
    
        /**
         * 根据类型和方法名解析字段名
         * @param aClass 类型
         * @param methodName 方法名
         * @param dbField 是否数据库字段
         * @return 字段名
         */
        private static String getColumn(Class<?> aClass, String methodName, boolean dbField) {
            String fieldName = PropertyNamer.methodToProperty(methodName);
            if (!dbField) {
                return fieldName;
            }
            EntityCache info = resolve(aClass);
            if (!StringUtils.hasLength(fieldName)) {
                throw new RuntimeException(String.format("找不到实体对应的字段-[%s.%s]", aClass.getTypeName(), methodName));
            }
            Map<String, String> map = info.getFieldNameMap();
            return map.get(fieldName);
        }
    
        /**
         * 解析实体
         * @param clazz 类型
         * @return 实体缓存对象
         */
        public static <T> EntityCache resolve(Class<T> clazz) {
            String typeName = clazz.getTypeName();
            EntityCache info = ENTITY_MAP.get(typeName);
            if (info != null) {
                return info;
            }
            Field[] declaredFields = clazz.getDeclaredFields();
            Map<String, String> fieldNameMap = new HashMap<>();
            for (Field declaredField : declaredFields) {
                declaredField.setAccessible(true);
                String fieldName = declaredField.getName();
                String dbFieldName = fieldName;
                Property property = declaredField.getAnnotation(Property.class);
                if (property != null) {
                    if (StringUtils.hasLength(property.name())) {
                        dbFieldName = property.name();
                    }
                    if (StringUtils.hasLength(property.value())) {
                        dbFieldName = property.value();
                    }
                }
                fieldNameMap.put(fieldName, dbFieldName);
            }
            List<String> labelList = resolveLabel(clazz);
            info = new EntityCache();
            info.setLabels(labelList);
            info.setClassName(typeName);
            info.setFieldNameMap(fieldNameMap);
            ENTITY_MAP.put(typeName, info);
            return info;
        }
    
        /**
         * 解析标签
         * @param clazz 类型
         * @return 标签列表
         */
        private static <T> List<String> resolveLabel(Class<T> clazz) {
            Node node = clazz.getAnnotation(Node.class);
            if (node == null) {
                throw new RuntimeException("非数据库实体对象实体!");
            }
            String[] value = node.value();
            String[] labels = node.labels();
            List<String> result = new ArrayList<>();
            result.addAll(Arrays.asList(value));
            result.addAll(Arrays.asList(labels));
            if (result.isEmpty()) {
                result.add(clazz.getSimpleName());
            }
            return result;
        }
    }
    
    
  3. 测试:

        @Test
        public void testEntity() {
            String column = EntityUtil.column(Job::getName);
            EntityUtil.label(Job.class);
            String column1 = EntityUtil.column(Job::getJobName);
            System.out.println(column +"  " + column1);
        }
    

使用

实际开发中,就可以将字符串进行替换,如下面:

查询标签Jobname='liry',并通过id顺序排序,取第一个的cypher构建

// match(a:Job) where a.name='liry' return a order by a.id  limit 1

Node temp = Cypher.node("Job").named("a");
ResultStatement statement = Cypher.match(temp)
                                          .where(temp.property("name").isEqualTo(Cypher.anonParameter(job.getName())))
                                          .returning(temp.getRequiredSymbolicName())
                                          .orderBy(temp.property("id"))
                                          .limit(1)
                                          .build();

进行替换

// match(a:Job) where a.name='liry' return a order by a.id  limit 1

// 字符串 Job  -> EntityUtil.label(Job.class)
// 字符串 name -> EntityUtil.column(Job::getName)
// 字符串 id -> EntityUtil.column(Job::getId)
Node temp = Cypher.node(EntityUtil.label(Job.class)).named("a");
ResultStatement statement = Cypher.match(temp)
                                          .where(temp.property(EntityUtil.column(Job::getName)).isEqualTo(Cypher.anonParameter(job.getName())))
                                          .returning(temp.getRequiredSymbolicName())
                                          .orderBy(temp.property(EntityUtil.column(Job::getId)))
                                          .limit(1)
                                          .build();

创建一个节点:

create (a:Job{name:'liry',jobName:'liry-job'}) return a;          
               

可以看到调试中构建的Cypher是正确的。

image-20241204181636501

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2254358.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【Calibre-Web】Calibre-Web服务器安装详细步骤(个人搭建自用的电子书网站,docker-compose安装)

文章目录 一、Calibre-Web和Calibre的区别是什么&#xff1f;使用场景分别是什么&#xff1f;二、服务器安装docker和docker-compose三、服务器安装Calibre-Web步骤1、安装完成后的目录结构2、安装步骤3、初始配置4、启动上传 四、安装Calibre五、docker-compose常用命令 最近想…

easyexcel 导出日期格式化

1.旧版本 在新的版本中formate已经被打上废弃标记。那么不推荐使用这种方式。 2.推荐方式 推荐使用另外一种方式【 Converter 】代码如下&#xff0c;例如需要格式化到毫秒【yyyy-MM-dd HH:mm:ss SSS】级别 创建一个公共Converter import com.alibaba.excel.converters.Conv…

ABAP - 系统集成之SAP的数据同步到OA(泛微E9)服务器数据库

需求背景 项目经理说每次OA下单都需要调用一次SAP的接口获取数据&#xff0c;导致效率太慢了&#xff0c;能否把SAP的数据保存到OA的数据库表里&#xff0c;这样OA可以直接从数据库表里获取数据效率快很多。思来想去&#xff0c;提供了两个方案。 在集群SAP节点下增加一个SQL S…

40分钟学 Go 语言高并发:【实战】分布式缓存系统

【实战课程】分布式缓存系统 一、整体架构设计 首先&#xff0c;让我们通过架构图了解分布式缓存系统的整体设计&#xff1a; 核心组件 组件名称功能描述技术选型负载均衡层请求分发、节点选择一致性哈希缓存节点数据存储、过期处理内存存储 持久化同步机制节点间数据同步…

w~视觉~合集27

我自己的原文哦~ https://blog.51cto.com/whaosoft/12715639 #视频AIGC~论文 1、Pix2Video: Video Editing using Image Diffusion 基于大规模图像库训练的图像扩散模型已成为质量和多样性方面最为通用的图像生成模型。它们支持反转真实图像和条件生成&#xff08;例如&…

MYSQL中的增删改查操作(如果想知道MYSQL中有关增删改查操作的知识,那么只看这一篇就足够了!)

前言&#xff1a;在 MySQL 中&#xff0c;增、删、改、查&#xff08;CRUD&#xff09;操作是基本的数据库操作&#xff0c;增操作&#xff08;INSERT&#xff09;用于插入数据&#xff0c;删操作&#xff08;DELETE&#xff09;用于删除数据&#xff0c;改操作&#xff08;UPD…

Ansible的yum和saltstack的哪个功能相似

Ansible的yum和saltstack的哪个功能相似 在 Ansible 和 SaltStack 中&#xff0c;Ansible 的 yum 模块 和 SaltStack 的 pkg 模块 功能相似。它们都用于管理软件包&#xff0c;支持安装、升级、删除和查询等操作。 Ansible 的 yum 模块 用途&#xff1a; 专门用于基于 Red Hat …

在做题中学习(76):颜色分类

解法&#xff1a;三指针 思路&#xff1a;用三个指针&#xff0c;把数组划分为三个区域&#xff1a; for循环遍历数组&#xff0c;i遍历数组&#xff0c;left是0区间的末尾&#xff0c;right是2区间的开头&#xff0c;0 1 2区间成功被划分 而上面的图画是最终实现的图样&…

Java版-速通数据结构-树基础知识

现在面试问mysql,红黑树好像都是必备问题了。动不动就让手写红黑树或者简单介绍下红黑树。然而&#xff0c;我们如果直接去看红黑树&#xff0c;可能会一下子蒙了。在看红黑树之前&#xff0c;需要先了解下树的基础知识&#xff0c;从简单到复杂&#xff0c;看看红黑树是在什么…

浙江工业大学《2024年828自动控制原理真题》 (完整版)

本文内容&#xff0c;全部选自自动化考研联盟的&#xff1a;《浙江工业大学828自控考研资料》的真题篇。后续会持续更新更多学校&#xff0c;更多年份的真题&#xff0c;记得关注哦~ 目录 2024年真题 Part1&#xff1a;2024年完整版真题 2024年真题

【计算机网络】实验11:边界网关协议BGP

实验11 边界网关协议BGP 一、实验目的 本次实验旨在验证边界网关协议&#xff08;BGP&#xff09;的实际作用&#xff0c;并深入学习在路由器上配置和使用BGP协议的方法。通过实验&#xff0c;我将探索BGP在不同自治系统之间的路由选择和信息交换的功能&#xff0c;理解其在互…

微信小程序全屏显示地图

微信小程序在界面上显示地图&#xff0c;只需要用map标签 <map longitude"经度度数" latitude"纬度度数"></map>例如北京的经纬度为&#xff1a;116.407004,39.904595 <map class"bgMap" longitude"116.407004" lati…

InfluxDB 集成 Grafana

将InfluxDB集成到Grafana进行详细配置通常包括以下几个步骤&#xff1a;安装与配置InfluxDB、安装与配置Grafana、在Grafana中添加InfluxDB数据源以及创建和配置仪表板。以下是一个详细的配置指南&#xff1a; 一、安装与配置InfluxDB 下载与安装&#xff1a; 从InfluxDB的官…

【AI系统】ESPNet 系列

ESPNet 系列 本文将会介绍 ESPNet 系列&#xff0c;该网络主要应用在高分辨率图像下的语义分割&#xff0c;在计算内存占用、功耗方面都非常高效&#xff0c;重点介绍一种高效的空间金字塔卷积模块&#xff08;ESP Module&#xff09;&#xff1b;而在 ESPNet V2 上则是会更进…

【Axios】如何在Vue中使用Axios请求拦截器

✨✨ 欢迎大家来到景天科技苑✨✨ &#x1f388;&#x1f388; 养成好习惯&#xff0c;先赞后看哦~&#x1f388;&#x1f388; &#x1f3c6; 作者简介&#xff1a;景天科技苑 &#x1f3c6;《头衔》&#xff1a;大厂架构师&#xff0c;华为云开发者社区专家博主&#xff0c;…

w~深度学习~合集1

我自己的原文哦~ https://blog.51cto.com/whaosoft/12663254 #Motion Plan 代码 github.com/liangwq/robot_motion_planing 轨迹约束中的软硬约束 前面的几篇文章已经介绍了&#xff0c;轨迹约束的本质就是在做带约束的轨迹拟合。输入就是waypoint点list&#xff0c;约束…

大语言模型应用开发框架LangChain

大语言模型应用开发框架LangChain 一、LangChain项目介绍1、简介2、LangChain的价值3、实战演练 二、LangChain提示词大语言模型应用1、简介1.1、提示词模板化的优点1.2、提示词模板LLM 的应用1.3、Prompt 2、应用实战2.1、PromptTemplate LLM2.2、PromptTemplate LLM Outpu…

公众号文章标题的重要性

标题&#xff0c;不仅仅是一个简单的标题&#xff0c;它更是吸引读者眼球的“颜值担当”。 信息爆炸的今天&#xff0c;一个好的标题就是打开流量之门的金钥匙。那么&#xff0c;如何衡量一个标题的“颜值”呢&#xff1f;我们可以从两个维度来看&#xff1a;打开率和传播率。…

116. UE5 GAS RPG 实现击杀掉落战利品功能

这一篇&#xff0c;我们实现敌人被击败后&#xff0c;掉落战利品的功能。首先&#xff0c;我们将创建一个新的结构体&#xff0c;用于定义掉落体的内容&#xff0c;方便我们设置掉落物。然后&#xff0c;我们实现敌人死亡时的掉落函数&#xff0c;并在蓝图里实现对应的逻辑&…

ros2人脸检测

第一步&#xff1a; 首先在工作空间/src下创建数据结构目录service_interfaces ros2 pkg create service_interfaces --build-type ament_cmake 然后再创建一个srv目录 在里面创建FaceDetect.srv&#xff08;注意&#xff0c;首字母要大写&#xff09; sensor_msgs/Image …