跳到主要内容

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>

源码安装

  1. 安装 obvec_jdbc。

    # 克隆 obvec_jdbc 仓库
    git clone https://github.com/oceanbase/obvec_jdbc.git
    # 进入 obvec_jdbc 目录
    cd obvec_jdbc
    # 安装 obvec_jdbc
    mvn install
  2. 添加依赖

    <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 的原理。

JSON Table 原理

具体说明如下:

  1. 用户操作:用户仍然使用熟悉的标准 SQL 语句(例如 CREATE TABLE 创建表结构,INSERT 插入数据,SELECT 查询数据)来与系统交互,无需关心数据在底层是如何存储的,就像操作普通的关系型数据库表一样。用户通过 SQL 语句创建的表是一个逻辑表,在 seekdb 数据库内部会对应两个物理表 meta_json_tdata_json_t

  2. JSON Table SDK:在应用程序内部,有一个 JSON Table SDK(软件开发工具包)。这个 SDK 是连接用户的 SQL 操作和 seekdb 数据库实际存储的关键。当执行 SQL 语句时,SDK 会拦截这些请求,并智能地将其转换为对 seekdb 数据库内部表 meta_json_tdata_json_t 的读写操作。

  3. seekdb 数据库内部:

    • meta_json_t (存储表结构):用于存储用户创建的逻辑表的元数据,也就是表的结构信息(例如,创建的表有哪些列,每个列的数据类型是什么)。当执行 CREATE TABLE 时,SDK 会将这些结构信息记录到 meta_json_t 中。
    • data_json_t (将行数据存储为 JSON 类型):用于存储实际插入的数据。与传统关系型数据库直接存储行数据不同,JSON Table 功能会将插入的每一行数据封装成一个 JSON 对象,然后存储在 data_json_t 表的某个列中。这样,即使数据结构比较灵活,也能高效存储。
  4. 数据查询:当执行 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}

使用示例

  1. 创建一个客户端

    构造函数如下:

    # 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);
  2. 执行 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);