OrientDB是一個開源的NoSQL資料庫管理系統,同時也是一款高效能的圖資料庫,支援ACID事務以及原子操作。
圖資料庫是以點、邊為基礎儲存單元,以高效儲存、查詢圖資料為設計原理的資料管理系統。
圖概念對於圖資料庫的理解至關重要。圖是一組點和邊的集合,「點」表示實體,「邊」表示實體間的關係。在圖資料庫中,資料間的關係和資料本身同樣重要 ,它們被作為資料的一部分儲存起來。這樣的架構使圖[資料庫]能夠快速響應複雜關聯查詢,因為實體間的關係已經提前儲存到了資料庫中。圖資料庫可以直觀地視覺化關係,是[儲存]、查詢、分析高度[互聯資料]的最優辦法。
圖資料庫屬於非關係型資料庫(NoSQL)。圖資料庫對資料的儲存、查詢以及資料結構都和關係型資料庫有很大的不同。圖資料結構直接儲存了節點之間的依賴關係,而關係型資料庫和其他型別的非關係型資料庫則以非直接的方式來表示資料之間的關係。圖資料庫把資料間的關聯作為資料的一部分進行儲存,關聯上可新增標籤、方向以及屬性,而其他資料庫針對關係的查詢必須在執行時進行具體化操作,這也是圖資料庫在關係查詢上相比其他型別資料庫有巨大效能優勢的原因。
最簡單的說,圖資料庫對於關係型資料庫中的join操作具有十分高的效率,每個節點儲存了所有邊的參照。
下面我們來看一個示列,加入圓代表一個學校教學樓,圓角矩形代表教學樓下面的教室,三角形代表教室的每一張桌椅,當我們需要獲取一個樓宇下面的所有桌椅,在關係型資料庫如MySQL中,就需要把教學樓表、教室表以及桌椅表join起來查詢,雖然有索引,但當量上去後依然是一個昂貴的操作。而在圖資料庫如OrientDB中,就可以直接從教學樓出發通過廣度優先或者深度優先直接找到桌椅,而且相比關係型資料庫具有極高的效率。
OrientDB是一款高效能的檔案、圖資料庫,在關係查詢、遍歷方面有很大的速度優勢,特別是處理傳統關係型資料庫中的join操作,圖資料庫具有無法比擬的優點。雖然OrientDB官方提供了Java的SDK,但是還是有一定的學習成本,需要手擼操作指令碼,本倉庫對OrientDB的Java SDK進行了二次封裝,以更加自然的語言操作OrientDB,降低學習成本,使得專案能更快的整合OrientDB。
更簡單的API :話不多說上例子感受,假如我們要儲存一個People
物件到圖資料庫,先看看原生的SDK例子:
public static void main(String[] args) {
//使用原生JDK儲存一個「人」頂點到圖資料庫
People people1 = new People("張三", "男", 18);
try (ODatabaseSession session = OrientSessionFactory
.getInstance()
.getSession()) {
//在圖資料庫建立Class:People
if (session.getClass("People") == null) {
session.createVertexClass("People");
}
OVertex vertex = session.newInstance();
vertex.setProperty("name", people1.getName());
vertex.setProperty("age", people1.getAge());
vertex.save();
}
}
原生的SDK將頂點封裝成了Overtex
物件,首先需要先獲取對談ODatabaseSession
,並且建立對應的頂點類,然後將實體相關的屬性需要呼叫setProperty
方法存入進去,並且儲存,還要要留意關閉對談,對於屬性多、數量多的實體簡直是災難,下面我們來看看使用OrientDB API:
public static void main(String[] args) {
//建立實體物件
People people2 = new People("李四", "男", 21);
//將實體物件包裝成ResourceNode物件,其提供了對頂點的操作,對邊的操作在ResourceRelation裡
ResourceNode<People> li = new GraphResourceNode<>(people2);
//直接呼叫save方法進行儲存
li.save();
}
@Getter
@Setter
@ToString
//實現Vertex語意介面,表明該實體是一個圖資料庫的頂點物件
public class People implements Vertex {
private String name;
private String sex;
private Integer age;
public People(String name, String sex, Integer age) {
this.name = name;
this.sex = sex;
this.age = age;
}
public People() {
}
}
如上圖,通過上面的語句就將實體物件People1
存入了OrientDB。
更優雅的查詢 :原生SDK的查詢難免會跟OrientDB的SQL
語句或者Match
語句打交道,而且國內的中文檔案很少和相關部落格也很少,學習成本進一步加大,因此OrientDB API對常用查詢操作進行了封裝,做到完全透明化,下面我們來看一個示列:使用原生SDK進行查詢:
public static void main(String[] args) {
try (ODatabaseSession session = OrientSessionFactory
.getInstance()
.getSession()) {
OResultSet resultSet = session.query("select * from People where name = ?", "李四");
People people=new People();
while(resultSet.hasNext()){
OResult result= resultSet.next();
people.setName(result.getProperty("name"));
people.setAge(result.getProperty("age"));
}
resultSet.close();
}
}
原生JDK使用起來跟JDBC差不多,體驗差,下面看看OrientDB API查詢:
public static void main(String[] args) {
//建立資源圖物件,其提供了很多對圖的直接操作。使用OrientDB儲存庫(後續可以拓展Neo4j等儲存庫)
ResourceGraph graph = new ResourceGraph(new OrientDBRepository());
//呼叫extractNode方法取出指定節點
ResourceNode<People> peopleResourceNode = graph.extractNode(QueryParamsBuilder
.newInstance()
.addParams("name", "李四")
.getParams());
//獲取節點對應的屬性實體
People people = peopleResourceNode.getSource();
}
更人性化的遍歷 : 單一的查詢肯定不能滿足實際的需要,OrientDB提供了圖的遍歷,支援更復雜的查詢,通過遍歷我們能找到與任意一個節點有某種關係的其他節點,使用原生SDK如下:
public static void main(String[] args) {
try (ODatabaseSession session = OrientSessionFactory
.getInstance()
.getSession()) {
//從頂點#74:0出發,深度優先遍歷7層以內的同學,並且進行分頁
OResultSet resultSet = session.query("select * from (traverse * from #74:0 MAXDEPTH 7 STRATEGY DEPTH_FIRST) where (@class = \"Classmate\") skip 0 limit 10");
List<ClassMates> classMates=new ArrayList<>();
while(resultSet.hasNext()){
ClassMates classMate=new ClassMates(null);
OResult result= resultSet.next();
classMate.setDate(result.getProperty("date"));
//...
classMates.add(classMate);
}
resultSet.close();
}
}
使用OrientDB API
public static void main(String[] args) {
//建立資源圖物件,其提供了很多對圖的直接操作。使用OrientDB儲存庫(後續可以拓展Neo4j等儲存庫)
ResourceGraph graph = new ResourceGraph(new OrientDBRepository());
//直接呼叫traverse方法,引數分別是,出發節點、深度、遍歷策略、分頁設定、目標實體型別
PagedResult<ResourceNode<? extends Vertex>> result = graph.traverse(graph.extractNode("#74:0"),
QueryParamsBuilder
.newInstance()
.getParams(), 7, TraverseStrategy.DEPTH_FIRST, new PageConfig(1, 10, true), ClassMates.class);
result
.getSources()
.forEach(item -> System.out.println(item.getSource()));
}
無需語句,透明化,更人性化。
圖的元素分為頂點和邊兩類,在使用圖資料庫時我們一定要想好以前在關係型資料庫中存的資料如何在建立模型,如介紹中的教學樓-教室-桌椅模型。建立好模型後就需要定義頂點實體類,如特性中的People
類,以及關係類頂點實體類需要實現Vertex介面,關係類需要實現Edge介面。
圖資料庫的相關設定在orientdb-config.yml
中下面是一個設定示列:
orientDB:
# 圖資料庫IP地址
url: "localhost"
# 圖資料庫庫名
database: "testDB"
# 存取使用者名稱
username: "root"
# 存取密碼
password: "0000"
# 連線池設定
pool:
min: 5
max: 30
acquireTimeout: 30
public static void main(String[] args) {
//建立實體物件
People people2 = new People("李四", "男", 21);
//將實體物件包裝成ResourceNode物件,其提供了對頂點的操作,對邊的操作在ResourceRelation裡
ResourceNode<People> li = new GraphResourceNode<>(people2);
//直接呼叫save方法進行儲存
li.save();
}
public static void main(String[] args) {
//建立實體物件
People people1 = new People("張三", "男", 18);
People people2 = new People("李四", "男", 21);
//建立關係物件
FriendOf friendOf = new FriendOf();
//封裝成ResourceNode物件
ResourceNode<People> zhang = new GraphResourceNode<>(people1);
ResourceNode<People> li = new GraphResourceNode<>(people2);
//封裝成ResourceRelation物件
ResourceRelation<FriendOf> friend = new GraphResourceRelation<>(friendOf);
//直接呼叫ResourceRelation的link方法建立有向邊,如果頂點還未建立會先建立頂點
friend.link(zhang, li);
//或者呼叫linkUndirected建立無向邊,即會建立兩條互相指向的邊
friend.linkUndirected(zhang,li)
}
public static void main(String[] args){
//通過指定PageCofig來實現分頁查詢,建構函式參數列分別代表:頁號、頁大小、是否查詢總數
PagedResult<ResourceNode<? extends Vertex>> pagedResult = graph.extractNodes(QueryParamsBuilder
.newInstance()
.addParams("sex", "男")
.getParams(), new PageConfig(1, 5, true));
System.out.println("分頁結果:"+pagedResult.getSources());
System.out.println("總條數:"+pagedResult.getTotal());
}
public static void main(String[] args){
//建立資源圖物件,其提供了很多對圖的直接操作。使用OrientDB儲存庫(後續可以拓展Neo4j等儲存庫)
ResourceGraph graph = new ResourceGraph(new OrientDBRepository());
//查詢張三的朋友的朋友
graph
//獲取關係匹配器
.getGraphResourceNodeMatcher()
//張三作為起始節點
.asStart(zhang)
//查詢張三的朋友
.findUndirected(FriendOf.class)
//查詢張三的朋友的朋友
.findUndirected(FriendOf.class)
//將結果收整合People物件
.collect(People.class)
//遍歷列印
.forEach(item -> System.out.println(item.getSource()));
}
public static void main(String[] args){
//查詢張到李的最短路徑,參數列分別是:起始頂點、終止頂點、關係方向,無向邊任意方向都可、路徑最大深度、路徑要經過的邊型別
List<ResourceNode<? extends Vertex>> find4 = graph.shortestPath(zhang, li, RelationDirection.OUT, 3, FriendOf.class);
find4.forEach(item -> System.out.println(item.getSource()));
}
public static void main(String[] args){
//修改張的年齡為15歲
zhang.update(QueryParamsBuilder
.newInstance()
.addParams("age", 15)
.getParams())
}
public static void main(String[] args){
//移除張
zhang.remove();
}
關係重定向意思是將原來的A->B的關係變更成A->C。
public static void main(String[] args){
ResourceRelation<FriendOf> friend = graph.extractRelation(QueryParamsBuilder
.newInstance()
.addParams("date", new Date())
.getParams());
//參數列分別是:要重定向的關係,重定向到的目標
graph.relink(friend, li);
}
public static void main(String[] args){
//構建引數key是連線的關係,value是目標頂點
Map<FriendOf, ResourceNode<People>> toMap = new HashMap<>();
toMap.put(new FriendOf(), li);
toMap.put(new FriendOf(), wang);
toMap.put(new FriendOf(), huang);
toMap.put(new FriendOf(), hu);
//呼叫linksUndirected方法,返回所有的關係
List<ResourceRelation<? extends Edge>> relations = zhang.linksUndirected(toMap);
relations.forEach(item -> System.out.println(item.getInEdgeId() + "," + item.getOutEdgeId()));
}
其他更多API正在更新中......