Java SDK 接口说明
obvec_jdbc 是一个专为 seekdb 向量存储场景和 JSON Table 虚拟表场景实现的 Java SDK。本文介绍了 obvec_jdbc 的使用方法。
安装
可选择下述任一方式安装 obvec_jdbc。
maven 依赖
在你项目的 pom.xml 文件中添加 obvec_jdbc 的依赖。
<dependency>
<groupId>com.oceanbase</groupId>
<artifactId>obvec_jdbc</artifactId>
<version>1.0.4</version>
</dependency>
源码安装
-
安装 obvec_jdbc。
# 克隆 obvec_jdbc 仓库
git clone https://github.com/oceanbase/obvec_jdbc.git
# 进入 obvec_jdbc 目录
cd obvec_jdbc
# 安装 obvec_jdbc
mvn install -
添加依赖
<dependency>
<groupId>com.oceanbase</groupId>
<artifactId>obvec_jdbc</artifactId>
<version>1.0.4</version>
</dependency>
API 接口定义与使用
obvec_jdbc 提供了 ObVecClient 对象来操作 seekdb 向量搜索功能和 JSON Table 虚拟表功能。
使用向量搜索功能
创建一个客户端
使用以下接口定义构造 ObVecClient 对象:
# uri:连接串,包含地址和端口以及连接的数据库名等
# user:用户名
# password:密码
public ObVecClient(String uri, String user, String password);
示例如下:
import com.oceanbase.obvec_jdbc.ObVecClient;
String uri = "jdbc:oceanbase://127.0.0.1:2881/test";
String user = "root@test";
String password = "";
String tb_name = "JAVA_TEST";
ObVecClient ob = new ObVecClient(uri, user, password);
ObFieledSchema 类
该类用于定义表的列 Schema,构造函数如下:
# name:列名
# dataType:列类型
public ObFieldSchema(String name, DataType dataType);
列类型目前支持的数据类型如下:
| DataType | 描述 |
|---|---|
| BOOL | 等价于 TINYINT |
| INT8 | 等价于 TINYINT |
| INT16 | 等价于 SMALLINT |
| INT32 | 等价于 INT |
| INT64 | 等价于 BIGINT |
| FLOAT | 等价于 FLOAT |
| DOUBLE | 等价于 DOUBLE |
| STRING | 等价于 LONGTEXT |
| VARCHAR | 等价于 VARCHAR |
| JSON | 等价于 JSON |
| FLOAT_VECTOR | 等价于 VECTOR |
对于更复杂的类型、约束等,可自行使用 seekdb JDBC 的接口进行创建,而不使用 obvec_jdbc。
接口定义如下:
| API 接口 | 描述 |
|---|---|
| String getName() | 获取列名。 |
| ObFieldSchema Name(String name) | 设置列名,返回对象本身方便链式操作。 |
| ObFieldSchema DataType(DataType dataType) | 设置数据类型。 |
| boolean getIsPrimary() | 是否是主键列。 |
| ObFieldSchema IsPrimary(boolean isPrimary) | 设置是否是主键。 |
| ObFieldSchema IsAutoInc(boolean isAutoInc) | 设置是否自增。注意IsAutoInc 需要在 IsPrimary 为真的情况才生效。 |
| ObFieldSchema IsNullable(boolean isNullable) | 设置是否可以为 NULL。注意IsNullable 默认是假,与 MySQL 行为不同。 |
| ObFieldSchema MaxLength(int maxLength) | 对于 VARCHAR 类型设置最大长度。 |
| ObFieldSchema Dim(int dim) | 对于 VECTOR 类型设置维度。 |
IndexParams/IndexParam
IndexParam 用于设置单个索引参数。IndexParams 用于设置一组向量索引参数,在某张表上创建多个向量索引时使用。
obvec_jdbc 仅支持创建向量索引,需要创建其他索引需使用 seekdb JDBC。
IndexParam 的构造函数如下:
# vidx_name:索引名
# vector_field_name:向量列名
public IndexParam(String vidx_name, String vector_field_name);
接口定义如下:
| API 接口 | 描述 |
|---|---|
| IndexParam M(int m) | 获取 HNSW 算法中每个向量的最大邻居数。 |
| IndexParam EfConstruction(int ef_construction) | 设置 HNSW 算法构造时搜索的最大候选向量数。 |
| IndexParam EfSearch(int ef_search) | 设置 HNSW 算法中搜索时的最大候选向量数。 |
| IndexParam Lib(String lib) | 设置使用的向量库类型。 |
| IndexParam MetricType(String metric_type) | 设置向量距离函数类型。 |
IndexParams 的构造函数如下:
public IndexParams();
接口定义如下:
| API 接口 | 描述 |
|---|---|
| void addIndex(IndexParam index_param) | 加入一个索引定义。 |
ObCollectionSchema 类
在创建表时需要依赖于 ObCollectionSchema 对象的设置,所以首先介绍该类的构造方法和接口。
ObCollectionSchema 的构造函数如下:
public ObCollectionSchema();
接口定义如下:
| API 接口 | 描述 |
|---|---|
| void addField(ObFieldSchema field) | 添加一个列定义。 |
| void setIndexParams(IndexParams index_params) | 设置表的向量索引参数。 |
删除表
构造函数如下:
# table_name:目标表名
public void dropCollection(String table_name);
检查表是否存在
构造函数如下:
# table_name:目标表名
public boolean hasCollection(String table_name);
创建表
构造函数如下:
# table_name:目标表名
# collection:ObCollectionSchema 类型数据结构,表的 Schema
public void createCollection(String table_name, ObCollectionSchema collection);
借助 ObFieldSchema、OCollectionSchema 以及 IndexParams 创建表的示例如下:
import com.oceanbase.obvec_jdbc.DataType;
import com.oceanbase.obvec_jdbc.ObCollectionSchema;
import com.oceanbase.obvec_jdbc.ObFieldSchema;
import com.oceanbase.obvec_jdbc.IndexParam;
import com.oceanbase.obvec_jdbc.IndexParams;
# 定义表schema
ObCollectionSchema collectionSchema = new ObCollectionSchema();
ObFieldSchema c1_field = new ObFieldSchema("c1", DataType.INT32);
c1_field.IsPrimary(true).IsAutoInc(true);
ObFieldSchema c2_field = new ObFieldSchema("c2", DataType.FLOAT_VECTOR);
c2_field.Dim(3).IsNullable(false);
ObFieldSchema c3_field = new ObFieldSchema("c3", DataType.JSON);
c3_field.IsNullable(true);
collectionSchema.addField(c1_field);
collectionSchema.addField(c2_field);
collectionSchema.addField(c3_field);
# 定义索引
IndexParams index_params = new IndexParams();
IndexParam index_param = new IndexParam("vidx1", "c2");
index_params.addIndex(index_param);
collectionSchema.setIndexParams(index_params);
ob.createCollection(tb_name, collectionSchema);
后建向量索引
构造函数如下:
# table_name:目标表名
# index_param:IndexParam 类型数据结构,表的向量索引参数
public void createIndex(String table_name, IndexParam index_param)
插入数据
构造函数如下:
# table_name:目标表名
# column_names:目标表的列名数组
# rows:数据行。ArrayList<Sqlizable[]>,每行数据是一个 Sqlizable 数组。Sqlizable 是一个用于将 Java 数据类型转换为 SQL 数据类型的包装类
public void insert(String table_name, String[] column_names, ArrayList<Sqlizable[]> rows);
rows 支持的数据类型包括:
- SqlInteger:包装 Int 类型的数据。
- SqlFloat:包装 Float 类型数 据。
- SqlDouble:包装 Double 类型数据。
- SqlText:包装 String 类型数据。
- SqlVector:包装 Vector 类型数据。
示例如下:
import com.oceanbase.obvec_jdbc.SqlInteger;
import com.oceanbase.obvec_jdbc.SqlText;
import com.oceanbase.obvec_jdbc.SqlVector;
import com.oceanbase.obvec_jdbc.Sqlizable;
ArrayList<Sqlizable[]> insert_rows = new ArrayList<>();
Sqlizable[] ir1 = { new SqlVector(new float[] {1.0f, 2.0f, 3.0f}), new SqlText("{\"doc\": \"oceanbase doc 1\"}") };
insert_rows.add(ir1);
Sqlizable[] ir2 = { new SqlVector(new float[] {1.1f, 2.2f, 3.3f}), new SqlText("{\"doc\": \"oceanbase doc 2\"}") };
insert_rows.add(ir2);
Sqlizable[] ir3 = { new SqlVector(new float[] {0f, 0f, 0f}), new SqlText("{\"doc\": \"oceanbase doc 3\"}") };
insert_rows.add(ir3);
ob.insert(tb_name, new String[] {"c2", "c3"}, insert_rows);
删除数据
构造函数如下:
# table_name:目标表名
# primary_key_name:主键列名
# primary_keys:目标行的主键列值数组
public void delete(String table_name, String primary_key_name, ArrayList<Sqlizable> primary_keys);
示例如下:
ArrayList<Sqlizable> ids = new ArrayList<>();
ids.add(new SqlInteger(2));
ids.add(new SqlInteger(1));
ob.delete(tb_name, "c1", ids);
ANN 查询
构造函数如下:
# table_name:目标表名
# vec_col_name:向量列名
# metric_type:向量距离函数类型。l2:对应 l2 距离函数;cosine:对应 cosine 距离函数;ip:对应 negative_inner_product 距离函数
# qv:待查询的向量值
# topk:返回 top k 个最相似的结果
# output_fields:投影列,即返回的字段数组
# output_datatypes:投影列的数据类型,即返回的字段数据类型,用于直接转换为 Java 数据类型
# where_expr:WHERE 条件表达式
public ArrayList<HashMap<String, Sqlizable>> query(
String table_name,
String vec_col_name,
String metric_type,
float[] qv,
int topk,
String[] output_fields,
DataType[] output_datatypes,
String where_expr);
示例如下:
ArrayList<HashMap<String, Sqlizable>> res = ob.query(tb_name, "c2", "l2",
new float[] {0f, 0f, 0f}, 10,
new String[] {"c1", "c3", "c2"},
new DataType[] {
DataType.INT32,
DataType.JSON,
DataType.FLOAT_VECTOR,
"c1 > 0"});
if (res != null) {
for (int i = 0; i < res.size(); i++) {
for (HashMap.Entry<String, Sqlizable> entry : res.get(i).entrySet()) {
System.out.printf("%s : %s, ", entry.getKey(), entry.getValue().toString());
}
System.out.print("\n");
}
} else {
System.out.println("res is null");
}
使用 JSON Table 功能
obvec_jdbc 的 JSON Table 功能依赖于 seekdb 对 JSON 数据类型的操作处理能力(包括 JSON_VALUE/JSON_TABLE/JSON_REPLACE 等)实现了一套虚拟表机制,多个用户(以用户 id 做区分)可以在同一张物理表上实现对虚拟表的 DDL 或者 DML 操作的同时保证用户之间的数据隔离。管理员用户可以操作 DDL,普通用户可以操作 DML。
这种设计结合了关系型数据库的结构化管理能力和 JSON 的灵活性,体现了 seekdb 数据库的多模一体化能力。用户既能享受到 SQL 的强大功能和易用性,又能处理半结构化数据,适应了现代应用对数据模型多样性的需求。操作的仍然是“表”,但底层却能以更灵活的 JSON 格式存储数据,从而更好地支持复杂多变的应用场景。
原理说明
这里以一张原理图说明 JSON Table 的原理。

