├── README.md
├── pom.xml
└── src
├── main
├── java
│ └── com
│ │ └── appleyk
│ │ ├── Application.java
│ │ ├── config
│ │ └── Neo4jSourceConfig.java
│ │ └── node
│ │ ├── RNode.java
│ │ ├── base
│ │ └── RObject.java
│ │ └── relation
│ │ └── REdge.java
├── resources
│ ├── application.properties
│ └── logback-boot.xml
└── webapp
│ └── WEB-INF
│ └── web.xml
└── test
└── java
└── Neo4jSessionTest.java
/README.md:
--------------------------------------------------------------------------------
1 | # Spring-Boot-Neo4jAPI
2 | Spring-Boot集成neo4j,采用JavaAPI驱动包+Bolt连接方式实现节点和关系的创建
3 |
4 | 博客地址:https://mp.csdn.net/postedit/80290551
5 |
--------------------------------------------------------------------------------
/pom.xml:
--------------------------------------------------------------------------------
1 |
3 | 4.0.0
4 | com.appleyk
5 | Spring-Boot-Neo4jAPI
6 | 0.0.1-SNAPSHOT
7 | war
8 | Spring-Boot 集成Neo4j,实现原生JavaAPI的节点、关系操作
9 |
10 | org.springframework.boot
11 | spring-boot-starter-parent
12 | 1.5.12.RELEASE
13 |
14 |
15 |
16 | 1.8
17 | 3.0.8
18 |
19 |
20 |
21 | org.springframework.boot
22 | spring-boot-starter-web
23 |
24 |
25 |
26 |
27 |
28 | org.springframework.boot
29 | spring-boot-devtools
30 |
31 |
32 | true
33 |
34 |
35 |
36 | org.springframework.boot
37 | spring-boot-starter-test
38 | test
39 |
40 |
41 |
42 | junit
43 | junit
44 |
45 |
46 |
47 | org.neo4j.driver
48 | neo4j-java-driver
49 | 1.6.1
50 |
51 |
52 |
--------------------------------------------------------------------------------
/src/main/java/com/appleyk/Application.java:
--------------------------------------------------------------------------------
1 | package com.appleyk;
2 |
3 | import org.springframework.boot.SpringApplication;
4 | import org.springframework.boot.autoconfigure.SpringBootApplication;
5 | import org.springframework.boot.builder.SpringApplicationBuilder;
6 | import org.springframework.boot.web.support.SpringBootServletInitializer;
7 |
8 |
9 | @SpringBootApplication
10 | public class Application extends SpringBootServletInitializer{
11 |
12 | public static void main(String[] args) {
13 | SpringApplication.run(Application.class, args);
14 | }
15 |
16 | @Override
17 | protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
18 | return application.sources(Application.class);
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/src/main/java/com/appleyk/config/Neo4jSourceConfig.java:
--------------------------------------------------------------------------------
1 | package com.appleyk.config;
2 | import org.neo4j.driver.v1.AuthTokens;
3 | import org.neo4j.driver.v1.Driver;
4 | import org.neo4j.driver.v1.GraphDatabase;
5 | import org.neo4j.driver.v1.Session;
6 | import org.springframework.beans.factory.annotation.Value;
7 | import org.springframework.context.annotation.Bean;
8 | import org.springframework.context.annotation.Configuration;
9 |
10 | @Configuration
11 | public class Neo4jSourceConfig {
12 |
13 | @Value("${spring.neo4j.url}")
14 | private String url;
15 |
16 | @Value("${spring.neo4j.username}")
17 | private String username;
18 |
19 | @Value("${spring.neo4j.password}")
20 | private String password;
21 |
22 | @Bean(name = "session")
23 | public Session neo4jSession() {
24 | Driver driver = GraphDatabase.driver(url, AuthTokens.basic(username, password));
25 | return driver.session();
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/src/main/java/com/appleyk/node/RNode.java:
--------------------------------------------------------------------------------
1 | package com.appleyk.node;
2 |
3 | import com.appleyk.node.base.RObject;
4 | import com.appleyk.node.relation.REdge;
5 |
6 | /**
7 | * 节点
8 | * @author yukun24@126.com
9 | * @blob http://blog.csdn.net/appleyk
10 | * @date 2018年5月12日-下午1:58:59
11 | */
12 | public class RNode extends RObject{
13 |
14 | /**
15 | * 节点的uuid == 对应其他数据库中的主键
16 | */
17 | private Long uuid;
18 |
19 | /**
20 | * 节点里面是否包含有边 == 关系
21 | */
22 | private REdge edge;
23 |
24 | public Long getUuid() {
25 | return uuid;
26 | }
27 |
28 | public void setUuid(Long uuid) {
29 | this.uuid = uuid;
30 | }
31 |
32 | public REdge getEdge() {
33 | return edge;
34 | }
35 |
36 | public void setEdge(REdge edge) {
37 | this.edge = edge;
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/src/main/java/com/appleyk/node/base/RObject.java:
--------------------------------------------------------------------------------
1 | package com.appleyk.node.base;
2 |
3 | import java.util.HashMap;
4 | import java.util.Map;
5 |
6 | /**
7 | * 节点和关系对象的基类,含公共部分的id、标签label以及属性properties
8 | * @author yukun24@126.com
9 | * @blob http://blog.csdn.net/appleyk
10 | * @date 2018年5月12日-上午9:55:19
11 | */
12 | public class RObject {
13 |
14 |
15 | /**
16 | * 节点标签名称 == Node Labels
17 | */
18 | private String label;
19 |
20 | /**
21 | * 节点属性键值对 == Property Keys
22 | */
23 | private Map properties;
24 |
25 |
26 | public RObject(){
27 | properties = new HashMap<>();
28 | }
29 |
30 |
31 | public String getLabel() {
32 | return label;
33 | }
34 | public void setLabel(String label) {
35 | this.label = label;
36 | }
37 | public Map getProperties() {
38 | return properties;
39 | }
40 |
41 | /**
42 | * 添加属性
43 | * @param key
44 | * @param value
45 | */
46 | public void addProperty(String key,Object value){
47 | properties.put(key, value);
48 | }
49 |
50 | /**
51 | * 拿到属性
52 | * @param key
53 | * @return
54 | */
55 | public Object getProperty(String key){
56 | return properties.get(key);
57 | }
58 | /**
59 | * 移除属性
60 | * @param key
61 | */
62 | public void removeProperty(String key){
63 | properties.remove(key);
64 | }
65 |
66 | public void setProperties(Map properties) {
67 | this.properties = properties;
68 | }
69 | }
70 |
--------------------------------------------------------------------------------
/src/main/java/com/appleyk/node/relation/REdge.java:
--------------------------------------------------------------------------------
1 | package com.appleyk.node.relation;
2 |
3 | import com.appleyk.node.RNode;
4 | import com.appleyk.node.base.RObject;
5 |
6 | /**
7 | * 边 == 关系
8 | * @author yukun24@126.com
9 | * @blob http://blog.csdn.net/appleyk
10 | * @date 2018年5月12日-上午9:54:55
11 | */
12 | public class REdge extends RObject{
13 |
14 | /**
15 | * 关系的ID == 聚合、连接、属于、包括等,这些关系可能是枚举字典,因此记录关系ID是有必要的
16 | */
17 | private Long relationID;
18 |
19 | /**
20 | * 关系名称
21 | */
22 | private String name;
23 |
24 | /**
25 | * 关系指向哪一个节点 == 可能这个节点还有关系【节点关系递增下去】
26 | */
27 | private RNode rNode;
28 |
29 | public Long getRelationID() {
30 | return relationID;
31 | }
32 |
33 | public void setRelationID(Long relationID) {
34 | this.relationID = relationID;
35 | }
36 |
37 | public String getName() {
38 | return name;
39 | }
40 |
41 | public void setName(String name) {
42 | this.name = name;
43 | }
44 |
45 | public RNode getrNode() {
46 | return rNode;
47 | }
48 |
49 | public void setrNode(RNode rNode) {
50 | this.rNode = rNode;
51 | }
52 |
53 | }
54 |
--------------------------------------------------------------------------------
/src/main/resources/application.properties:
--------------------------------------------------------------------------------
1 | server.port=8080
2 | server.session.timeout=10
3 | server.tomcat.uri-encoding=utf8
4 |
5 | #在application.properties文件中引入日志配置文件
6 | #===================================== log =============================
7 | logging.config=classpath:logback-boot.xml
8 |
9 | #Neo4j配置
10 | #连接器:dbms.connector.bolt 协议:Bolt二进制协议
11 | spring.neo4j.url=bolt://localhost:7687
12 | #账户名称
13 | spring.neo4j.username=neo4j
14 | #账户密码
15 | spring.neo4j.password=n123
--------------------------------------------------------------------------------
/src/main/resources/logback-boot.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | %d %p (%file:%line\)- %m%n
8 |
9 | UTF-8
10 |
11 |
12 |
13 |
14 |
15 |
17 |
18 | opt/spring-boot-web/logs/sys.log
19 |
20 |
21 |
22 |
23 |
24 | log/sys.%d.%i.log
25 |
26 | 30
27 |
28 |
29 | 10MB
30 |
31 |
32 |
33 |
34 |
35 | %d %p (%file:%line\)- %m%n
36 |
37 |
38 | UTF-8
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
--------------------------------------------------------------------------------
/src/main/webapp/WEB-INF/web.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | Spring-Boot-Neo4jAPI
4 |
5 | index.html
6 | index.htm
7 | index.jsp
8 | default.html
9 | default.htm
10 | default.jsp
11 |
12 |
--------------------------------------------------------------------------------
/src/test/java/Neo4jSessionTest.java:
--------------------------------------------------------------------------------
1 | import org.junit.Test;
2 | import org.junit.runner.RunWith;
3 | import org.neo4j.driver.v1.Record;
4 | import org.neo4j.driver.v1.Session;
5 | import org.neo4j.driver.v1.StatementResult;
6 | import org.neo4j.driver.v1.Value;
7 | import org.neo4j.driver.v1.types.Node;
8 | import org.springframework.beans.factory.annotation.Autowired;
9 | import org.springframework.boot.test.context.SpringBootTest;
10 | import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
11 |
12 | import com.appleyk.Application;
13 | import com.appleyk.node.RNode;
14 | import com.appleyk.node.relation.REdge;
15 | import com.fasterxml.jackson.core.JsonGenerator;
16 | import com.fasterxml.jackson.databind.ObjectMapper;
17 |
18 | @RunWith(SpringJUnit4ClassRunner.class)
19 | @SpringBootTest(classes = Application.class)
20 | public class Neo4jSessionTest {
21 |
22 | final static ObjectMapper mapper = new ObjectMapper();
23 |
24 | static {
25 | /**
26 | * 使用neo4j的session执行条件语句statement,一定要使用这个反序列化对象为json字符串
27 | * 下面的设置的作用是,比如对象属性字段name="李二明",正常反序列化json为 == "name":"李二明"
28 | * 如果使用下面的设置后,反序列name就是 == name:"appleyk"
29 | * 而session执行语句create (:儿子{"name":"李二明","uuid":3330,"age":12,"height":"165cm"})会报错
30 | * 因此,......etc
31 | */
32 | mapper.configure(JsonGenerator.Feature.QUOTE_FIELD_NAMES, false);
33 | }
34 |
35 | @Autowired
36 | private Session session;
37 |
38 | @Test
39 | public void connect() {
40 | System.out.println("连接是否成功:" + session.isOpen());
41 | }
42 |
43 | @Test
44 | public void createNodeAndRelation() throws Exception {
45 |
46 | /**
47 | * 建立一个爸爸与儿子的关系
48 | */
49 |
50 | // 1.首先创建爸爸节点
51 | RNode rDad = new RNode();
52 | rDad.setUuid(1110L);
53 | rDad.setLabel("爸爸");
54 | // 2.其次为爸爸节点添加属性 == Property Keys
55 | rDad.addProperty("uuid", 1110L);
56 | rDad.addProperty("name", "李大明");
57 | rDad.addProperty("age", 45);
58 | rDad.addProperty("height", "182.5cm");
59 | // 3.添加关系
60 | REdge edge = new REdge();
61 | edge.addProperty("relationID", 521L);
62 | edge.addProperty("time", "2001-05-12");
63 | edge.setRelationID(521L);
64 | edge.setName("父亲");
65 | // 4.为关系节点添加指向节点 == 创建儿子节点
66 | RNode rSon = new RNode();
67 | rSon.setUuid(3330L);
68 | rSon.setLabel("儿子");
69 | // 5.为儿子节点添加属性 == Property Keys
70 | rSon.addProperty("uuid", 3330L);
71 | rSon.addProperty("name", "李二明");
72 | rSon.addProperty("age", 17);
73 | rSon.addProperty("height", "178cm");
74 | // 6.给爸爸节点添加关系
75 | rDad.setEdge(edge);
76 |
77 | createNode(rDad);
78 | createNode(rSon);
79 | createRelation(rDad,rSon);
80 | System.err.println("创建成功");
81 |
82 | }
83 |
84 | /**
85 | * 创建节点
86 | * @param rNode
87 | * @throws Exception
88 | */
89 | public void createNode(RNode rNode) throws Exception{
90 | RNode srcNode = queryNode(rNode);
91 | //查node是否已經存在了,不存在則創建
92 | if(srcNode == null){
93 | String propertiesString = mapper.writeValueAsString(rNode.getProperties());
94 | String cypherSql = String.format("create (:%s%s)", rNode.getLabel(), propertiesString);
95 | System.out.println(cypherSql);
96 | session.run(cypherSql);
97 | System.err.println("创建节点:"+rNode.getLabel()+"成功!");
98 | }else{
99 | System.err.println("节点已存在,跳过创建");
100 | }
101 | }
102 |
103 | /**
104 | * 创建关系
105 | * @param srcNode
106 | * @param tarNode
107 | * @throws Exception
108 | */
109 | public void createRelation(RNode srcNode,RNode tarNode) throws Exception{
110 | REdge edge = queryRelation(srcNode,tarNode);
111 | if(edge == null){
112 | edge = srcNode.getEdge();
113 | String propertiesString = mapper.writeValueAsString(edge.getProperties());
114 | String cypherSql = String.format("match(a),(b) where a.uuid=%d and b.uuid=%d create (a)-[r:%s %s]->(b)",
115 | srcNode.getUuid(),tarNode.getUuid(),
116 | edge.getName(), propertiesString);
117 | System.out.println(cypherSql);
118 | session.run(cypherSql);
119 | System.err.println("创建关系:"+edge.getName()+"成功!");
120 | }else{
121 | System.err.println("关系已存在,跳过创建");
122 | }
123 | }
124 |
125 |
126 |
127 | /**
128 | * 查询节点
129 | *
130 | * @param rNode
131 | * @return
132 | */
133 | public RNode queryNode(RNode rNode) {
134 |
135 | RNode node = null;
136 | String cypherSql = String.format("match(n:%s) where n.uuid = %d return n", rNode.getLabel(), rNode.getUuid());
137 | StatementResult result = session.run(cypherSql);
138 | if (result.hasNext()) {
139 | Record record = result.next();
140 | for (Value value : record.values()) {
141 | /**
142 | * 结果里面只要类型为节点的值
143 | */
144 | if (value.type().name().equals("NODE")) {
145 | Node noe4jNode = value.asNode();
146 | node = new RNode();
147 | node.setLabel(rNode.getLabel());
148 | node.setProperties(noe4jNode.asMap());
149 |
150 | }
151 | }
152 | }
153 | return node;
154 | }
155 |
156 | /**
157 | * 查询关系
158 | * @param rNode
159 | * @return
160 | */
161 | public REdge queryRelation(RNode srcNode,RNode tarNode){
162 | REdge edge = srcNode.getEdge();
163 | String cypherSql =String.format("match(n)-[r:%s]-(b) where n.uuid = %d and b.uuid = %d return r",
164 | edge.getName(),srcNode.getUuid(),tarNode.getUuid());
165 | StatementResult result = session.run(cypherSql);
166 | if(result.hasNext()){
167 | return edge;
168 | }
169 | return null;
170 | }
171 | }
172 |
--------------------------------------------------------------------------------