人CI要求
我們將在這里簡單地通過構建人員CI來開始。 出于各種原因而使人們進入CMDB很有用:它們使您可以定義細粒度的訪問控制(例如,Jim可以將這樣的應用程序部署到開發環境中; Eric可以將他想要的任何東西部署到他想要的任何地方;等等。 ); 它們使您可以定義將接收關鍵事件通知的組。 等等
我們的個人CI將有一個用戶名,名字和姓氏,一些電話號碼,一個電子郵件地址,一個經理,直接報告,最后是他或她工作的項目。 我們需要能夠在列表視圖中顯示人員,在詳細信息視圖中顯示給定人員,允許用戶創建,編輯和刪除人員等等。 例如,下面是列表視圖的外觀,至少現在是這樣:

這是我們的詳細信息視圖的外觀:

人與項目之間的關系具有關聯的角色。 這種關系也是協作者列表的基礎:如果至少有一個項目是他們兩個成員,那么兩個人就是協作者。
我們的簡單要求應該足以顯示編寫Spring Data Neo4j代碼的感覺。
創建人員和項目成員實體
首先,我們將創建人。 我已經取消了驗證和JAXB批注,因為它們與我們當前的目的無關:
package org.skydingo.skybase.model;import java.util.Set;
import org.neo4j.graphdb.Direction;
import org.skydingo.skybase.model.relationship.ProjectMembership;
import org.springframework.data.neo4j.annotation.*;
import org.springframework.data.neo4j.support.index.IndexType;@NodeEntity
public class Person implements Comparable<Person> {@GraphId private Long id;@Indexed(indexType = IndexType.FULLTEXT, indexName = "searchByUsername")private String username;private String firstName, lastName, title, workPhone, mobilePhone, email;@RelatedTo(type = "REPORTS_TO")private Person manager;@RelatedTo(type = "REPORTS_TO", direction = Direction.INCOMING)private Set<Person> directReports;@RelatedToVia(type = "MEMBER_OF")private Set<ProjectMembership> memberships;public Long getId() { return id; }public void setId(Long id) { this.id = id; }public String getUsername() { return username; }public void setUsername(String username) { this.username = username; }... other accessor methods ...public Person getManager() { return manager; }public void setManager(Person manager) { this.manager = manager; }public Set<Person> getDirectReports() { return directReports; }public void setDirectReports(Set<Person> directReports) {this.directReports = directReports;}public Iterable<ProjectMembership> getMemberships() { return memberships; }public ProjectMembership memberOf(Project project, String role) {ProjectMembership membership = new ProjectMembership(this, project, role);memberships.add(membership);return membership;}... equals(), hashCode(), compareTo() ...
}
我們使用許多注釋來放置結構。 讓我們從節點及其屬性開始。 然后,我們將研究節點之間的簡單關系。 然后,我們看一下所謂的關系實體,它們基本上是奇特的關系。 首先,這是我們的域模型的抽象表示:

現在讓我們看一些細節。
節點及其屬性 。 當我們有一個節點支持的實體時,首先我們用@NodeEntity注釋對其進行注釋。 大多數簡單的節點屬性(即與其他節點沒有關系的屬性)都會隨處可見。 請注意,我不必注釋firstName,lastName,email等。 Spring Data Neo4j將在那里自動處理映射。
但是有兩個例外。 第一個是我將@GraphId放在我的id屬性上。 這告訴Spring Data Neo4j這是一個我們可以用于查找的標識符。 另一個是@Indexed注釋,該注釋(驚奇)為所討論的屬性創建了一個索引。 當您想要替代基于ID的查找時,這很有用。
現在我們來看一下關系。 概括地說,有簡單的關系和更高級的關系。 我們將從簡單的開始。
簡單的關系 。 從低層次上來說,Neo4j是一個圖形數據庫,因此我們可以用圖形理論術語來討論圖形,例如節點,邊,有向邊,DAG等。 但是這里我們使用圖進行領域建模,因此我們根據更高層次的領域建模概念來解釋低層次的圖形概念。 Spring Data Neo4j使用的語言是“節點實體”代表節點,“關系”代表邊緣。
我們的Person CI有一個簡單的關系,稱為REPORTS_TO,它與人們相關聯,因此我們可以對報告層次結構進行建模。 Person有兩個用于此關系的字段:manager和directReports。 這些是具有相同關系的相對站點。 我們使用@RelatedTo(type =“ REPORTS_TO”)注釋這些字段。 注釋還具有一個direction元素,其默認值為Direction.OUTGOING,這意味著“此”節點為邊緣尾部。 這就是為什么我們為directReports字段明確指定direction = Direction.INCOMING的原因。
這在數據庫中是什么樣的? Neoclipse揭示了一切。 以下是一些報告關系示例(單擊圖像可查看大圖):

