├── 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 | --------------------------------------------------------------------------------