使用 MyBatis 操作 Nebula Graph 的实践

ywz888
发布于 2022-10-10 16:25
6204浏览
0收藏

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

>>>>

MyBatis

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

>>>>

实现途径

主要通过 MyBatis 结合 nebula-jdbc https://github.com/nebula-contrib/nebula-jdbc 来实现参数返回值映射以及语句执行。

>>>>

Demo 示例

完整代码参见 https://github.com/DA1Y1/nebula-springboot-mybatis-demo

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);
  • 1.
  • 2.
  • 3.
工程结构

使用 MyBatis 操作 Nebula Graph 的实践-鸿蒙开发者社区

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
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
DO

@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class PlayerDO {
    /**
     * vid
     */
    private String id;
    private String name;
    private Long age;
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
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);
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
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>
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.
  • 31.
  • 32.
  • 33.
  • 34.
  • 35.
  • 36.
  • 37.
  • 38.
  • 39.
  • 40.
  • 41.
  • 42.
  • 43.
  • 44.
  • 45.
  • 46.
  • 47.
  • 48.
  • 49.
  • 50.
  • 51.
  • 52.
  • 53.
  • 54.
  • 55.
  • 56.
  • 57.
  • 58.
  • 59.
  • 60.
  • 61.
  • 62.
  • 63.
  • 64.
  • 65.
  • 66.
  • 67.
  • 68.
  • 69.
  • 70.
  • 71.
  • 72.
  • 73.
  • 74.
  • 75.
  • 76.
  • 77.
  • 78.
  • 79.
  • 80.
  • 81.
  • 82.
  • 83.
  • 84.
  • 85.
  • 86.
  • 87.
  • 88.
  • 89.
  • 90.
  • 91.
  • 92.
  • 93.
  • 94.
  • 95.
  • 96.
  • 97.
  • 98.
  • 99.
  • 100.
  • 101.
  • 102.
  • 103.
  • 104.
  • 105.
  • 106.
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"));
    }
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
Edge 及 Path 操作

篇幅有限,这里不做赘述。详情可以参见 https://github.com/DA1Y1/nebula-springboot-mybatis-demo

>>>>

版本适配

目前仅支持了 Nebula Graph 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


文章转载自公众号:Nebula Graph Community

分类
标签
已于2022-10-10 16:25:46修改
收藏
回复
举报


回复
    相关推荐