天天看點

Springboot 撞上 NebulaGraph——NGbatis 初體驗

作者:NebulaGraph
Springboot 撞上 NebulaGraph——NGbatis 初體驗
本文首發于 NebulaGraph 公衆号 https://mp.weixin.qq.com/s/z56o6AEz1Z4RmS8Zdx6dTA

大家好,我是開源項目 NGbatis 的發起人大葉([CorvusYe@GitHub](https://github.com/CorvusYe))。目前 NGbatis 也已成為 NebulaGraph 開源生态項目之一。在過去的 4 個月裡,NGbatis 從送出第一行代碼以來,已經釋出了 3 個版本,正在一步步變得越來越好。感謝一路同行的人們。

這裡給大家貼上倉庫位址:https://github.com/nebula-contrib/ngbatis,歡迎大家在倉庫下方留言提出建議回報。

一、目前有哪些參與者?

Springboot 撞上 NebulaGraph——NGbatis 初體驗

其中,@Szt-1 做了和 Spring Cloud 和 Nacos 的相容,@liuxiaocs7 完善了文檔,@soul-gin 做了 Java 與資料庫之間屬性别名的映射,@Nicole00 做了項目自動化與代碼規範,@wey-gu 提了很多有利于項目發展的建議并做了國際化。@DawnZzzzz、@hejiahuichengxuyuan、@yarodai 與 @LiuTianyou 則提了不少 issues,issues 讓人獲得不少靈感。

可以說現階段的 NGbatis 是使用者與開發者想法碰撞後的共同産物。

二、什麼是 NGbatis?

NGbatis 是一款針對 NebulaGraph + Spring Boot 的資料庫 ORM 架構。借鑒于 MyBatis 的使用習慣進行開發。包含了一些類似于 mybatis-plus 的單表操作,另外還有一些圖特有的實體 - 關系基本操作。

如果是 Java 後端服務的開發人員,相信看到這裡,大家對 NGbatis 的用途有了比較清晰的了解。接下來會從幾個問題出發,跟讀者們介紹 NGbatis:

  • 關于 NGbatis 有哪些思考?
  • NGbatis 能做什麼?
  • NGbatis 是怎麼實作的?
  • NGbatis 怎麼使用?

三、關于 NGbatis 有哪些思考?

  • Q: 最原始的訴求是什麼?A: 與 MyBatis 相同,想實作 GQL 與 Java 代碼的分離 。
  • Q: 為什麼不直接使用 MyBatis 內建?A:MyBatis 遵循 JDBC 規範,而 JDBC 規範更适合于傳統資料庫,圖資料庫存在與傳統資料庫不同的、圖特有的結構,如果采用 JDBC 規範,會受到一定局限。想為圖資料庫量身定制一款 ORM,随着圖資料庫的發展,友善拓展。
  • Q: 是否可以基于 JDBC 拓展出 GJDBC 的規範?A: 個人能力有限,不敢想,或許 NebulaGraph 官方可以考慮下。
  • Q: 為什麼版本号從 v1.1.0 開始,缺失了 v1.0.0 的版本号?A:最開始的版本是用來适配 Neo4j,後來選用了 NebulaGraph,保留了一個不曾釋出的小版本。第一次接觸的 NebulaGraph 是 v3.1.0,相容性方面重點放在 v3.1.0+ 的版本

以上,便是開發之初對 NGbatis 的一些方案選擇的思考,做了一些取舍,是好處多一些還是壞處多一些,我自己目前也還在糾結中。比如說放棄 JDBC 的規範後也意味着放棄其背後的生态,比如說優秀的第三方連接配接池方案。

糾結歸糾結,既然做了決定,路還是要往下走。開胃菜上完了,也該上正餐了。

四、NGbatis 能做什麼?

一個項目誕生最恰當的理由是:想要用它解決一些問題。以解決問題為中心,可以讓項目走得更遠。NGbatis 的任務就是盡可能地減少日常開發中或重複或繁瑣的工作。

  • 在代碼裡頻繁地做 “字元串”+” 字元串”
  • 一遍一遍地重複處理 ResultSet -> 業務對象
  • 重複寫單表基本的增、删、改、查
  • 在內建時,做過多配置,為什麼萬事就一定是開頭難,簡單點,內建的方式簡單點
  • 需要關注與業務關系不是很密切的 Session 問題

我們生活在一個基礎設施相對完善的時代,好處在于問題産生的同時,答案的模型也同時存在,我們需要做的隻是在問題與答案之間做适配,這裡真誠地對作出貢獻的前輩們表示感謝。

以上問題就要求 NGbatis 需要做到以下幾點:

  • 開箱即用,實作與 Springboot、Springcloud 的快速內建
  • 實作 GQL 與 Java 代碼分離,使用 XML 統一管理
  • 使用模闆引擎,解決 GQL 參數拼接繁瑣、容易寫錯的問題
  • 實作 ResultSet 與 Java 對象根據屬性名自動轉換
  • 單表基本增、删、改、查以及分頁
  • 本地 Session 管理,降低資源消耗

方向有了,剩下的就是工程問題了。

五、NGbatis 是怎麼實作的?

我們最本質的要求就是:把 GQL 語句執行到 NebulaGraph 當中。我們以帶參的 Hello Nebula 為例,即:

Springboot 撞上 NebulaGraph——NGbatis 初體驗

根據最樸素的 Java 開發方法,可以想到的是:先通過 XML 給 GQL 定義一個坐标,再定義一個接口,最後編寫一個實作類按坐标讀取 GQL 語句,使用模闆引擎替換參數。即:

  • XML
<mapper namespace=
        "com.example.dao.TestDao">

    <select id="greet">
        RETURN 'Hello ${ p0 }'
    </select>

</mapper>
           
  • DAO 接口
package com.example.dao;

public interface TestDao {
  String greet(String who);
}
           
  • DAO 實作(僞代碼)
package com.example.dao;

public class TestDaoImpl implements TestDao {

  @Override
  public String greet(String who) {
    Object[] var2 = new Object[]{who};
    String namespace = "com.example.dao.TestDao";
    String methodName = "greet";
    // 有一個函數,可以完成以下事情:
    // 1. 根據坐标讀取 GQL
    // 2. 使用模闆引擎完成參數拼接(Beetl)
    // 3. 執行到資料庫
    // 4. 轉換 ResultSet 形成 業務對象
    return foo( namespace, methodName, var2 );
  }
}
           

做到這裡其實就剩下 foo 怎麼編寫的問題了。到這裡,相信讀者們都有自己的思路。大家有興趣的話可以參考 org.nebula.contrib.ngbatis.proxy.MapperProxy。

但這裡引入了另一個問題:每個 dao 的方法,寫法基本是一樣的,又帶來了重複的工作,有悖于 NGbatis 的初衷。是以,使用動态代理,從 XML 與 DAO 資訊中自動生成 TestDao$Proxy,這邊使用的代理方案是基于位元組碼技術 ASM 來生成。上述的例子生成的位元組碼反編譯後的結果如下:

package com.example.dao;

import org.nebula.contrib.ngbatis.proxy.MapperProxy;

public class TestDao$Proxy implements TestDao {

  @Override
  public String greet(String var1) {
    Object[] var2 = new Object[]{var1};
    return (String) MapperProxy.invoke( "com.example.dao.TestDao", "greet", var2 );
  }
}
           

是以,開發者便不需要再重複編寫諸多 TestDaoImpl,定義好 XML 與 DAO,剩下的工作可以放心地交給 NGbatis。

最後剩下一個問題,參數替換問題:這個問題應該是與開發者關系最為密切的問題。是以,這裡不得不提的模闆引擎架構:Beetl 是國内流行模闆引擎,也是 NGbatis 一個重要的組成部分,連結是官網的 API。

  • 在調用時,将入參 json 化成 nebula-java 可以接收的參數形式(List、Set、Map、字元串、基本類型...):
{
String hello = dao.greet(“Nebula”);       -->       “p0”: “Nebula”
                                                  }
           
  • 最後以 XML 内容為模闆,進行替換:
RETURN 'Hello ${ p0 }'                  -->        RETURN 'Hello Nebula'
           

六、全局流程圖

Springboot 撞上 NebulaGraph——NGbatis 初體驗

七、NGbatis 該如何內建到自己的 Springboot 項目

  1. 添加依賴
<dependency>
        <groupId>org.nebula-contrib</groupId>
        <artifactId>ngbatis</artifactId>
        <version>1.1.0-rc</version>
    </dependency>
           
  1. 配置 NebulaGrpah 資料庫
nebula:
  hosts: 127.0.0.1:19669, ip:port, ....
  username: root
  password: nebula
  space: test
  pool-config:
    min-conns-size: 0
    max-conns-size: 10
    timeout: 0
    idle-time: 0
    interval-idle: -1
    wait-time: 0
    min-cluster-health-rate: 1.0
    enable-ssl: false
           
  1. 添加掃描包以引入 NGbatis bean
@SpringBootApplication(
  exclude={ DataSourceAutoConfiguration.class }, 
  scanBasePackages = {  "org.nebula.contrib", "your.domain" }  )
public class YourSpringbootApplication {

}
           
  1. 聲明主鍵生成器
import org.nebula.contrib.ngbatis.PkGenerator;
@Component
public class CustomPkGenerator implements PkGenerator {

    @Override
    public <T> T generate(String tagName, Class<T> pkType) {
        Object id = null; // 此處自行對 id 進行設值。
        return (T) id;
    }

}
           

到此,對于內建工作來說,任務已經完成,剩下就是開發的工作了。

開發人員隻需要做三件事:

  1. 定義接口:
package your.domain;

import  org.nebula.contrib.ngbatis.proxy.NebulaDaoBasic;

public interface PersonDao extends NebulaDaoBasic<Person, String> {
    Person selectByName( @Param("name") String param );
}
           
  1. 在 resources/mapper/PersonDao.xml 中編寫 GQL
<mapper namespace="your.domain.PersonDao">
    <select id="selectByName">
        MATCH (n: person)
        WHERE n.person.name == $name
        RETURN n
        LIMIT 1
    </select>
</mapper>
           

調用

注入:

Springboot 撞上 NebulaGraph——NGbatis 初體驗

調用自定義接口

    1. Person tom = dao.selectByName("Tom");

調用基類接口

// 不管屬性是否為空,如果資料庫中已有對應 id 的值,則覆寫
  public void insert( Person person ) {
    dao.insert( person );
  }

  // 僅寫入非空屬性
  public void insertSelective( Person preson ) {
    dao.insertSelective( person );
  }

  // 此處,Person 的主鍵欄 name 為 String ,則入參為 String
  public Person selectById( String id ) {
    return dao.selectById( id );
  }

  // 按屬性查詢
  public List<Person> selectBySelective( Person person ) {
    return dao.selectBySelective( person );
  }           

八、尾聲

以上就是本次交流的全部内容。如果 NGbatis 實作方式也是你喜歡的,issue、pr、star 都是 ok 的。如果對項目感興趣,也可以參與到開發中來,從中獲得成就感。倉庫位址:https://github.com/nebula-contrib/ngbatis。

最後,希望 NGbatis 能給越來越多的開發者帶來開發上的便利。

謝謝你讀完本文 (///▽///)

NebulaGraph Desktop,Windows 和 macOS 使用者安裝圖資料庫的綠色通道,10s 拉起搞定海量資料的圖服務。通道傳送門:http://c.nxw.so/blVC6

想看源碼的小夥伴可以前往 GitHub 閱讀、使用、(^з^)-☆ star 它 ~