(小寫:有一個@Fetch注釋-我們待會兒會看到它-告訴Spring Data Neo4j渴望加載相關實體。出于某種原因,我不必在管理器和直接報表關系中使用它,我不知道為什么。如果有人知道,我將不勝感激。
關系實體 。 除了人與人之間的REPORTS_TO關系之外,我們還關心人與項目之間的MEMBER_OF關系。 這比REPORTS_TO關系更有趣,因為MEMBER_OF具有關聯的屬性-角色-類似于向RDBMS中的鏈接表添加列,正如我在上一篇文章對Brig的答復中提到的那樣。 Person.memberOf()方法提供了一種使用特殊ProjectMembership“關系實體”將人員分配到項目的便捷方法。 這是代碼:
package org.skydingo.skybase.model.relationship;import org.skydingo.skybase.model.Person;
import org.skydingo.skybase.model.Project;
import org.springframework.data.neo4j.annotation.*;@RelationshipEntity(type = "MEMBER_OF")
public class ProjectMembership {@GraphId private Long id;@Fetch @StartNode private Person person;@Fetch @EndNode private Project project;private String role;public ProjectMembership() { }public ProjectMembership(Person person, Project project, String role) {this.person = person;this.project = project;this.role = role;}public Person getPerson() { return person; }public void setPerson(Person person) { this.person = person; }public Project getProject() { return project; }public void setProject(Project project) { this.project = project; }public String getRole() { return role; }public void setRole(String role) { this.role = role; }... equals(), hashCode(), toString() ...}
像Person一樣,ProjectMembership是一個實體,但它是一個關系實體。 我們使用@RelationshipEntity(type =“ MEMBER_OF”)將其標記為關系實體,并且與Person一樣,將@GraphId用作id屬性。 @StartNode和@EndNode注釋分別指示邊緣的尾部和頭部。 @Fetch告訴Spring Data Neo4j急切地加載節點。 默認情況下,Spring Data Neo4j不會急于加載關系,因為可能會將整個圖形加載到內存中。
創建人倉庫
這是我們的PersonRepository接口:
package org.skydingo.skybase.repository;import java.util.Set;
import org.skydingo.skybase.model.Person;
import org.skydingo.skybase.model.Project;
import org.springframework.data.neo4j.annotation.Query;
import org.springframework.data.neo4j.repository.GraphRepository;public interface PersonRepository extends GraphRepository<Person> {Person findByUsername(String username);@Query("start project=node({0}) match project<--person return person")Set<Person> findByProject(Project project);@Query("start person=node({0}) " +"match person-[:MEMBER_OF]->project<-[:MEMBER_OF]-collaborator " +"return collaborator")Set<Person> findCollaborators(Person person);
}
我在上一篇文章中指出,我們需要做的就是擴展GraphRepository接口。 Spring Data自動生成實現。

對于findByUsername(),Spring Data可以找出所需的查詢。 對于其他兩個查詢,我們使用@Query和Cypher查詢語言來指定所需的結果集。 查詢中的{0}指的是finder方法參數。 在findCollaborators()查詢中,我們使用[:MEMBER_OF]指示我們要遵循的關系。 這些返回Set而不是Iterables以消除重復項。
創建網頁控制器
我們不會在這里介紹整個控制器,但是會介紹一些代表性的方法。 假設我們已經將一個PersonRepository注入到控制器中。
創建一個人 。 要創建一個人,我們可以使用以下方法:
@RequestMapping(value = "", method = RequestMethod.POST)
public String createPerson(Model model, @ModelAttribute Person person) {personRepo.save(person);return "redirect:/people?a=created";
}
再一次,我們忽略了驗證。 我們要做的就是在存儲庫上調用save()方法。 這也是更新的工作方式。
尋找所有人 。 接下來,這是我們如何吸引所有人的方法:
@RequestMapping(value = "", method = RequestMethod.GET)
public String getPersonList(Model model) {Iterable<Person> personIt = personRepo.findAll();List<Person> people =new ArrayList<Person>(IteratorUtil.asCollection(personIt));Collections.sort(people);model.addAttribute(people);return "personList";
}
我們必須做一些工作才能使PersonRepository.findAll()返回的Iterable成為所需的格式。 Neo4j(org.neo4j.helpers.collection.IteratorUtil)隨附的IteratorUtil在此提供幫助。
尋找一個人 。 在這里,我們要顯示我們上面建立的個人詳細信息。 與findAll()一樣,我們必須做一些自我按摩:
@RequestMapping(value = "/{username}", method = RequestMethod.GET)
public String getPersonDetails(@PathVariable String username, Model model) {Person person = personRepo.findByUsername(username);List<ProjectMembership> memberships =CollectionsUtil.asList(person.getMemberships());List<Person> directReports =CollectionsUtil.asList(person.getDirectReports());List<Person> collaborators =CollectionsUtil.asList(personRepo.findCollaborators(person));Collections.sort(directReports);Collections.sort(collaborators);model.addAttribute(person);model.addAttribute("memberships", memberships);model.addAttribute("directReports", directReports);model.addAttribute("collaborators", collaborators);return "personDetails";
}
如果要查看JSP,請訪問Skybase GitHub網站 。
配置APP
最后,這是我的beans-service.xml文件:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"xmlns:context="http://www.springframework.org/schema/context"xmlns:neo4j="http://www.springframework.org/schema/data/neo4j"xmlns:p="http://www.springframework.org/schema/p"xmlns:tx="http://www.springframework.org/schema/tx"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans-3.0.xsdhttp://www.springframework.org/schema/contexthttp://www.springframework.org/schema/context/spring-context-3.0.xsdhttp://www.springframework.org/schema/data/neo4jhttp://www.springframework.org/schema/data/neo4j/spring-neo4j-2.0.xsdhttp://www.springframework.org/schema/txhttp://www.springframework.org/schema/tx/spring-tx-3.0.xsd"><context:property-placeholderlocation="classpath:/spring/environment.properties" /><context:annotation-config /><context:component-scan base-package="org.skydingo.skybase.service" /><tx:annotation-driven mode="proxy" /><neo4j:config storeDirectory="${graphDb.dir}" /><neo4j:repositories base-package="org.skydingo.skybase.repository" />
</beans>
Neo4j具有一個基于POJO的基本映射模型和一個基于AspectJ的高級映射模型。 在此博客文章中,我們一直在使用基于POJO的基本方法,因此我們不需要包括與AspectJ相關的配置,例如<context:spring-configured>。
在那里,這就是Neo4j支持的Person CI。 編碼愉快!
要更詳細地查看代碼或參與Skybase開發,請訪問Skybase GitHub網站 。
參考:來自Skydingo博客的JCG合作伙伴 Willie Wheeler的Spring Data Neo4j進行領域建模 。
相關文章 :
- Spring Data JPA的持久層
- 基于事務的基于事件的NOSQL存儲
- SQL或NOSQL:這是問題嗎?
- 什么是NoSQL?
- Cassandra,MongoDB,CouchDB,Redis,Riak,HBase比較
翻譯自: https://www.javacodegeeks.com/2012/01/domain-modeling-with-spring-data-neo4j.html