本文首发于 Nebula Graph Community 公众号
我最近注意到很多同学对于 ORM 框架的需求比较迫切,而且有热心的同学已经捐赠了自己开发的项目,Nebula 社区也在 working on it。下面主要介绍一下我们在使用 MyBatis 操作 Nebula Graph 方面的一些经验,希望能够帮助到大家。
Java 开发的同学想必对 MyBatis 都比较熟悉了。MyBatis 是一款优秀的持久层框架,它支持自定义 SQL、存储过程以及高级映射,并且免除了几乎所有的 JDBC 代码以及设置参数和获取结果集的工作。
主要通过 MyBatis 结合 nebula-jdbc 来实现参数返回值映射以及语句执行。
完整代码参见 GitHub。
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);
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
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class PlayerDO {
/**
* vid
*/
private String id;
private String name;
private Long age;
}
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);
}
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>
@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"));
}
}
篇幅有限,详情可以参见github。
目前仅支持了 Nebula 2.5 版本,后续版本的支持还在适配中。
优点
存在的问题
MATCH v RETURN v
)、Edge、无属性 Path 的类型目前采用在 MyBatis 中的 Interceptor 做拦截处理,也能满足使用。但这种实现方式感觉不是很好,后期有待优化。为了方便使用我们还开发了类似 mybatis-generator 这种工具来生成一些基础代码,提供基本的增删改查功能。感兴趣的同学可以在 IDEA 的 Plugins 中搜索 Nebula Generator 下载,使用方式参见:https://plugins.jetbrains.com/plugin/18026-nebula-generator
最后感谢 @DA1Y1 以及其他几位小伙伴的贡献!
交流图数据库技术?加入 Nebula 交流群请先填写下你的 Nebula 名片,Nebula 小助手会拉你进群~~