本文首发于 Nebula Graph Community 公众号

我最近注意到很多同学对于 ORM 框架的需求比较迫切,而且有热心的同学已经捐赠了自己开发的项目,Nebula 社区也在 working on it。下面主要介绍一下我们在使用 MyBatis 操作 Nebula Graph 方面的一些经验,希望能够帮助到大家。

MyBatis

Java 开发的同学想必对 MyBatis 都比较熟悉了。MyBatis 是一款优秀的持久层框架,它支持自定义 SQL、存储过程以及高级映射,并且免除了几乎所有的 JDBC 代码以及设置参数和获取结果集的工作。

实现途径

主要通过 MyBatis 结合 nebula-jdbc 来实现参数返回值映射以及语句执行。

Demo 示例

完整代码参见 GitHub

Nebula Schema

CREATE SPACE basketballplayer(partition_num=10,replica_factor=1,vid_type=fixed_string(32));
CREATE TAG IF NOT EXISTS player(name string, age int);
CREATE EDGE IF NOT EXISTS follow(degree int);

工程结构

application.yaml

spring:
datasource:
driver-class-name: com.vesoft.nebula.jdbc.NebulaDriver
url: jdbc:nebula://localhost:9669/basketballplayer
username: nebula
password: nebula
hikari:
maximum-pool-size: 20
mybatis:
mapper-locations: classpath:mapper/*.xml

DO

@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class PlayerDO {
/**
* vid
*/
private String id;
private String name;
private Long age;
}

Dao

public interface PlayerDao {

    int insert(PlayerDO entity);

    int update(PlayerDO entity);

    int insertBatch(List<PlayerDO> batch);

    PlayerDO select(String id);

    List<PlayerDO> selectBatch(List<String> batch);

    int delete(String id);

    int deleteBatch(List<String> batch);

    //以上代码自动生成

    PlayerDO selectReturnV(String id);
}

Mapper

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.example.dao.PlayerDao">
<!-- 通用查询映射结果 -->
<resultMap id="BaseResultMap" type="com.example.pojo.PlayerDO">
<result column="id" property="id" jdbcType="VARCHAR"/>
<result column="name" property="name" jdbcType="VARCHAR"/>
<result column="age" property="age" jdbcType="BIGINT"/>
</resultMap> <!-- 插入点或边 -->
<insert id="insert" parameterType="com.example.pojo.PlayerDO">
insert vertex `player` (
<trim suffixOverrides=",">
<if test="name != null">
name,
</if>
<if test="age != null">
age,
</if>
</trim>
) values #{id} :(
<trim suffixOverrides=",">
<if test="name != null">
#{name},
</if>
<if test="age != null">
#{age},
</if>
</trim>
)
</insert> <!-- 批量插入点或边-->
<insert id="insertBatch" parameterType="com.example.pojo.PlayerDO">
insert vertex `player`
<trim prefix="(" suffix=")" suffixOverrides=",">
name,
age,
</trim>
values
<foreach collection="list" item="item" separator=",">
#{item.id} :
<trim prefix="(" suffix=")" suffixOverrides=",">
#{item.name},
#{item.age},
</trim>
</foreach>
</insert> <!-- 更新点或边 -->
<update id="update" parameterType="com.example.pojo.PlayerDO">
UPDATE vertex ON `player` #{id}
<trim prefix="set" suffixOverrides=",">
<if test="name != null">
name = #{name},
</if>
<if test="age != null">
age = #{age},
</if>
</trim>
</update> <!-- 查询点 -->
<select id="select" resultType="com.example.pojo.PlayerDO">
match (v:`player`) where id(v) == #{id} return
<trim suffixOverrides=",">
id(v) as id,
v.name as name,
v.age as age,
</trim>
</select> <!-- 批量查询点 -->
<select id="selectBatch" resultType="com.example.pojo.PlayerDO">
match (v:`player`) where id(v) in [
<foreach collection="list" item="item" separator=",">
#{item}
</foreach>
] return
<trim suffixOverrides=",">
id(v) as id,
v.name as name,
v.age as age,
</trim>
</select> <!-- 删除点或边 -->
<delete id="delete" parameterType="java.lang.String">
delete vertex #{id}
</delete> <!-- 批量删除点或边 -->
<delete id="deleteBatch"
parameterType="java.lang.String">
delete vertex
<foreach collection="list" item="item" separator=",">
#{item}
</foreach>
</delete>
<!--以上代码自动生成--> <select id="selectReturnV" resultMap="BaseResultMap">
match (v:`player`) where id(v) == #{id} return v
</select>
</mapper>

