Springboot 撞上 NebulaGraph——NGbatis 初體驗

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>
  1. 調用
    1. 注入:

          @Autowired private PersonDao dao;
      
    2. 調用自定義接口

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

      更多文檔:自定義 nGQL

    3. 調用基類接口

        // 不管屬性是否爲空,如果數據庫中已有對應 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 它 -> GitHub;和其他的 NebulaGraph 用戶一起交流圖數據庫技術和應用技能,留下「你的名片」一起玩耍呢~

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章