關於Spring Boot整合Neo4j的介紹很多,但自己上手參考的時候,仍然有些東西雲裏霧裏有點暈。慢慢才摸出正道。
聊記,分享。
一、各組件直接的搭檔配合和版本密切相關。版本不合適,配合不上。
我是用的版本搭檔是:
Noe4j 3.5.8
spring data Noe4j 5.1.3.RELEASE (主要包括OGM SUpport、 Spring Data Repository Support)
利用maven進行jar包管理
neo4j jar包引入這塊容易暈,和neo4j相關的包很多,例如:
spring-boot-starter-data-neo4j
neo4j
spring-data-neo4j
neo4j-ogm-api
neo4j-ogm-bolt-driver
…………
每個是幹什麼的?引哪個,不引哪個?如何引?這是個技術活。需要根據各子項目的需要,搞清楚各個jar包的用途,在合適的位置引入。
我們項目既沒有直接引入neo4j包,更不是引入spring starter相關的neo4j組件,例如:
<!--neo4j-->
<!-- <dependency>-->
<!-- <groupId>org.springframework.boot</groupId>-->
<!-- <artifactId>spring-boot-starter-data-neo4j</artifactId>-->
<!-- </dependency>-->
<!-- https://mvnrepository.com/artifact/org.neo4j/neo4j -->
<!-- <dependency>-->
<!-- <groupId>org.neo4j</groupId>-->
<!-- <artifactId>neo4j</artifactId>-->
<!-- <version>${neo4j.version}</version>-->
<!-- </dependency>-->
而是在base模塊引入neo4j-ogm-api組件,例:
<dependency>
<groupId>org.neo4j</groupId>
<artifactId>neo4j-ogm-api</artifactId>
<version>2.1.6</version>
</dependency>
像JPA使用了ORM一樣,Neo4j使用了對象-圖形映射(Object-Graph Mapping,OGM)的方式來建模。
service模塊引入:
<!--neo4j-->
<dependency>
<groupId>org.neo4j</groupId>
<artifactId>neo4j-ogm-bolt-driver</artifactId>
<version>2.1.6</version>
</dependency>
Neo4j總共有三種連接方式。常用的有兩種,一種是http的連接方式【端口:7474】,一種是Bolt的連接方式【端口:7687】
dao模塊引入:
<dependencies>
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-neo4j</artifactId>
<version>4.2.11.RELEASE</version>
</dependency>
</dependencies>
Spring DATA Neo4j存儲庫提供了不同的API來支持不同的場景
- GraphRepository
- GraphTemplate
- CrudRepository
- PaginationAndSortingRepository
這些是Java類。 每個具有執行Neo4j數據庫操作的特定目的
S.No. | Spring 數據 Neo4j 類 | 用法 |
---|---|---|
1。 | GraphRepository | 它用於執行Basic Neo4j DB操作。 |
2。 | GraphTemplate | 像其他模塊一樣,它是執行Neo4j DB操作的Spring模板。 |
3。 | CrudRepository | 它用於使用Cypher查詢語言(CQL)執行Neo4j CRUD操作。 |
4。 | PaginationAndSortingRepository | 它用於執行Neo4j CQL查詢結果的分頁和排序。 |
我們只需要使接口繼承Neo4jRepository就可以使用該接口提供的一些基礎的增刪改查方法。
@Repository
public interface BotRepository extends Neo4jRepository<BotNode,Long> {
BotNode findAllByName(String name);
}
對於複雜的查詢我們可以參照上面講到的CQL語句執行。
@Repository
public interface BotRelationRepository extends Neo4jRepository<BotRelation,Long> {
//返回節點n以及n指向的所有節點與關係
@Query("MATCH p=(n:Bot)-[r:BotRelation]->(m:Bot) WHERE id(n)={0} RETURN p")
List<BotRelation> findAllByBotNode(BotNode botNode);
//返回節點n以及n指向或指向n的所有節點與關係
@Query("MATCH p=(n:Bot)<-[r:BotRelation]->(m:Bot) WHERE m.name={name} RETURN p")
List<BotRelation> findAllBySymptom(@Param("name") String name);
//返回節點n以及n指向或指向n的所有節點以及這些節點間的所有關係
@Query("MATCH p=(n:Bot)<-[r:BotRelation]->(m:Bot)<-[:BotRelation]->(:Bot)<-[:BotRelation]->(n:Bot) WHERE n.name={name} RETURN p")
List<BotRelation> findAllByStartNode(@Param("name") String name);
}
二、 application.yml配置neo4j庫訪問信息
neo4j默認密碼爲neo4j登錄時會提示修改密碼 此爲修改後的密碼
data:
neo4j:
uri: bolt://10.143.151.27:7687
username: neo4j
password: xxxneo4j
三、Neo4j實戰遇到的一些問題解決記錄
(1)Neo4j刪除節點和關係、徹底刪除節點標籤名
此處給出原文總結:
【1】先刪關係,再刪節點
【2】當記不得關係名時,type(r)可以查到關係名
【3】徹底刪除節點標籤名,需要刪除前期對該標籤名建立的索引
具體參考:https://www.jianshu.com/p/59bd829de0de
(2)怎麼合併相同節點?Neo4j圖數據庫爲什麼可以重複插入同一條數據,怎麼可以不重複插入
CREATE (ww:DatabaseConnection { ConnectionId:'c338df71cdcf85ebadac1aab31e25b3f',ConnectionHost:'localhost',ConnectionPort:'1521',ConnectionSeverName:'TESTUSE',ConnectionUserName:'system',ConnectionPassword:'orcl'})
比如這樣一條語句,我執行兩次會產生兩個一樣的節點
使用merge 關鍵字
merge (n:person{id:1}) set n+={id:1,name:'lz',age:18} return n
當person節點屬性 id=1 匹配時 ,更新該節點,若不存在則創建該節點。
具體參考:http://neo4j.com.cn/topic/595229bf4ee6742c0459236e
(3)@JsonIdentityInfo的使用:
使用註解@JsonIdentityInfo是防止查詢數據時引發遞歸訪問效應,註解@NodeEntity標誌這個類是一個節點實體,註解@GraphId定義了節點的一個唯一性標識,它將在創建節點時由系統自動生成,所以它是不可缺少的。
@JsonIdentityInfo(generator=JSOGGenerator.class)
@NodeEntity
public class Actor {
@GraphId Long id;
private String name;
private int born;
public Actor() { }
代碼清單2-22是電影節點實體建模,註解@Relationship表示List是一個關係列表,其中type設定了關係的類型,direction設定這個關係的方向,Relationship.INCOMING表示以這個節點爲終點。addRole定義了增加一個關係的方法。
代碼清單2-22 電影節點實體建模
@JsonIdentityInfo(generator=JSOGGenerator.class)
@NodeEntity
public class Movie {
@GraphId Long id;
String title;
String year;
String tagline;
@Relationship(type="ACTS_IN", direction = Relationship.INCOMING)
List<Role> roles = new ArrayList<>();
public Role addRole(Actor actor, String name){
Role role = new Role(actor,this,name);
this.roles.add(role);
return role;
}
public Movie() { }
代碼清單2-23是角色的關係實體建模,註解@RelationshipEntity表明這個類是一個關係實體,並用type指定了關係的類型,其中@StartNode指定起始節點的實體,
@EndNode指定終止節點的實體,這說明了圖中一條有向邊的起點和終點的定義。其中定義了一個創建關係的構造函數Role(Actor actor,Movie movie,String name),這裏的name參數用來指定這個關係的屬性。
代碼清單2-23 角色關係實體建模
@JsonIdentityInfo(generator=JSOGGenerator.class)
@RelationshipEntity(type = "ACTS_IN")
public class Role {
@GraphId
Long id;
String role;
@StartNode
Actor actor;
@EndNode
Movie movie;
public Role() {
}
public Role(Actor actor, Movie movie, String name) {
this.actor = actor;
this.movie = movie;
this.role = name;
}
(4)如果你是使用Spring boot2.0以上,在你創建項目完成後,啓動程序報錯:
Caused by: java.lang.ClassNotFoundException: org.neo4j.ogm.drivers.http.driver.HttpDriver
at java.net.URLClassLoader.findClass(URLClassLoader.java:381) ~[na:1.8.0_111]
at java.lang.ClassLoader.loadClass(ClassLoader.java:424) ~[na:1.8.0_111]
at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:331) ~[na:1.8.0_111]
at java.lang.ClassLoader.loadClass(ClassLoader.java:357) ~[na:1.8.0_111]
at java.lang.Class.forName0(Native Method) ~[na:1.8.0_111]
at java.lang.Class.forName(Class.java:264) ~[na:1.8.0_111]
at org.neo4j.ogm.session.SessionFactory.newDriverInstance(SessionFactory.java:92) ~[neo4j-ogm-core-3.1.0.jar:3.1.0]
... 45 common frames omitted
原因是缺少依賴,解決方法是導入缺少的依賴:
<dependency>
<groupId>org.neo4j</groupId>
<artifactId>neo4j-ogm-http-driver</artifactId>
</dependency>
(5)新建節點類,id
的屬性爲Long
而不能爲long
還需要注意的是在Spring boot1.5中修飾id
屬性的註釋爲@GraphId
,org.neo4j.ogm.annotation.Id
不存在,效果一樣,都是Neo4j數據庫自動創建的ID值。
@Id
@GeneratedValue
private Long id; //id
(6)測試更新數據:
@Test
public void updata(){
Movie movie = movieRepository.findAllById(8183l);
movie.setName("《迪迦》");
movieRepository.save(movie);
System.out.println(movieRepository.findAll());
}
執行程序,報錯:
java.lang.NullPointerException
我們看到程序執行的CQL語句爲:
MATCH (n:`Movie`) WHERE n.`id` = { `id_0` } WITH n RETURN n, ID(n)
然後我們在Neo4j瀏覽器控制檯執行查詢語句:
這是爲什麼呢?在Neo4j中,根據Id查詢節點的語句爲:
MATCH (n:Movie) where id(n)=8183 RETURN n
我們修改Repository層的查詢方法:
@Repository
public interface MovieRepository extends Neo4jRepository<Movie, Long> {
@Query("MATCH (n:Movie) where id(n)={id} RETURN n")
Movie findAllById(@Param("id") Long id);
}
再次執行更新程序,結果爲:
[Movie{id=8183, name='《迪迦》'}]
四、更多參考
https://www.2cto.com/database/201801/713556.html
https://blog.csdn.net/zt15732625878/article/details/98797467
https://www.jianshu.com/p/df99fe312c04
https://yq.aliyun.com/articles/89699/
https://docs.spring.io/spring-data/neo4j/docs/5.1.3.RELEASE/reference/html/
Cypher語言
https://blog.csdn.net/ainuser/article/details/72268344