Tag 操作

@SpringBootTest
public class PlayerDaoTest { @Resource
private PlayerDao playerDao; @Test
public void operation() {
//insert
PlayerDO player = PlayerDO.builder().id("daiyi").name("daiyi").age(22l).build();
playerDao.insert(player);
//insertBatch
PlayerDO playerBatch = PlayerDO.builder().id("daiyi").name("daiyi").age(22l).build();
PlayerDO joe = PlayerDO.builder().id("joe").name("joe").age(24l).build();
playerDao.insertBatch(Lists.newArrayList(playerBatch, joe));
//update
playerDao.update(PlayerDO.builder().id("daiyi").name("daiyiupdate").build());
//select
PlayerDO playerDO = playerDao.select("daiyi");
//selectBatch
List<PlayerDO> players = playerDao.selectBatch(Lists.newArrayList("daiyi", "joe"));
//selectReturnV
playerDao.selectReturnV("daiyi");
//delete
playerDao.delete("daiyi");
//deleteBatch
playerDao.deleteBatch(Lists.newArrayList("daiyi", "joe"));
}
}

Edge 及 Path 操作

篇幅有限,详情可以参见github

版本适配

目前仅支持了 Nebula 2.5 版本,后续版本的支持还在适配中。

总结

优点

  • 使用简单,消除了使用 JDBC 或 nebula-client 带来的冗余代码。
  • 可以使用配套连接池管理连接,并且可以与 Spring Boot 无缝衔接。
  • nGQL 与代码解耦,方便管理。
  • 大量便捷标签,免除了代码拼接语句的烦恼。

