赞
踩
neo4j是基于Java语言编写图形数据库。图是一组节点和连接这些节点的关系。图形数据库也被称为图形数据库管理系统或GDBMS。
Neo4j的是一种流行的图形数据库。 其他的图形数据库是Oracle NoSQL数据库,OrientDB,HypherGraphDB,GraphBase,InfiniteGraph,AllegroGraph。
Neo4j图形数据库的主要构建块是:
节点:是图表的基本单位。 它包含具有键值对的属性
关系:连接两个节点,具有方向:单向和双向。每个关系包含“开始节点”或“从节点”和“到节点”或“结束节点”。关系也可以包含属性作为键值对。
属性:是用于描述图节点和关系的键值对。Key =值,其中Key是一个字符串,值可以通过使用任何Neo4j数据类型来表示
标签:将节点分组为集合。将一个公共名称与一组节点或关系相关联。 节点或关系可以包含一个或多个标签。 我们可以为现有节点或关系创建新标签。我们可以从现有节点或关系中删除现有标签。
数据浏览器:用于执行CQL命令并查看输出输出。
从官网下载最新版 Neo4j 社区版(Community),注意使用Neo4j需要安装java环境,我这里使用3.5.4版本,对应java1.8可以使用。
Neo4j-3.5.4 社区window版和java1.8,网盘资源获取,提取码me9t
下载后解压压缩包文件夹内容如下:
Neo4j应用程序有如下主要的目录结构:
bin目录:用于存储Neo4j的可执行程序;
conf目录:用于控制Neo4j启动的配置文件;
data目录:用于存储核心数据库文件;
plugins目录:用于存储Neo4j的插件;
在计算机-属性中创建系统环境变量NEO4J_HOME,并把主目录(E:\Program Files (x86)\neo4j-community-3.4.0)设置为变量值。
NEO4J_HOME=E:\Program Files (x86)\neo4j-community-3.4.0
如果没有安装过java的环境,也需要配置java环境变量。
JAVA_HOME=E:\Program Files (x86)\jdk1.8.0_181
再在path中添加Neo4j和java安装目录下的bin文件夹。
%NEO4J_HOME%\bin。
%JAVA_HOME%\bin。
修改neo4j-community-3.5.25\conf下的neo4j.conf配置文件
去掉第54、71、75、79前面的#
# 允许远程访问
dbms.connectors.default_listen_address=0.0.0.0
# 开启bolt服务,默认端口7687
dbms.connector.bolt.listen_address=0.0.0.0:7687
# 开启http服务,默认端口7474
dbms.connector.http.listen_address=0.0.0.0:7474
# 开启https服务,默认端口7473
dbms.connector.https.listen_address=0.0.0.0:7473
方法一:通过控制台启动Neo4j程序(win10建议使用)
CMD管理员身份运行(一定要以管理员身份进入CMD,否则会出现警告。警告: This command does not appear to be running with administrative rights. Some commands may fail e.g. Start/Stop)
输入:
neo4j.bat console
方法二:把Neo4j安装为服务(linux建议使用)
安装和卸载服务:
neo4j install-service
neo4j uninstall-service
启动服务,停止服务,重启服务和查询服务的状态:
neo4j start
neo4j stop
neo4j restart
neo4j status
在浏览器中打开http://localhost:7474 默认跳转到 http://localhost:7474/browser,出现Neo4j界面,则安装成功。初始密码:neo4j ,进去之后需要修改密码。
在这里插入图片描述
每次启动neo4j,它只能读取一个数据库。默认情况下的Graph.db数据库。
方法一:修改配置文件(win10)
使用Neo3.x创建新数据库而不删除现有数据库,所以只需在$NEO4J_HOME\conf的目录编辑neo4j.conf。搜寻dbms.active_database=,其默认值应为graph.db。用其他名称替换它,然后再次启动neo4j。现在,将在该目录名下创建一个新数据库。若要切换回以前的db,请重复这些步骤,只需将新值替换为graph.db在配置文件中。
注意:如果在neo4j启动的时候修改了配置文件,则需要重新启动一次,浏览器页面才会更新。
方法二:建立软连接(linux)
首次修改:
// 请将$NEO4j_HOME改为你的neo4j的安装路径
cd $NEO4j_HOME/data/databases/
// 保存原来数据库
mv graph.db graph1.db
//建立指向新数据库的软件接
ln -s graph2.db graph.db
//重启neo4j
cd $NEO4j_HOME/bin
./neo4j restart
再次修改
倘若我想访问graph3.db,就很简单了
//建立指向新数据库的软件接
ln -s graph3.db graph.db
//重启neo4j
cd $NEO4j_HOME/bin
./neo4j restart
CQL代表Cypher查询语言。 像Oracle数据库具有查询语言SQL,Neo4j具有CQL作为查询语言。
运行Neo4j之后,在软件窗口可以选择数据库创建位置,之后Neo4j就自动为我们创建好了数据库。
Neo4j CQL
我们可以通过Neo4j的图形化界面进行输入,在数据浏览器中的美元提示符下键入命令进行操控数据库。
1、创建节点
CREATE (dept:Dept { deptno:10,dname:"Accounting",location:"Hyderabad" })
2、查询节点
MATCH (dept: Dept)
RETURN dept.deptno,dept.dname
3、使用现有节点创建关系
MATCH (cust:Customer),(cc:CreditCard)
CREATE (cust)-[r:DO_SHOPPING_WITH{shopdate:"12/12/2014",price:55000}]->(cc)
RETURN r
4、使用新节点创建关系
CREATE (video1:YoutubeVideo1{title:"Action Movie1",updated_by:"Abc",uploaded_date:"10/10/2010"})
-[movie:ACTION_MOVIES{rating:1}]->
(video2:YoutubeVideo2{title:"Action Movie2",updated_by:"Xyz",uploaded_date:"12/12/2012"})
5、查询关系
MATCH (video1:YoutubeVideo1)-[movie:ACTION_MOVIES]->(video2:YoutubeVideo2)
RETURN movie
6、根据条件查询
MATCH (emp:Employee)
WHERE emp.name = 'Abc'
RETURN emp
7、删除节点(只能删除没有关系的节点)
MATCH (e: Employee) DELETE e
8、删除关系(可以连着节点一起删除)
MATCH (cc: CreditCard)-[rel]-(c:Customer)
DELETE cc,c,rel
9、删除节点或关系的属性和标签
MATCH (book { id:122 })
REMOVE book.price
RETURN book
10、修改添加节点或关系属性
MATCH (book:Book)
SET book.title = 'superstar'
RETURN book
11、排序
MATCH (emp:Employee)
RETURN emp.empid,emp.name,emp.salary,emp.deptno
ORDER BY emp.name
12、联合查询(必须两个查询命令属性相同)
MATCH (cc:CreditCard) RETURN cc.id,cc.number
UNION
MATCH (dc:DebitCard) RETURN dc.id,dc.number
13、过滤或限制查询返回的行数
MATCH (emp:Employee)
RETURN emp
LIMIT 2
14、跳过多少行返回数据
MATCH (emp:Employee)
RETURN emp
SKIP 2
15、不存在进行创建,存在不创建
MERGE (gp2:GoogleProfile2{ Id: 201402,Name:"Nokia"})
16、null值查询(新建时不输入任何属性也会出现null值)
MATCH (e:Employee)
WHERE e.id IS NULL
RETURN e.id,e.name,e.sal,e.deptno
17、批量查询in
MATCH (e:Employee)
WHERE e.id IN [123,124]
RETURN e.id,e.name,e.sal,e.deptno
18、字符串函数(大写:UPPER,小写:LOWER,截取:SUBSTRING,替换:REPLACE)
MATCH (e:Employee)
RETURN e.id,UPPER(e.name),e.sal,e.deptno
19、聚合函数(总行数:COUNT,最大值:MAX,最小值:MIN,求和:SUM,平均值:AVG)
MATCH (e:Employee) RETURN COUNT(*)
20、关系函数(关系启点:STARTNODE,关系终点:ENDNODE,关系id:ID,关系类型:TYPE)
MATCH (a)-[movie:ACTION_MOVIES]->(b)
RETURN STARTNODE(movie)
21、创建普通索引
CREATE INDEX ON :Customer (name)
22、创建唯一索引
CREATE CONSTRAINT ON (cc:CreditCard)
ASSERT cc.number IS UNIQUE
23、删除普通索引
DROP INDEX ON :Customer (name)
24、删除唯一索引
DROP CONSTRAINT ON (cc:CreditCard)
ASSERT cc.number IS UNIQUE
25、修改密码
:server change-password
26、case表达式(选择语句)
MATCH(n)
RETURN
CASE n.eyes
WHEN 'blue' THEN 1
WHEN 'brown' THEN 2
ELSE 3
END
AS result
27、深度匹配 (路径长度匹配)
(a)-[*2..4]->(b) // 匹配路径长度为 2 到4 之间 的路径
(a)-[*2..]->(b) // 匹配路径长度大于2的路径
(a)-[*..2]->(b) // 匹配路径长度小于2的路径
(a)-[*]->(b) // 匹配任意长度的路径
match(n) -[r:TYPE*minHops..maxHops]->(m) return r
28、最短路径匹配 shortestPath() 函数
match (martin:Person {name:"name1"}),(oliver:Perspn{name:"name2"}),p = shortestPath((martin)-[*..15]-(oliver))
return p
29、所有最短路径 allShortestPaths()
match (martin:Person {name:"name1"}),(oliver:Perspn{name:"name2"}),p= allShortestPaths((martin)-[*]-(oliver))
return p
首先从MySQL数据库导出csv文件,复制到import文件夹E:\Program Files (x86)\neo4j-community-3.4.0\import里,然后执行下列语句:
//导入节点 电影类型 == 注意类型转换 LOAD CSV WITH HEADERS FROM "file:///genre.csv" AS line MERGE (p:Genre{gid:toInteger(line.gid),name:line.gname}) //导入节点 演员信息 LOAD CSV WITH HEADERS FROM 'file:///person.csv' AS line MERGE (p:Person { pid:toInteger(line.pid),birth:line.birth, death:line.death,name:line.name, biography:line.biography, birthplace:line.birthplace}) // 导入节点 电影信息 LOAD CSV WITH HEADERS FROM "file:///movie.csv" AS line MERGE (p:Movie{mid:toInteger(line.mid),title:line.title,introduction:line.introduction, rating:toFloat(line.rating),releasedate:line.releasedate}) // 导入关系 actedin 电影是谁参演的 1对多 LOAD CSV WITH HEADERS FROM "file:///person_to_movie.csv" AS line match (from:Person{pid:toInteger(line.pid)}),(to:Movie{mid:toInteger(line.mid)}) merge (from)-[r:actedin{pid:toInteger(line.pid),mid:toInteger(line.mid)}]->(to) //导入关系 电影是什么类型 == 1对多 LOAD CSV WITH HEADERS FROM "file:///movie_to_genre.csv" AS line match (from:Movie{mid:toInteger(line.mid)}),(to:Genre{gid:toInteger(line.gid)}) merge (from)-[r:is{mid:toInteger(line.mid),gid:toInteger(line.gid)}]->(to)
Neo4j提供JAVA API以编程方式执行所有数据库操作。
它支持两种类型的API:
Neo4j原生Java API是一种低级别的纯JAVA API,用于执行数据库操作。 Neo4j Cypher Java API是简单而强大的JAVA API,用于执行所有CQL命令以执行数据库操作。
下图显示了Neo4j JAVA API应用程序的体系结构
客户端程序使用Neo4j Java API之一来在Neo4j数据库上交互和执行数据库操作。
使用idea创建SpringBoot的项目。
1、引入依赖:
<?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 https://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.4.4</version> <relativePath/> <!-- lookup parent from repository --> </parent> <groupId>com.chengkun</groupId> <artifactId>springboot-neo4j</artifactId> <version>0.0.1-SNAPSHOT</version> <name>springboot-neo4j</name> <description>Demo project for Spring Boot</description> <properties> <java.version>8</java.version> </properties> <dependencies> <!-- 集成springboot web组件 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <!-- 集成springboot test组件 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> <!-- 集成lombok 框架 --> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <optional>true</optional> </dependency> <dependency> <!-- 服务器开发需要的jar包 --> <groupId>org.neo4j.driver</groupId> <artifactId>neo4j-java-driver</artifactId> <version>1.5.0</version> </dependency> <dependency> <!-- 嵌入式开发需要的jar包 --> <groupId>org.neo4j</groupId> <artifactId>neo4j</artifactId> <version>3.5.4</version> </dependency> <!-- 文件上传组件 --> <dependency> <groupId>commons-fileupload</groupId> <artifactId>commons-fileupload</artifactId> <version>[1.3.3,)</version> </dependency> <!--阿里巴巴 fastjson --> <dependency> <groupId>com.alibaba</groupId> <artifactId>fastjson</artifactId> <version>1.2.56</version> </dependency> <!-- 引入Spring封装的jdbc--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-jdbc</artifactId> </dependency> <!-- 引入mysql数据库连接驱动--> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <scope>runtime</scope> </dependency> <!-- 引入 Druid 数据源依赖:https://mvnrepository.com/artifact/com.alibaba/druid --> <dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> <version>2.1.1</version> </dependency> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid-spring-boot-starter</artifactId> <version>1.1.10</version> </dependency> <dependency> <groupId>org.xmlunit</groupId> <artifactId>xmlunit-core</artifactId> </dependency> <!--swagger2--> <dependency> <groupId>io.springfox</groupId> <artifactId>springfox-swagger2</artifactId> <version>2.9.2</version> <exclusions> <exclusion> <artifactId>guava</artifactId> <groupId>com.google.guava</groupId> </exclusion> </exclusions> </dependency> <dependency> <groupId>io.springfox</groupId> <artifactId>springfox-swagger-ui</artifactId> <version>2.9.2</version> </dependency> <!-- google java lib --> <dependency> <groupId>com.google.guava</groupId> <artifactId>guava</artifactId> <version>[24.1.1,)</version> </dependency> </dependencies> <profiles> <profile> <id>base</id> <activation> <activeByDefault>true</activeByDefault> </activation> <properties> <package.environment>base</package.environment> </properties> </profile> <profile> <id>dev</id> <activation> <activeByDefault>true</activeByDefault> </activation> <properties> <package.environment>dev</package.environment> </properties> </profile> <profile> <id>test</id> <properties> <package.environment>test</package.environment> </properties> </profile> </profiles> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> <configuration> <fork>true</fork> <mainClass>com.chengkun.neo4j.SpringbootNeo4jApplication</mainClass> </configuration> </plugin> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-dependency-plugin</artifactId> <executions> <execution> <id>copy</id> <phase>package</phase> <goals> <goal>copy-dependencies</goal> </goals> <configuration> <outputDirectory> ${project.build.directory}\lib </outputDirectory> </configuration> </execution> </executions> </plugin> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <configuration> <source>8</source> <target>8</target> <encoding>utf8</encoding> </configuration> </plugin> </plugins> <resources> <resource> <directory>src/main/java</directory> <includes> <include>**/**</include> </includes> <filtering>true</filtering> </resource> <resource> <directory>src/main/resources</directory> <includes> <include>**/*.properties</include> <include>**/*.yml</include> <include>**/*.xml</include> <include>**/*.tld</include> <include>**/*.doc</include> </includes> <filtering>true</filtering> </resource> <resource> <directory>src/main/resources</directory> <includes> <!--不对excel模板过滤--> <include>**/*.xlsx</include> <include>**/*.xls</include> </includes> <excludes> <exclude>**/*.properties</exclude> <exclude>**/*.yml</exclude> <exclude>**/*.xml</exclude> <exclude>**/*.tld</exclude> <exclude>**/*.doc</exclude> </excludes> <filtering>false</filtering> </resource> </resources> </build> </project>
2、配置文件application.yml
server: port: 8080 spring: datasource: username: root password: root url: jdbc:mysql://127.0.0.1:3306/test?useUnicode=true&characterEncoding=utf-8&autoReconnect=true&useSSL=false&serverTimezone=Asia/Shanghai driver-class-name: com.mysql.cj.jdbc.Driver type: com.alibaba.druid.pool.DruidDataSource druid: initial-size: 5 min-idle: 5 max-active: 20 max-wait: 60000 time-between-eviction-runs-millis: 60000 min-evictable-idle-time-millis: 300000 validation-query: SELECT 1 FROM DUAL test-while-idle: true test-on-borrow: false test-on-return: false pool-prepared-statements: true #配置监控统计拦截的filters,去掉后监控界面sql无法统计,'wall'用于防火墙 filters: stat,wall,lombok max-pool-prepared-statement-per-connection-size: 20 use-global-data-source-stat: true connection-properties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=500 #easypoi配置 main: allow-bean-definition-overriding: true #mybatis配置 mybatis: configuration: map-underscore-to-camel-case: true mapper-locations: classpath*:com/chengkun/**/*Mapper.xml type-aliases-package: com.chengkun.entity #日志 logging: config: classpath:logback-spring.xml #########其他配置########## ##主要用于加载接口程序需要的配置信息到内存中使用 product为加载生产配置 可配置test,product,dev environment: dev # 开启swagger swagger: enabled: true # neo4j数据库地址 neo4j: db_neo4j: D:\neo4j-community-3.5.4-windows\data\databases\graph.db
3、创建节点标签枚举:
package com.chengkun.neo4j.entity;
import org.neo4j.graphdb.Label;
/**
* 自定义节点标签
*
* @author chengkun
* @version v1.0
* @create 2021/3/29 14:59
**/
public enum MyLabel implements Label {
JAVA, SCALA, SQL, NEO4J;
}
4、创建关系类型枚举
package com.chengkun.neo4j.entity;
import org.neo4j.graphdb.RelationshipType;
/**
* 自定义关系类型
*
* @author chengkun
* @version v1.0
* @create 2021/3/29 15:00
**/
public enum MyRelationshipType implements RelationshipType {
JVM_LANGIAGES, NON_JVM_LANGIAGES;
}
5、noe4j参数实体类
package com.chengkun.neo4j.entity; import io.swagger.annotations.ApiModel; import io.swagger.annotations.ApiModelProperty; import lombok.Data; /** * @author chengkun * @version v1.0 * @create 2021/3/29 19:09 **/ @Data @ApiModel(description = "neo4j参数实体类") public class Code { @ApiModelProperty(value = "id") private String id; @ApiModelProperty(value = "节点") private String node; @ApiModelProperty(value = "关系") private String relation; @ApiModelProperty(value = "属性") private String property; @ApiModelProperty(value = "节点标签") private String label; @ApiModelProperty(value = "边起点id") private String nodeFromId; @ApiModelProperty(value = "边起点标签") private String nodeFromLabel; @ApiModelProperty(value = "边终点id") private String nodeToId; @ApiModelProperty(value = "边终点标签") private String nodeToLabel; @ApiModelProperty(value = "条件语句") private String where; @ApiModelProperty(value = "修改语句") private String update; @ApiModelProperty(value = "返回结果") private String result; }
6、初始化neo4j数据库bean
package com.chengkun.neo4j.config; import org.neo4j.driver.v1.AuthTokens; import org.neo4j.driver.v1.Driver; import org.neo4j.driver.v1.GraphDatabase; import org.neo4j.graphdb.GraphDatabaseService; import org.neo4j.graphdb.factory.GraphDatabaseFactory; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import java.io.File; /** * @author chengkun * @version v1.0 * @create 2021/3/29 15:16 **/ @Configuration public class Neo4jConfig { @Value(value = "${neo4j.db_neo4j}") private String dbNoe4j; @Value(value = "${neo4j.neo4j_url}") private String noe4jUrl; @Value(value = "${neo4j.username}") private String username; @Value(value = "${neo4j.password}") private String password; //使用内嵌式数据库 @Bean public GraphDatabaseService getGraphDatabaseService() { GraphDatabaseFactory dbFactory = new GraphDatabaseFactory(); GraphDatabaseService db = dbFactory.newEmbeddedDatabase(new File(dbNoe4j)); return db; } //使用服务器式数据库 @Bean public Driver getDrive() { return GraphDatabase.driver(noe4jUrl, AuthTokens.basic(username, password)); } }
7、创建neo4j操作Controller
package com.chengkun.neo4j.controller; import com.chengkun.neo4j.entity.Code; import com.chengkun.neo4j.entity.MyLabel; import com.chengkun.neo4j.entity.MyRelationshipType; import io.swagger.annotations.*; import lombok.extern.log4j.Log4j2; import org.neo4j.driver.v1.Driver; import org.neo4j.driver.v1.Record; import org.neo4j.driver.v1.Session; import org.neo4j.driver.v1.StatementResult; import org.neo4j.graphdb.*; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.*; import java.util.ArrayList; import java.util.List; import static org.neo4j.driver.v1.Values.parameters; /** * @author chengkun * @version v1.0 * @create 2021/3/29 15:32 **/ @Api(tags = "neo4j增删改查") @RestController @Log4j2 @RequestMapping("/neo4j") public class Neo4jController { @Autowired private GraphDatabaseService graphDB; //使用内嵌式数据库 @Autowired private Driver driver; //使用服务器式数据库(推荐) @ApiOperation(value = "创建数据(使用内嵌式数据库)") @GetMapping(value = "/create", produces = "application/text;charset=UTF-8") public String create() { Transaction tx = graphDB.beginTx(); try { Node java = graphDB.createNode(MyLabel.JAVA); java.setProperty("id", 1); java.setProperty("name", "java"); Node c = graphDB.createNode(MyLabel.C); c.setProperty("id", 2); c.setProperty("name", "c"); Node python = graphDB.createNode(MyLabel.PYTHON); python.setProperty("id", 3); python.setProperty("name", "python"); Node scala = graphDB.createNode(MyLabel.SCALA); scala.setProperty("id", 4); scala.setProperty("name", "scala"); Node mysql = graphDB.createNode(MyLabel.MYSQL); mysql.setProperty("id", 5); mysql.setProperty("name", "mysql"); Node neo4j = graphDB.createNode(MyLabel.NEO4J); neo4j.setProperty("id", 6); neo4j.setProperty("name", "neo4j"); Relationship javaMysql = java.createRelationshipTo(mysql, MyRelationshipType.JVM_LANGIAGES); javaMysql.setProperty("id", 1); javaMysql.setProperty("name", "java连接mysql"); Relationship cMysql = c.createRelationshipTo(mysql, MyRelationshipType.NON_JVM_LANGIAGES); cMysql.setProperty("id", 2); cMysql.setProperty("name", "c连接mysql"); Relationship pythonMysql = python.createRelationshipTo(mysql, MyRelationshipType.NON_JVM_LANGIAGES); pythonMysql.setProperty("id", 3); pythonMysql.setProperty("name", "python连接mysql"); Relationship javaNeo4j = java.createRelationshipTo(neo4j, MyRelationshipType.JVM_LANGIAGES); javaNeo4j.setProperty("id", 4); javaNeo4j.setProperty("name", "java连接neo4j"); Relationship scalaNeo4j = scala.createRelationshipTo(neo4j, MyRelationshipType.NON_JVM_LANGIAGES); scalaNeo4j.setProperty("id", 5); scalaNeo4j.setProperty("name", "scala连接neo4j"); tx.success(); } finally { tx.close(); } return "创建成功"; } @ApiOperation(value = "根据id查询节点(使用内嵌式数据库)") @GetMapping(value = "/getNode", produces = "application/text;charset=UTF-8") public String getNode(@RequestParam("id") Integer id) { Transaction tx = graphDB.beginTx(); Node node; try { node = graphDB.findNode(MyLabel.JAVA, "id", id); //这里类型必须与保存的类型一致 System.out.println(node.getId()); System.out.println(node.getLabels()); System.out.println(node.getAllProperties()); tx.success(); } finally { tx.close(); } return "查询成功"; } @ApiOperation(value = "查看所有(使用内嵌式数据库)") @GetMapping(value = "/findAll", produces = "application/text;charset=UTF-8") public String findAll() { Transaction tx = graphDB.beginTx(); try { for (Node node : graphDB.getAllNodes()) { System.out.println(node.getId()); System.out.println(node.getLabels()); System.out.println(node.getAllProperties()); for (Relationship relationship : node.getRelationships()) { System.out.println(relationship.getId()); System.out.println(relationship.getType()); System.out.println(relationship.getAllProperties()); } } tx.success(); } finally { tx.close(); } return "查询成功"; } @ApiOperation(value = "删除节点以及关联的边(使用内嵌式数据库)") @GetMapping(value = "/deleteNode", produces = "application/text;charset=UTF-8") public String deleteNode(@RequestParam("id") Integer id) { Transaction tx = graphDB.beginTx(); try { Node scala = graphDB.findNode(MyLabel.SCALA, "id", id); Iterable<Relationship> relationships = scala.getRelationships(MyRelationshipType.NON_JVM_LANGIAGES); //删除边 for (Relationship relationship : relationships) { relationship.delete(); } //删除节点 scala.delete(); System.out.println("delete ok"); tx.success(); } finally { tx.close(); } return "删除成功"; } @ApiOperation(value = "修改节点以及关联的边(使用内嵌式数据库)") @GetMapping(value = "/updateNode", produces = "application/text;charset=UTF-8") public String updateNode(@RequestParam("id") Integer id) { Transaction tx = graphDB.beginTx(); try { Node c = graphDB.findNode(MyLabel.C, "id", id); Iterable<Relationship> relationships = c.getRelationships(Direction.OUTGOING, MyRelationshipType.NON_JVM_LANGIAGES); //修改边 for (Relationship relationship : relationships) { relationship.setProperty("type", "c->out->other"); } //修改节点 c.setProperty("type", "c语言"); System.out.println("update ok"); tx.success(); } finally { tx.close(); } return "修改成功"; } @ApiOperation(value = "删除所有(使用内嵌式数据库)") @GetMapping(value = "/deleteAll", produces = "application/text;charset=UTF-8") public String deleteAll() { Transaction tx = graphDB.beginTx(); try { for (Node node : graphDB.getAllNodes()) { for (Relationship relationship : node.getRelationships()) { relationship.delete(); } node.delete(); } tx.success(); } finally { tx.close(); } return "删除所有"; } @ApiOperation(value = "创建数据(使用服务器式数据库)") @GetMapping(value = "create1", produces = "application/text;charset=UTF-8") public String create1() { try { Session session = driver.session(); session.run("CREATE (a:Person {id: {id}, name: {name}, title: {title}})", parameters("id", 1, "name", "Arthur001", "title", "King001")); StatementResult result = session.run("MATCH (a:Person) WHERE a.name = {name} RETURN a.id as id,a.name AS name, a.title AS title", parameters("name", "Arthur001")); while (result.hasNext()) { Record record = result.next(); System.out.println(record.get("id").asInt() + " " + record.get("title").asString() + " " + record.get("name").asString()); } session.close(); System.out.println("使用服务器式数据库创建数据成功"); } catch (Exception e) { log.info("使用服务器式数据库创建数据失败"); return "使用服务器式数据库创建数据失败"; } return "使用服务器式数据库创建数据成功"; } @ApiOperation(value = "修改数据(使用服务器式数据库)") @ApiImplicitParams({ @ApiImplicitParam(name = "id", value = "修改节点id"), @ApiImplicitParam(name = "name", value = "修改后的名称"), }) @GetMapping(value = "update", produces = "application/json;charset=UTF-8") public void update(@RequestParam("id") Integer id, @RequestParam("name") String name) { try { Session session = driver.session(); StatementResult result = session.run("MATCH (a:Person) WHERE a.id={id} SET a.name = {name} RETURN a.id as id,a.name AS name, a.title AS title", parameters("id", id, "name", name)); while (result.hasNext()) { Record record = result.next(); System.out.println(record.get("id").asInt() + " " + record.get("title").asString() + " " + record.get("name").asString()); } session.close(); System.out.println("使用服务器式数据库修改数据成功"); } catch (Exception e) { log.info("使用服务器式数据库修改数据失败"); } } @ApiOperation(value = "添加关系(使用服务器式数据库)") @PostMapping(value = "relate", produces = "application/json;charset=UTF-8") public void relate(@RequestBody Code code) { try { Session session = driver.session(); session.run("MATCH (a:" + code.getNodeFromLabel() + "), (b:" + code.getNodeToLabel() + ") " + "WHERE a.id = " + code.getNodeFromId() + " AND b.id = " + code.getNodeToId() + " CREATE (a)-[:" + code.getRelation() + "]->(b)"); session.close(); System.out.println("使用服务器式数据库添加边成功"); } catch (Exception e) { log.info("使用服务器式数据库添加边失败"); } } @ApiOperation(value = "删除数据(使用服务器式数据库)") @GetMapping(value = "delete", produces = "application/text;charset=UTF-8") public String delete(@RequestParam("id") Integer id) { try { Session session = driver.session(); session.run("match (n:Person) where n.id = {id} delete n", parameters("id", id)); session.close(); System.out.println("使用服务器式数据库删除数据成功"); } catch (Exception e) { log.info("使用服务器式数据库删除数据失败"); return "使用服务器式数据库删除数据失败"; } return "使用服务器式数据库删除数据成功"; } @ApiOperation(value = "删除关系(使用服务器式数据库)") @PostMapping(value = "deleteRelate", produces = "application/json;charset=UTF-8") public void deleteRelate(@RequestBody Code code) { try { Session session = driver.session(); //不知道为什么执行报错 // session.run("MATCH (a:{NodeFromLabel})-[r:{relation}]->(b:{nodeToLabel}) WHERE a.id={nodeFromId} and b.id={nodeToId} DELETE r", // parameters("NodeFromLabel", code.getNodeFromLabel(), "relation", code.getRelation(), "nodeToLabel", code.getNodeToLabel(), "nodeFromId", code.getNodeFromId(), "nodeToId", code.getNodeToId())); session.run("MATCH (a:" + code.getNodeFromLabel() + ")-[r:" + code.getRelation() + "]->(b:" + code.getNodeToLabel() + ") WHERE a.id = " + code.getNodeFromId() + " and b.id = " + code.getNodeToId() + " DELETE r"); session.close(); System.out.println("使用服务器式数据库删除数据成功"); } catch (Exception e) { log.info("使用服务器式数据库删除数据失败"); } } @ApiOperation(value = "查询数据(使用服务器式数据库)") @GetMapping(value = "search", produces = "application/json;charset=UTF-8") public List<String> search() { List<String> resultList = new ArrayList<>(); try { Session session = driver.session(); StatementResult result = session.run("match (n) return n.id as id,n.name as name"); while (result.hasNext()) { Record record = result.next(); resultList.add(record.get("id").toString() + " " + record.get("name").toString()); } session.close(); System.out.println("使用服务器式数据库查询数据成功"); } catch (Exception e) { log.info("使用服务器式数据库查询数据失败"); } return resultList; } }
注意:
1、使用内嵌式数据库,neo4j界面启动时会锁住整个数据库,这时候java就无法连接这个据库
2、使用服务器形式数据库,可以跟界面进行联调,它比嵌入式配置更安全,推荐使用这种
下图显示了Spring DATA Neo4j模块的体系结构
Spring DATA Neo4j存储库
它提供了不同的API来支持不同的场景
这些是Java类。 每个具有执行Neo4j数据库操作的特定目的,继承关系是:Neo4jRepository->PaginationAndSortingRepository->CrudRepository
S.No. | Spring 数据 Neo4j 类 | 用法 |
---|---|---|
1 | Neo4jRepository | 它用于执行Basic Neo4j DB操作。 |
2 | CrudRepository | 它用于使用Cypher查询语言(CQL)执行Neo4j CRUD操作。 |
3 | PaginationAndSortingRepository | 它用于执行Neo4j CQL查询结果的分页和排序。。 |
1、添加Spring Data Neo4j相关依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-neo4j</artifactId>
</dependency>
2、application.yml添加Neo4j环境配置
spring:
neo4j:
uri: bolt://localhost:7687
authentication:
username: neo4j
password: neo4j
3、Neo4j中要定义节点,使用Spring Data Neo4j的注解@NodeEntity,标记该类为节点,节点名称为 Student
package com.chengkun.neo4j.entity; import com.fasterxml.jackson.annotation.JsonIgnore; import lombok.*; import org.neo4j.ogm.annotation.*; import java.util.ArrayList; import java.util.List; /** * @author chengkun * @version v1.0 * @create 2021/3/30 21:46 **/ @Data @NodeEntity("Student") public class Student{ /** * neo4j 生成的id */ @Id @GeneratedValue private Long id; /** * 属性,name */ @Property("name") private String name; /** * 属性,age */ @Property("age") private Integer age; /** * 关系,定义为友谊 指向别人 */ @JsonIgnore // 禁止json序列化无限遍历 @Relationship(type = "FRIENDSHIP_RELATION", direction = Relationship.OUTGOING) private List<FriendshipRelation> friendshipRelationOutList; /** * 关系,定义为友谊 指向自己 */ @JsonIgnore @Relationship(type = "FRIENDSHIP_RELATION", direction = Relationship.INCOMING) private List<FriendshipRelation> friendshipRelationInList; /** * 添加友谊的关系 * * @param friendshipRelation */ public void addOutRelation(FriendshipRelation friendshipRelation) { if (this.friendshipRelationOutList == null) { this.friendshipRelationOutList = new ArrayList<>(); } this.friendshipRelationOutList.add(friendshipRelation); } /** * 添加友谊的关系 * * @param friendshipRelation */ public void addInRelation(FriendshipRelation friendshipRelation) { if (this.friendshipRelationInList == null) { this.friendshipRelationInList = new ArrayList<>(); } this.friendshipRelationInList.add(friendshipRelation); } }
4、Neo4j也要定义关系,使用Spring Data Neo4j的注解@RelationshipEntity,标记其为关系,关系名为 FRIENDSHIP_RELATION,在里面定义StartNode和EndNode
package com.chengkun.neo4j.entity; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import lombok.Getter; import lombok.Setter; import org.neo4j.ogm.annotation.*; /** * 关系不能有toString方法 * * @author chengkun * @version v1.0 * @create 2021/3/30 22:01 **/ @Getter @Setter @RelationshipEntity(type = "FRIENDSHIP_RELATION") public class FriendshipRelation { @Id @GeneratedValue private Long id; @Property("name") private String name; @StartNode private Student from; @EndNode private Student to; }
注意:
JSON序列化注意事项:这里的关系里面有节点,节点里面有关系,造成循环依赖,在序列化中会造成栈溢出,就需要使用@JsonIgnoreProperties或@JsonIgnore进行序列化属性忽略
/**
* 关系,定义为友谊 指向别人
*/
@JsonIgnore // 禁止json序列化无限遍历
@Relationship(type = "FRIENDSHIP_RELATION", direction = Relationship.OUTGOING)
private List<FriendshipRelation> friendshipRelationOutList;
/**
* 关系,定义为友谊 指向自己
*/
@JsonIgnore
@Relationship(type = "FRIENDSHIP_RELATION", direction = Relationship.INCOMING)
private List<FriendshipRelation> friendshipRelationInList;
/**
* 关系,定义为友谊 指向别人
*/
@JsonIgnoreProperties({"from", "to"}) // 禁止json序列化无限遍历
@Relationship(type = "FRIENDSHIP_RELATION", direction = Relationship.OUTGOING)
private List<FriendshipRelation> friendshipRelationOutList;
/**
* 关系,定义为友谊 指向自己
*/
@JsonIgnoreProperties({"from", "to"})
@Relationship(type = "FRIENDSHIP_RELATION", direction = Relationship.INCOMING)
private List<FriendshipRelation> friendshipRelationInList;
5、StudentRepository为数据库操作,因为后面有删除关系,所以需要@Transactional
package com.chengkun.neo4j.repository; import com.chengkun.neo4j.entity.FriendshipRelation; import com.chengkun.neo4j.entity.Student; import org.springframework.data.neo4j.annotation.Query; import org.springframework.data.neo4j.repository.Neo4jRepository; import org.springframework.stereotype.Repository; import org.springframework.transaction.annotation.Transactional; import java.util.List; /** * @author chengkun * @version v1.0 * @create 2021/3/31 9:30 **/ @Transactional @Repository public interface StudentRepository extends Neo4jRepository<Student, Long> { /** * 通过name查找学生node * * @param name * @return */ Student findByName(String name); /** * 根据name获取学生 out 友谊关系 * * @param name * @return */ @Query("match p=(a:Student)-[r:FRIENDSHIP_RELATION]->(b:Student) where a.name = {0} return p") List<FriendshipRelation> outFriendship(String name); /** * 根据name获取学生 in 友谊关系 * * @param name * @return */ @Query("match p=(a:Student)<-[r:FRIENDSHIP_RELATION]-(b:Student) where a.name={0} return p") List<FriendshipRelation> inFriendship(String name); /** * 根据name获取学生 both 友谊关系 * * @param name * @return */ @Query("match p=(a:Student) <- [r:FRIENDSHIP_RELATION] ->(b:Student) <- [rr:FRIENDSHIP_RELATION] -> (c:Student) where b.name = {0} return p") List<FriendshipRelation> bothFriendship(String name); }
6、创建SpringDataDeo4jController进行测试
package com.chengkun.neo4j.controller; import com.chengkun.neo4j.entity.FriendshipRelation; import com.chengkun.neo4j.entity.Student; import com.chengkun.neo4j.repository.StudentRepository; import com.google.common.collect.Lists; import io.swagger.annotations.Api; import io.swagger.annotations.ApiImplicitParam; import io.swagger.annotations.ApiImplicitParams; import io.swagger.annotations.ApiOperation; import lombok.extern.log4j.Log4j2; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.domain.Page; import org.springframework.data.domain.PageRequest; import org.springframework.data.domain.Pageable; import org.springframework.web.bind.annotation.*; import java.util.List; /** * @author chengkun * @version v1.0 * @create 2021/3/31 10:21 **/ @Api(tags = "spring-data-neo4j增删改查") @RestController @Log4j2 @RequestMapping("/spring-data-neo4j") public class SpringDataDeo4jController { @Autowired private StudentRepository studentRepository; @ApiOperation(value = " 保存单个节点(使用spring-data-neo4j)") @PostMapping(value = "/saveStudentNode", produces = "application/text;charset=UTF-8") public String saveStudentNode(String name, int age) { Student student = new Student(); student.setName(name); student.setAge(age); studentRepository.save(student); return "创建成功"; } @ApiOperation(value = "保存批量节点(使用spring-data-neo4j)") @GetMapping(value = "saveAllStudentNode", produces = "application/text;charset=UTF-8") public String saveAllStudentNode() { Student student1 = new Student(); student1.setName("张三"); student1.setAge(18); Student student2 = new Student(); student2.setName("李四"); student2.setAge(19); Student student3 = new Student(); student3.setName("王五"); student3.setAge(18); Student student4 = new Student(); student4.setName("赵六"); student4.setAge(20); List<Student> list = Lists.newArrayList(student1, student2, student3, student4); studentRepository.saveAll(list); return "创建成功"; } @ApiOperation(value = "根据name查询学生(使用spring-data-neo4j)") @PostMapping(value = "/findStudentByName", produces = "application/json;charset=UTF-8") @ApiImplicitParam(name = "name", value = "学生名称") public Student findStudentByName(@RequestParam("name") String name) { Student student = studentRepository.findByName(name); return student; } @ApiOperation(value = "修改学生名称(使用spring-data-neo4j)") @GetMapping(value = "/updateStudentByName", produces = "application/json;charset=UTF-8") @ApiImplicitParams({ @ApiImplicitParam(name = "name", value = "修改前学生名称"), @ApiImplicitParam(name = "updateName", value = "修改后学生名称"), }) public Student updateStudentByName(String name, String updateName) { Student student = studentRepository.findByName(name); student.setName(updateName); studentRepository.save(student); return student; } @ApiOperation(value = "根据名称删除学生(使用spring-data-neo4j)") @GetMapping(value = "/deleteStudentByName", produces = "application/json;charset=UTF-8") @ApiImplicitParams({ @ApiImplicitParam(name = "name", value = "修改前学生名称"), }) public Student deleteStudentByName(String name) { Student student = studentRepository.findByName(name); studentRepository.delete(student); return student; } @ApiOperation(value = "删除所有学生(使用spring-data-neo4j)") @GetMapping(value = "/deleteAllStudent", produces = "application/text;charset=UTF-8") public String deleteAllStudent() { studentRepository.deleteAll(); return "删除成功"; } @ApiOperation(value = "查询全部(使用spring-data-neo4j)") @GetMapping(value = "/findAll", produces = "application/json;charset=UTF-8") public List<Student> findAll() { List<Student> studentNodeList = Lists.newArrayList(studentRepository.findAll()); return studentNodeList; } /** * 分页查询 */ @ApiOperation(value = "分页查询(使用spring-data-neo4j)") @GetMapping(value = "/pageFindAll", produces = "application/json;charset=UTF-8") @ApiImplicitParams({ @ApiImplicitParam(name = "page", value = "第几页"), @ApiImplicitParam(name = "size", value = "每页条数"), }) public Page<Student> pageFindAll(int page, int size) { // page从0开始 Pageable pageable = PageRequest.of(page - 1, size); Page<Student> studentNodePage = studentRepository.findAll(pageable); return studentNodePage; } @ApiOperation(value = "保存友谊关系(使用spring-data-neo4j)") @GetMapping(value = "/saveFriendShip", produces = "application/text;charset=UTF-8") @ApiImplicitParams({ @ApiImplicitParam(name = "studentFromName", value = "关系起点名称"), @ApiImplicitParam(name = "studentToName", value = "关系终点名称"), }) public String saveFriendShip(String studentFromName, String studentToName) { Student studentFrom = studentRepository.findByName(studentFromName); Student studentTo = studentRepository.findByName(studentToName); FriendshipRelation studentFromToRelation = new FriendshipRelation(); studentFromToRelation.setName("友谊关系"); // 添加from studentFromToRelation.setFrom(studentFrom); //添加to studentFromToRelation.setTo(studentTo); //只需要在from节点保存关系即可 studentFrom.addOutRelation(studentFromToRelation); studentRepository.save(studentFrom); return "保存成功"; } @ApiOperation(value = "保存双向友谊关系(使用spring-data-neo4j)") @GetMapping(value = "/saveBrotherFriendShip", produces = "application/text;charset=UTF-8") @ApiImplicitParams({ @ApiImplicitParam(name = "name1", value = "学生名称1"), @ApiImplicitParam(name = "name2", value = "学生名称2"), }) public String saveBrotherFriendShip(String name1, String name2) { Student studentFrom = studentRepository.findByName(name1); Student studentTo = studentRepository.findByName(name2); FriendshipRelation studentFromToRelation = new FriendshipRelation(); studentFromToRelation.setName("友谊关系"); // 添加from studentFromToRelation.setFrom(studentTo); //添加to studentFromToRelation.setTo(studentTo); //from->to studentFrom.addOutRelation(studentFromToRelation); FriendshipRelation studentToFromRelation = new FriendshipRelation(); studentToFromRelation.setName("友谊关系"); // 添加from studentToFromRelation.setFrom(studentTo); //添加to studentToFromRelation.setTo(studentFrom); //to->from studentFrom.addInRelation(studentToFromRelation); studentRepository.save(studentFrom); return "保存成功"; } @ApiOperation(value = "根据名称查询in关系(使用spring-data-neo4j)") @GetMapping(value = "/inFriendshipByName", produces = "application/json;charset=UTF-8") @ApiImplicitParams({ @ApiImplicitParam(name = "name", value = "学生名称"), }) public List<FriendshipRelation> inFriendshipByName(@RequestParam("name") String name) { List<FriendshipRelation> friendshipRelations = studentRepository.inFriendship(name); return friendshipRelations; } @ApiOperation(value = "根据名称查询out关系(使用spring-data-neo4j)") @GetMapping(value = "/outFriendshipByName", produces = "application/json;charset=UTF-8") @ApiImplicitParams({ @ApiImplicitParam(name = "name", value = "学生名称"), }) public List<FriendshipRelation> outFriendshipByName(String name) { List<FriendshipRelation> friendshipRelations = studentRepository.outFriendship(name); return friendshipRelations; } @ApiOperation(value = "获取兄弟关系(使用spring-data-neo4j)") @GetMapping(value = "/getBrotherFriendShip", produces = "application/json;charset=UTF-8") @ApiImplicitParams({ @ApiImplicitParam(name = "name", value = "学生名称"), }) public List<FriendshipRelation> getBrotherFriendShip(String name) { List<FriendshipRelation> friendshipRelations = studentRepository.bothFriendship(name); return friendshipRelations; } }
总结:
源码地址:springboot-neo4j
参考网址:
Neo4j安装及简单使用
W3Cschool-neo4j教程
Neo4j的使用与Java调用实例
Spring Data Neo4j 使用
Sping Data Neo4j官网
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。