• Java初始化大量数据到Neo4j中(一)


    背景:我们项目第一次部署图数据库,要求我们把现有的业务数据以及关系上线第一时间初始化到Neo4j中。开发环境数据量已经百万级别。生成环境数据量更多。

    我刚开始开发的时候,由于对Neo4j的了解并没有很多,第一想到的是用代码通用组装create语句进行创建节点以及关系。

    业务说明:系统中有很多实体表,每个实体表中有自己的数据,不同实体有一张关系表进行维护。

    我开发的思路是:1.先将所有的表中数据取出来做为节点 2.根据关系表将这个数据的关系查出来之后组装语句将数据添加到Neo4j中。

    具体代码如下(Springboot项目版本2.2.5RELEASE):
    pom.xml

    <dependency>
                <groupId>org.springframework.bootgroupId>
                <artifactId>spring-boot-starter-data-neo4jartifactId>
            dependency>
    
    • 1
    • 2
    • 3
    • 4

    配置文件进行下面配置:

    spring:
       data:
          neo4j:
             uri: bolt://localhost:7687
             username: neo4j
             password: neo4j
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    使用Java代码组装CQL语句,用原生session进行
    Neo4jConfig.java

    @Configuration
    public class Neo4jConfig {
    
        @Value("${spring.data.neo4j.uri}")
        private String uri;
        @Value("${spring.data.neo4j.username}")
        private String userName;
        @Value("${spring.data.neo4j.password}")
        private String password;
    
        @Bean
        public org.neo4j.ogm.config.Configuration getConfiguration() {
            org.neo4j.ogm.config.Configuration configuration = new org.neo4j.ogm.config.Configuration.
                    Builder().uri(uri).connectionPoolSize(100).credentials(userName, password).withBasePackages("com.troy.keeper.desc.repository").build();
            return configuration;
        }
    
        @Bean
        public SessionFactory sessionFactory() {
            return new SessionFactory(getConfiguration());
        }
    
        @Bean("neo4jTransaction")
        public Neo4jTransactionManager neo4jTransactionManager(SessionFactory sessionFactory) {
            return new Neo4jTransactionManager(sessionFactory);
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26

    接口入口Controller.java

    @GetMapping("initDataToNeo4j")
            public void initDataToNeo4j() {
                service.initDataToNeo4j();
            }
    
    • 1
    • 2
    • 3
    • 4

    Service.java

    //节点数据,按照自己的实际业务添加,我这里对应的是所有表的数据,因为我业务中所有表结果基本一样,也即节点属性都一样。每个表的数据一个map,key是表名作为节点的标签
    Map<String, List<NodeData>> nodeDataMap;
    //关系数据,将每一个表数据的关系作为RelationData实体
    List<RelationData> relationDatas;
    
    //数据组装完成后,进行节点的创建
    neo4jUtil.creatNode(nodeDataMap);
    
    //进行关系绑定
    neo4jUtil.bindRelation(relationDatas);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    NodeData.java

    private String id;//属性id
    private String name;//属性名称
    private String table;//作为节点标签
    
    • 1
    • 2
    • 3

    RelationData.java

    //关系id
    private String id;
    //关系名称
    private String relationName;
    //因为我这里的关系跨实体,所以需要指定结束标签
    private String endLableName;
    
    //因为我这里的关系跨实体,所以需要指定开始标签
    private String startLableName;
    
    //开始节点的值
    private String startValue;
    
    //结束节点的值
    private String endWhereValue;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    Neo4jUtil.java

    @Component
    public class Neo4jUtil {
    @Resource
    private Session session;
    /**
         * 删除标签下的节点(包括节点之间的关系)
         * @param lableName
         * @return
         */
        public Integer deleteByLable(String lableName) {
            if (StringUtils.isEmpty(lableName)) {
                return 0;
            }
    
            String cypherSql = String.format("MATCH (r:`%s`) DETACH DELETE r ", lableName);
            Result query = session.query(cypherSql, new HashMap<>(16));
            session.clear();
            return query.queryStatistics().getNodesDeleted();
        }
    
    //创建节点
    public  void creatNode(Map<String, List<NodeData>> nodeDataMap) {
    
            if (nodeDataMap == null) {
                return ;
            }
    
            for(String key:nodeDataMap.keySet()){
                List<NodeData> data= nodeDataMap.get(key);
                if (StringUtils.isEmpty(key)) {
                    continue;
                }
    
                //表下没有数据的只创建一个没有属性的节点
                if (data== null || data.isEmpty()) {
                    String sql =String.format("create (:`%s`)",key);
                    session.query(sql, new HashMap<>(16));
                    continue;
                }
                //因为是全量导入,可以先删除这个标签下的全部节点和关系,按照自己业务要求自行添加
                deleteByLable(key);
                for (NodeData nodeData:data) {
                    //兼容中文和特殊符号
                    String  labels = ":`" + String.join("`:`", key) + "`";;
                    String id = nodeData.getId();
                    String name = nodeData.getName();
                    String property =  String.format("{id:'%s',name:'%s'} ",  id,name);
    
                    String sql = String.format("create (%s%s)", labels, property);
                    session.query(sql, new HashMap<>(16));
    
                }
            }
        
        }
    
    //绑定关系
    public void bindRelation( List<RelationData> relations) {
         if (relations== null) {
             return;
         }
          for (RelationData relation:relations) {
               String id = relation.getId();
                String relationName = relation.getRelationName();
              String startLableName = relation.getStartLableName();
              String endLableName = relation.getEndLableName();
              String startValue = relation.getStartValue();
              String endValue = relation.getEndValue();
        
              String property =  String.format("{id:'%s',name:'%s'} ", id,relationName);
              String cypherSql =  String.format("MATCH (n:`%s`),(m:`%s`) where n.id ='%s' and m.id= '%s' CREATE (n)-[r:%s%s]->(m)",
                      startLableName,endLableName,startValue ,endValue ,relationName,property) ;
              session.query(cypherSql, new HashMap<>(16));
          }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76

    之后执行controller接口,进行数据抽取和导入Neo4j,我开发的时候用的环境,大约有7w个节点,120w条关系。用本地Neo4j跑了两个多小时,连服务器部署的(跨地区)跑了8个小时。。。。

    太慢了

    后来查资料说是create适合数据量小的时候用,对于大量数据导入可以用neo4j-admin import ,接下来改造用neo4j-admin import ,参见Java初始化数据到Neo4j中(二)

  • 相关阅读:
    TS 的 高级用法 与 知识点总结
    springmvc1:初探springmvc
    【机器学习】YOLOv10与YOLOv8分析
    强大的SQL计算利器-SPL
    Visual Studio 2019 导出DLL库
    将网址转化为map或对象,方便取值
    如何通过内网穿透+AnyTXT Searcher实现便捷在线办公搜索?
    息肉分割(Polyp Segmentation)方向常用数据集汇总
    打点初级技巧
    【Three.js】知识梳理二十二:相机视角的平滑过渡与点击模型视角切换
  • 原文地址:https://blog.csdn.net/weixin_49456013/article/details/133351419