自学内容网 自学内容网

SpringBoot该怎么使用Neo4j - 优化篇

前言

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

实体工具

我们以这篇的工具为基础进行下面的开发: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


原文地址:https://blog.csdn.net/qq_28911061/article/details/144246948

免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!