存在的问题

  • 针对返回值为 Vertex(类似MATCH v RETURN v)、Edge、无属性 Path 的类型目前采用在 MyBatis 中的 Interceptor 做拦截处理,也能满足使用。但这种实现方式感觉不是很好,后期有待优化。
  • 对于返回值类型为带属性 Path、多 Tag 查询以及 GET SUBGRAPH 语句的情况,因为返回的结果中实体以及边的类型可能有多种,目前没有想到比较好的映射方式也就没有支持。
  • 上述示例中使用的 JDBC 驱动是我们自己开发的版本(详见 https://github.com/DA1Y1/nebula-jdbc),与社区版的主要区别在 URL 上服务地址的指定以及⼀些转义字符的处理,后续也希望能将这些 Feature 合并到社区版本中,统⼀使⽤。

为了方便使用我们还开发了类似 mybatis-generator 这种工具来生成一些基础代码,提供基本的增删改查功能。感兴趣的同学可以在 IDEA 的 Plugins 中搜索 Nebula Generator 下载,使用方式参见:https://plugins.jetbrains.com/plugin/18026-nebula-generator

最后感谢 @DA1Y1 以及其他几位小伙伴的贡献!


交流图数据库技术?加入 Nebula 交流群请先填写下你的 Nebula 名片,Nebula 小助手会拉你进群~~

使用 MyBatis 操作 Nebula Graph 的实践的更多相关文章

  1. Neo4j 导入 Nebula Graph 的实践总结

    摘要: 主要介绍如何通过官方 ETL 工具 Exchange 将业务线上数据从 Neo4j 直接导入到 Nebula Graph 以及在导入过程中遇到的问题和优化方法. 本文首发于 Nebula 论坛 ...

  2. 分布式图数据库 Nebula Graph 的 Index 实践

    导读 索引是数据库系统中不可或缺的一个功能,数据库索引好比是书的目录,能加快数据库的查询速度,其实质是数据库管理系统中一个排序的数据结构.不同的数据库系统有不同的排序结构,目前常见的索引实现类型如 B ...

  3. Nebula Graph 在微众银行数据治理业务的实践

    本文为微众银行大数据平台:周可在 nMeetup 深圳场的演讲这里文字稿,演讲视频参见:B站 自我介绍下,我是微众银行大数据平台的工程师:周可,今天给大家分享一下 Nebula Graph 在微众银行 ...

  4. Nebula Graph 的 Ansible 实践

    本文首发于 Nebula Graph 公众号 NebulaGraphCommunity,Follow & 看大厂图数据库技术实践 背景 在 Nebula-Graph 的日常测试中,我们会经常在 ...

  5. Nebula Graph 在网易游戏业务中的实践

    本文首发于 Nebula Graph Community 公众号 当游戏上知识图谱,网易游戏是如何应对大规模图数据的管理问题,Nebula Graph 又是如何帮助网易游戏落地游戏内复杂的图的业务呢? ...

  6. GraphX 在图数据库 Nebula Graph 的图计算实践

    不同来源的异构数据间存在着千丝万缕的关联,这种数据之间隐藏的关联关系和网络结构特性对于数据分析至关重要,图计算就是以图作为数据模型来表达问题并予以解决的过程. 一.背景 随着网络信息技术的飞速发展,数 ...

  7. 解析 Nebula Graph 子图设计及实践

    本文首发于 Nebula Graph 公众号 NebulaGraphCommunity,Follow 看大厂图数据库技术实践. 前言 在先前的 Query Engine 源码解析中,我们介绍了 2.0 ...

  8. 分布式图数据库 Nebula Graph 中的集群快照实践

    1 概述 1.1 需求背景 图数据库 Nebula Graph 在生产环境中将拥有庞大的数据量和高频率的业务处理,在实际的运行中将不可避免的发生人为的.硬件或业务处理错误的问题,某些严重错误将导致集群 ...

  9. Nebula Graph 技术总监陈恒:图数据库怎么和深度学习框架进行结合?

    引子 Nebula Graph 的技术总监在 09.24 - 09.30 期间同开源中国·高手问答的小伙伴们以「图数据库的设计和实践」为切入点展开讨论,包括:「图数据库的存储设计」.「图数据库的计算设 ...

  10. 图数据库 Nebula Graph 的安装部署

    Nebula Graph:一个开源的分布式图数据库.作为唯一能够存储万亿个带属性的节点和边的在线图数据库,Nebula Graph 不仅能够在高并发场景下满足毫秒级的低时延查询要求,还能够实现服务高可 ...

随机推荐

  1. 【AIGC】只要10秒,AI生成IP海报,解放双手!!!

    看完这篇文章,你将学会以下价值连城的内容 1.云端部署(配置不行的小伙伴看)+ 云端模型放置位置 2.本地部署(配置达标的小伙伴看) 3.运用SD训练IP的流程和技巧(LoRA篇) 4.运用SD稳定生 ...

  2. css3写一个加载动画

    先制作一个正方形,让圆点在正方形的最外侧 <style> body { margin: 0; } .loading { width: 200px; height: 200px; backg ...

  3. 【JS 逆向百例】猿人学系列 web 比赛第五题:js 混淆 - 乱码增强,详细剖析

    逆向目标 猿人学 - 反混淆刷题平台 Web 第五题:js 混淆,乱码增强 目标:抓取全部 5 页直播间热度,计算前 5 名直播间热度的加和 主页:https://match.yuanrenxue.c ...

  4. 【JS 逆向百例】某空气质量监测平台无限 debugger 以及数据动态加密

    关注微信公众号:K哥爬虫,持续分享爬虫进阶.JS/安卓逆向等技术干货! 声明 本文章中所有内容仅供学习交流,抓包内容.敏感网址.数据接口均已做脱敏处理,严禁用于商业用途和非法用途,否则由此产生的一切后 ...

  5. 【NSSCTF-Round#16】 Web和Crypto详细完整WP

    每天都要加油哦!    ------2024-01-18  11:16:55 [NSSRound#16 Basic]RCE但是没有完全RCE <?php error_reporting(0); ...

  6. CouchDB vs. LevelDB

    CouchDB 和 LevelDB 都是数据库系统,但它们在很多方面有着不同的设计和应用重点.下面是对这两个数据库在一些关键点上的对比: 数据模型: CouchDB:CouchDB 是一种面向文档的数 ...

  7. windwos10-11打开任意文件弹出警告

    如下打开exe或者视频.图片都弹出警告 解决方案输入快捷键win+s换出搜索框 输入Internet 选项 进入安全选项点击自定义级别 找到,加载应用程序和不安全文件 勾选启用(不安全) 然后确定-在 ...

  8. CreateProcess函数源码分析

    CreateProcess函数源码分析 ​ 源码版本:Windows 2003 源码 ​ 源码阅读工具:Source Insight 函数功能分析 函数原型 BOOL CreateProcessA( ...

  9. C/C++ MinHook 库的使用技巧

    在上面的各种Hook挂钩方式中,我们都是在手写封装代码,但这样的方式并不高效,真正的生产环境中我们必须使用现成的Hook库,常用的Hook库有免费开源的MinHook和商业的Detours Hook, ...

  10. axios请求失败,获取接口返回错误信息

    一般vue项目都会对axios进行封装,后台统一规范默认让服务器对所有请求都返回成功,然后在成功的对象里面包装一层对象result,里面也包含code,msg,result信息,前端拿这个result ...