具体说明如下:
-
用户操作:用户仍然使用熟悉的标准 SQL 语句(例如
CREATE TABLE创建表结构,INSERT插入数据,SELECT查询数据)来与系统交互,无需关心数据在底层是如何存储的,就像操作普通的关系型数据库表一样。用户通过 SQL 语句创建的表是一个逻辑表,在 seekdb 数据库内部会对应两个物理表meta_json_t和data_json_t。 -
JSON Table SDK:在应用程序内部,有一个 JSON Table SDK(软件开发工具包)。这个 SDK 是连接用户的 SQL 操作和 seekdb 数据库实际存储的关键。当执行 SQL 语句时,SDK 会拦截这些请求,并智能地将其转换为对 seekdb 数据库内部表
meta_json_t和data_json_t的读写操作。 -
seekdb 数据库内部:
meta_json_t(存储表结构):用于存储用户创建的逻辑表的元数据,也就是表的结构信息(例如,创建的表有哪些列,每个列的数据类型是什么)。当执行CREATE TABLE时,SDK 会将这些结构信息记录到meta_json_t中。data_json_t(将行数据存储为 JSON 类型):用于存储实际插入的数据。与传统关系型数据库直接存储行数据不同,JSON Table 功能会将插入的每一行数据封装成一个 JSON 对象,然后存储在data_json_t表的某个列中。这样,即使数据结构比较灵活,也能高效存储。
-
数据查询:当执行
SELECT等查询操作时,SDK 会从data_json_t中读取 JSON 格式的数据,并结合meta_json_t中的表结构信息,将这些 JSON 数据重新解析并呈现为你熟悉的表格形式,返回给你的应用程序。
meta_json_t 表存储了 JSON Table 的元数据信息,即用户通过 CREATE TABLE 定义的逻辑表结构。它记录了每个逻辑表的列信息,表结构如下:
| 字段名 | 说明 | 示例 |
|---|---|---|
user_id | 用户 ID,用于区分不同用户的逻辑表。 | 0, 1, 2 |
jtable_name | 逻辑表的名称。 | test_count |
jcol_id | 逻辑表中的列 ID。 | 1, 2, 3 |
jcol_name | 逻辑表中的列名。 | c1, c2, c3 |
jcol_type | 列的数据类型。 | INT, VARCHAR(124), DECIMAL(10,2) |
jcol_nullable | 列是否可为空。 | 0, 1 |
jcol_has_default | 列是否有默认值。 | 0, 1 |
jcol_default | 列的默认值。 | {'default': null} |
当用户执行 CREATE TABLE 时,JSON Table SDK 会将这些列定义信息解析并插入到 meta_json_t 表中。
data_json_t 表存储了 JSON Table 的实际数据,即用户通过 INSERT 插入的数据。它记录了每个逻辑表的行数据,表结构如下:
| 字段名 | 说明 | 示例 |
|---|---|---|
user_id | 用户 ID,用于区分不同用户的逻辑表。 | 0, 1, 2 |
admin_id | 管理员用户 ID。 | 0 |
jtable_name | 逻辑表的名称,用于关联 meta_json_t 中的元数据。 | test_count |
jdata_id | 数据 ID,JSON 数据的唯一标识符,对应逻辑表中的每一行。 | 1, 2, 3 |
jdata | 这是一个 JSON 类型的列,用于存储逻辑表中的实际行数据。 | {"c1": 1, "c2": "test", "c3": 1.23} |
使用示例
-
创建一个客户端
构造函数如下:
# uri:连接串,包含地址和端口以及连接的数据库名等
# user:用户名
# password:密码
# user_id:用户 id
# log_level:日志级别
public ObVecJsonClient(String uri, String user, String password, String user_id, Level log_level);示例如下:
import com.oceanbase.obvec_jdbc.ObVecJsonClient;
String uri = "jdbc:oceanbase://127.0.0.1:2881/test";
String user = "root@test";
String password = "";
ObVecJsonClient client = new ObVecJsonClient(uri, user, password, 0, Level.INFO); -
执行 DDL 语句
直接调用
parseJsonTableSQL2NormalSQL接口传入具体的 SQL 语句即可使用。-
创建表
String sql = "CREATE TABLE `t2` (c1 INT NOT NULL DEFAULT 10, c2 VARCHAR(30) DEFAULT 'ca', c3 VARCHAR NOT NULL, c4 DECIMAL(10, 2), c5 TIMESTAMP DEFAULT CURRENT_TIMESTAMP);";
client.parseJsonTableSQL2NormalSQL(sql); -
ALTER TABLE CHANGE COLUMN
sql = "ALTER TABLE t2 CHANGE COLUMN c2 changed_col INT";
client.parseJsonTableSQL2NormalSQL(sql); -
ALTER TABLE ADD COLUMN
sql = "ALTER TABLE t2 ADD COLUMN email VARCHAR(100) default 'example@example.com'";
client.parseJsonTableSQL2NormalSQL(sql); -
ALTER TABLE MODIFY COLUMN
sql = "ALTER TABLE t2 MODIFY COLUMN changed_col TIMESTAMP NOT NULL DEFAULT current_timestamp";
client.parseJsonTableSQL2NormalSQL(sql); -
ALTER TABLE DROP COLUMN
sql = "ALTER TABLE t2 DROP c1";
client.parseJsonTableSQL2NormalSQL(sql); -
ALTER TABLE RENAME
sql = "ALTER TABLE t2 RENAME TO alter_test";
client.parseJsonTableSQL2NormalSQL(sql);
-