Springboot撞上NebulaGraphNGbatis
本文首发于NebulaGraph公众号https:mp。weixin。qq。comsz56o6AEz1Z4RmS8Zdx6dTA
大家好,我是开源项目NGbatis的发起人大叶(〔CorvusYeGitHub〕(https:github。comCorvusYe))。目前NGbatis也已成为NebulaGraph开源生态项目之一。在过去的4个月里,NGbatis从提交第一行代码以来,已经发布了3个版本,正在一步步变得越来越好。感谢一路同行的人们。
这里给大家贴上仓库地址:https:github。comnebulacontribngbatis,欢迎大家在仓库下方留言提出建议反馈。一、目前有哪些参与者?
其中,Szt1做了和SpringCloud和Nacos的兼容,liuxiaocs7完善了文档,soulgin做了Java与数据库之间属性别名的映射,Nicole00做了项目自动化与代码规范,weygu提了很多有利于项目发展的建议并做了国际化。DawnZzzzz、hejiahuichengxuyuan、yarodai与LiuTianyou则提了不少issues,issues让人获得不少灵感。
可以说现阶段的NGbatis是使用者与开发者想法碰撞后的共同产物。二、什么是NGbatis?
NGbatis是一款针对NebulaGraphSpringBoot的数据库ORM框架。借鉴于MyBatis的使用习惯进行开发。包含了一些类似于mybatisplus的单表操作,另外还有一些图特有的实体关系基本操作。
如果是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当中。我们以带参的HelloNebula为例,即:
根据最朴素的Java开发方法,可以想到的是:先通过XML给GQL定义一个坐标,再定义一个接口,最后编写一个实现类按坐标读取GQL语句,使用模板引擎替换参数。即:XMLmappernamespacecom。example。dao。TestDaoselectidgreetRETURNHello{p0}selectmapperDAO接口packagecom。example。dao;publicinterfaceTestDao{Stringgreet(Stringwho);}DAO实现(伪代码)packagecom。example。dao;publicclassTestDaoImplimplementsTestDao{OverridepublicStringgreet(Stringwho){Object〔〕var2newObject〔〕{who};Stringnamespacecom。example。dao。TestDao;StringmethodNamegreet;有一个函数,可以完成以下事情:1。根据坐标读取GQL2。使用模板引擎完成参数拼接(Beetl)3。执行到数据库4。转换ResultSet形成业务对象returnfoo(namespace,methodName,var2);}}
做到这里其实就剩下foo怎么编写的问题了。到这里,相信读者们都有自己的思路。大家有兴趣的话可以参考org。nebula。contrib。ngbatis。proxy。MapperProxy。
但这里引入了另一个问题:每个dao的方法,写法基本是一样的,又带来了重复的工作,有悖于NGbatis的初衷。因此,使用动态代理,从XML与DAO信息中自动生成TestDaoProxy,这边使用的代理方案是基于字节码技术ASM来生成。上述的例子生成的字节码反编译后的结果如下:packagecom。example。dao;importorg。nebula。contrib。ngbatis。proxy。MapperProxy;publicclassTestDaoProxyimplementsTestDao{OverridepublicStringgreet(Stringvar1){Object〔〕var2newObject〔〕{var1};return(String)MapperProxy。invoke(com。example。dao。TestDao,greet,var2);}}
因此,开发者便不需要再重复编写诸多TestDaoImpl,定义好XML与DAO,剩下的工作可以放心地交给NGbatis。
最后剩下一个问题,参数替换问题:这个问题应该是与开发者关系最为密切的问题。所以,这里不得不提的模板引擎框架:Beetl是国内流行模板引擎,也是NGbatis一个重要的组成部分,链接是官网的API。在调用时,将入参json化成nebulajava可以接收的参数形式(List、Set、Map、字符串、基本类型。。。):{Stringhellodao。greet(Nebula);p0:Nebula}最后以XML内容为模板,进行替换:RETURNHello{p0}RETURNHelloNebula六、全局流程图
七、NGbatis该如何集成到自己的Springboot项目添加依赖dependencygroupIdorg。nebulacontribgroupIdngbatisartifactIdversion1。1。0rcversiondependency配置NebulaGrpah数据库nebula:hosts:127。0。0。1:19669,ip:port,。。。。username:rootpassword:nebulaspace:testpoolconfig:minconnssize:0maxconnssize:10timeout:0idletime:0intervalidle:1waittime:0minclusterhealthrate:1。0enablessl:false添加扫描包以引入NGbatisbeanSpringBootApplication(exclude{DataSourceAutoConfiguration。class},scanBasePackages{org。nebula。contrib,your。domain})publicclassYourSpringbootApplication{}声明主键生成器importorg。nebula。contrib。ngbatis。PkGenerator;ComponentpublicclassCustomPkGeneratorimplementsPkGenerator{OverridepublicTTgenerate(StringtagName,ClassTpkType){Objectidnull;此处自行对id进行设值。return(T)id;}}
到此,对于集成工作来说,任务已经完成,剩下就是开发的工作了。
开发人员只需要做三件事:定义接口:packageyour。domain;importorg。nebula。contrib。ngbatis。proxy。NebulaDaoBasic;publicinterfacePersonDaoextendsNebulaDaoBasicPerson,String{PersonselectByName(Param(name)Stringparam);}在resourcesmapperPersonDao。xml中编写GQLmappernamespaceyour。domain。PersonDaoselectidselectByNameMATCH(n:person)WHEREn。person。namenameRETURNnLIMIT1selectmapper
调用
注入:
调用自定义接口Persontomdao。selectByName(Tom);
调用基类接口不管属性是否为空,如果数据库中已有对应id的值,则覆盖publicvoidinsert(Personperson){dao。insert(person);}仅写入非空属性publicvoidinsertSelective(Personpreson){dao。insertSelective(person);}此处,Person的主键栏name为String,则入参为StringpublicPersonselectById(Stringid){returndao。selectById(id);}按属性查询publicListPersonselectBySelective(Personperson){returndao。selectBySelective(person);}八、尾声
以上就是本次交流的全部内容。如果NGbatis实现方式也是你喜欢的,issue、pr、star都是ok的。如果对项目感兴趣,也可以参与到开发中来,从中获得成就感。仓库地址:https:github。comnebulacontribngbatis。
最后,希望NGbatis能给越来越多的开发者带来开发上的便利。
谢谢你读完本文()
NebulaGraphDesktop,Windows和macOS用户安装图数据库的绿色通道,10s拉起搞定海量数据的图服务。通道传送门:http:c。nxw。soblVC6
想看源码的小伙伴可以前往GitHub阅读、使用、()star它