nebula-jdbc 文档

前言

这是我在开源之夏 Summer 2021 中负责的项目的文档,目前这个项目已经通过导师和主办方审核顺利结项啦哈哈哈哈哈。中期报告在我前面的博客中,代码已经合并到 vesoft 的 Github 仓库中,源码及文档见此处

nebula-jdbc 文档

Introduction

nebula-jdbc 是基于 nebula-java 封装的,在其基础上对接了 JDBC 协议,实现 JDBC 的相关接口。比起 nebula-java 你可能更加熟悉 JDBC 的 API,使用 nebula-jdbc 你可以不必熟悉 nebula-java 的 API(熟悉的话会更好,这样你会理解为什么我们连接字符串的格式是为什么与传统的 JDBC 连接字符串不同),像在 java 程序中操作关系型数据库一样操作 Nebula 服务

Architecture

nebula-jdbc 主要的一些类和接口的关系如下:(蓝色实线是类之间的 extends 关系,绿色实线是接口之间的 implements 关系,绿色虚线是抽象类与接口之间的 implements 关系)

nebula-jdbc项目架构

用户首先通过 NebulaDriver 注册驱动,其中有 NebulaPool 属性,用于获取 Session 与数据库通信。NebulaDriver 中提供三个构造函数,无参构造函数按照默认参数配置 NebulaPool,接收一个 Properties 类型参数的构造函数可以自定义 NebulaPool 配置,接收一个 String 类型参数的构造函数可以只指定连接地址,其余参数按照默认配置。

注册驱动后用户可以通过 DriverManager::getConnection(String url) 获取 Connection。在 NebulaConnection 的构造函数中会通过 NebulaDriver 中的 NebulaPool 获取 Session 接着连接到在 url 中指定的图空间(graphSpace)。

获取到 Connection 后用户可以通过 Connection::createStatementConnection::prepareStatement 拿到 Statement 或者 PreparedStatement 对象,调用其中的 executeQuery、executeUpdate、execute 方法向数据库发送命令,数据库执行此命令后的结果会封装在 NebulaResult 中,再调用其中各种获取数据的方法可以得到不同数据类型的数据。

Usage

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
// 获取并注册默认的 NebulaDriver,默认的连接地址是 127.0.0.1:9669,其余的默认参数可以查看 NebulaDriver::setDefaultPoolProperties()
NebulaDriver defaultDriver = new NebulaDriver();

// 如果只想设置连接地,其余参数按照默认配置,则可以使用 NebulaDriver (String address)
NebulaDriver customizedUrlDriver = new NebulaDriver("192.168.66.116:9669");

// 如果要连接特定的服务地址以及自定义连接配置,可以使用自定义 NebulaDriver:将配置参数封装在一个 Properties 对象中,然后调用 NebulaDriver::NebulaDriver(Properties poolProperties)
Properties poolProperties = new Properties();
ArrayList<HostAddress> addressList = new ArrayList<>();
addressList.add(new HostAddress("192.168.66.226", 9669));
addressList.add(new HostAddress("192.168.66.222", 9670));

poolProperties.put("addressList", addressList);
poolProperties.put("minConnsSize", 2);
poolProperties.put("maxConnsSize", 12);
poolProperties.put("timeout", 1015);
poolProperties.put("idleTime", 727);
poolProperties.put("intervalIdle", 1256);
poolProperties.put("waitTime", 1256);

NebulaDriver customizedDriver = new NebulaDriver(poolProperties);

// 获取 Connection
Connection connection = DriverManager.getConnection("jdbc:nebula://JDBC_TEST_SPACE", "root", "nebula123");

// 获取 Statement 并执行
Statement statement = connection.createStatement();

String queryStatementNgql = "match (v:testNode) return v.theString as theString, v.theInt as theInt";
ResultSet queryStatementResult = statement.executeQuery(queryStatementNgql);

// 获取结果
while (queryStatementResult.next()){
String theString = queryStatementResult.getString("theString");
int theInt = queryStatementResult.getInt(2);
}

String insertTestNode = "INSERT VERTEX testNode (theString, theInt, theDouble, theTrueBool, theFalseBool, theDate, theTime, theDatetime) VALUES " +
"\"testNode_7\":(\"Young\", 20, , 12.56, true, false, date(\"1949-10-01\"), time(\"15:00:00.000\"), datetime(\"1949-10-01T15:00:00.000\")); ";
statement.executeUpdate(insertTestNode);

// 获取 PreparedStatement,设置参数并执行
String insertPreparedStatementNgql = "INSERT VERTEX testNode (theString, theInt, theDouble, theTrueBool, theFalseBool, theDate, theTime, theDatetime) VALUES " +
"\"testNode_8\":(?, ?, ?, ?, ?, ?, ?, ?); ";
PreparedStatement insertPreparedStatement = connection.prepareStatement(insertPreparedStatementNgql);

insertPreparedStatement.setString(1, "YYDS");
insertPreparedStatement.setInt(2, 98);
insertPreparedStatement.setDouble(3, 12.56);
insertPreparedStatement.setBoolean(4, true);
insertPreparedStatement.setBoolean(5, false);
insertPreparedStatement.setDate(6, Date.valueOf("1949-10-01"));
insertPreparedStatement.setTime(7, Time.valueOf("15:00:00"));
// 类型转换后再调用 setDatetime
NebulaPreparedStatement nebulaPreparedStatement = (NebulaPreparedStatement) insertPreparedStatement;
nebulaPreparedStatement.setDatetime(8, new java.util.Date());

insertPreparedStatement.execute();

// 关闭连接
connection.close();

Q & A

  • 连接字符串"jdbc:nebula://graphSpace"中不用指定连接地址吗?

由于地址列表已经在 NebulaDriver 中配置(默认或自定义),所以连接字符串不需要指定地址,只需要指定图空间。

  • PreparedStatement 是否有预编译功能?

服务端暂不支持。

  • executeQueryexecuteUpdateexecute 的使用场景?

executeQuery 专门用于查询 Nebula 中的数据,此时 nGql 需包含查询关键字 [“match”, “lookup”, “go”, “fetch”, “find”, “subgraph”],返回查询结果 ResultSetexecuteUpdate 用于修改数据,nGql 需包含修改关键字 [“update”, “delete”, “insert”, “upsert”, “create”, “drop”, “alter”, “rebuild”],返回查询结果 0execute 用于其他 admin 操作,执行成功则返回查询 true

  • executeUpdate 的返回结果是0,为什么不是受到该语句影响的数据量?

目前服务端没有 updateCount 统计返回给用户。假如用户一条插入语句里面同时插入多个点或者多条边,这里面可能有部分成功,但服务端只会返回告诉用户失败了,但是其实用户可能能查到部分数据。统一返回0给用户。

  • 查询语句中返回点、边、路径后在 Result 中应该如何获得?

ResultSet 转为 NebulaResultSet,然后调用 getNodegetEdgegetPath;对于列表、集合、映射也是如此。