Arrow JDBC 适配器¶
Arrow JDBC 适配器旨在协助处理 JDBC 和 Arrow 数据。目前,它支持将 JDBC ResultSet 读取为 Arrow VectorSchemaRoots。
ResultSet 到 VectorSchemaRoot 的转换¶
可以通过 JdbcToArrow 类进行访问。生成的 ArrowVectorIterator 会将 ResultSet 以批处理行的方式转换为 Arrow 数据。
try (ArrowVectorIterator it = JdbcToArrow.sqlToArrowVectorIterator(resultSet, allocator)) {
while (it.hasNext()) {
VectorSchemaRoot root = it.next();
// Consume the root…
}
}
批处理大小和类型映射均可自定义。
JdbcToArrowConfig config = new JdbcToArrowConfigBuilder(allocator, /*calendar=*/null)
.setReuseVectorSchemaRoot(reuseVectorSchemaRoot)
.setJdbcToArrowTypeConverter((jdbcFieldInfo -> {
switch (jdbcFieldInfo.getJdbcType()) {
case Types.BIGINT:
// Assume actual value range is SMALLINT
return new ArrowType.Int(16, true);
default:
return null;
}
}))
.build();
try (ArrowVectorIterator iter = JdbcToArrow.sqlToArrowVectorIterator(rs, config)) {
while (iter.hasNext()) {
VectorSchemaRoot root = iter.next();
// Consume the root…
}
}
JDBC 类型可以显式指定,这很有用,因为 JDBC 驱动程序有时会提供虚假的类型信息。例如,已观察到 Postgres 驱动程序会使用比例(scale)和精度(precision)均为 0 的 Decimal 类型;通过在读取前显式指定类型可以处理这些情况。此外,某些 JDBC 驱动程序可能会返回比例不一致的 BigDecimal 值。可以设置 RoundingMode 来处理这些情况。
Map<Integer, JdbcFieldInfo> mapping = new HashMap<>();
mapping.put(1, new JdbcFieldInfo(Types.DECIMAL, 20, 7));
JdbcToArrowConfig config = new JdbcToArrowConfigBuilder(allocator, /*calendar=*/null)
.setBigDecimalRoundingMode(RoundingMode.UNNECESSARY)
.setExplicitTypesByColumnIndex(mapping)
.build();
try (ArrowVectorIterator iter = JdbcToArrow.sqlToArrowVectorIterator(rs, config)) {
while (iter.hasNext()) {
VectorSchemaRoot root = iter.next();
// Consume the root…
}
}
从 JDBC 类型到 Arrow 类型的映射可以通过 JdbcToArrowConfig 进行覆盖,但无法自定义从 JDBC 值到 Arrow 值本身的转换,也无法为不支持的类型定义转换。
类型映射¶
JDBC 到 Arrow 的类型映射可以在运行时从 JdbcToArrowUtils.getArrowTypeFromJdbcType 获取。
JDBC 类型 |
Arrow 类型 |
备注 |
|---|---|---|
ARRAY |
列表 |
(1) |
BIGINT |
Int64 |
|
BINARY |
Binary |
|
BIT |
Bool |
|
BLOB |
Binary |
|
BOOLEAN |
Bool |
|
CHAR |
Utf8 字符串 |
|
CLOB |
Utf8 字符串 |
|
DATE |
Date32 |
|
DECIMAL |
Decimal128 |
(2) |
DOUBLE |
Double |
|
FLOAT |
Float32 |
|
INTEGER |
Int32 |
|
LONGVARBINARY |
Binary |
|
LONGNVARCHAR |
Utf8 字符串 |
|
LONGVARCHAR |
Utf8 字符串 |
|
NCHAR |
Utf8 字符串 |
|
NULL |
Null |
|
NUMERIC |
Decimal128 |
|
NVARCHAR |
Utf8 字符串 |
|
REAL |
Float32 |
|
SMALLINT |
Int16 |
|
STRUCT |
结构体 |
(3) |
TIME |
Time32[ms] |
|
TIMESTAMP |
Timestamp[ms] |
(4) |
TINYINT |
Int8 |
|
VARBINARY |
Binary |
|
VARCHAR |
Utf8 字符串 |
(1) 列表值的类型必须显式配置,不能推断。请使用 setArraySubTypeByColumnIndexMap 或 setArraySubTypeByColumnNameMap。
(2) 默认情况下,十进制值的比例(scale)必须与类型中的比例完全匹配;精度(precision)允许为任何大于或等于类型精度的值。如果存在不匹配,默认会抛出异常。这可以通过使用 setBigDecimalRoundingMode 设置不同的 RoundingMode 来进行配置。
(3) 未完全支持:虽然定义了类型转换,但未定义值转换。请参见 ARROW-17006。
(4) 如果提供了 Calendar,则时间戳将具有该日历的时区;否则,它将是不带时区的时间戳。
VectorSchemaRoot 到 PreparedStatement 参数的转换¶
该适配器可以将 VectorSchemaRoot 中的 Arrow 数据行绑定到 JDBC PreparedStatement 的参数中。这可以通过 JdbcParameterBinder 类访问。每次调用 next() 时,都会绑定来自下一行数据的参数,随后应用程序可以根据需要执行语句、调用 addBatch() 等。空值(Null)将导致调用带有适当 JDBC 类型代码(如下所列)的 setNull。
final JdbcParameterBinder binder =
JdbcParameterBinder.builder(statement, root).bindAll().build();
while (binder.next()) {
statement.executeUpdate();
}
// Use a VectorLoader to update the root
binder.reset();
while (binder.next()) {
statement.executeUpdate();
}
向量到参数的映射、转换器使用的 JDBC 类型代码以及类型转换本身均可自定义。
final JdbcParameterBinder binder =
JdbcParameterBinder.builder(statement, root)
.bind(/*parameterIndex*/2, /*columnIndex*/0)
.bind(/*parameterIndex*/1, customColumnBinderInstance)
.build();
类型映射¶
Arrow 到 JDBC 的类型映射可以在运行时通过 ColumnBinder 上的方法获取。Flight SQL JDBC 驱动程序遵循相同的映射,并对下述 UUID 扩展类型提供额外支持。
Arrow 类型 |
JDBC 类型 |
备注 |
|---|---|---|
Binary |
VARBINARY (setBytes) |
|
Bool |
BOOLEAN (setBoolean) |
|
Date32 |
DATE (setDate) |
|
Date64 |
DATE (setDate) |
|
Decimal128 |
DECIMAL (setBigDecimal) |
|
十进制数256 |
DECIMAL (setBigDecimal) |
|
FixedSizeBinary |
BINARY (setBytes) |
|
Uuid (extension) |
OTHER (setObject) |
(3) |
Float32 |
REAL (setFloat) |
|
Int8 |
TINYINT (setByte) |
|
Int16 |
SMALLINT (setShort) |
|
Int32 |
INTEGER (setInt) |
|
Int64 |
BIGINT (setLong) |
|
大二进制 |
LONGVARBINARY (setBytes) |
|
LargeUtf8 |
LONGVARCHAR (setString) |
(1) |
Time[s] |
TIME (setTime) |
|
Time[ms] |
TIME (setTime) |
|
Time[us] |
TIME (setTime) |
|
Time[ns] |
TIME (setTime) |
|
Timestamp[s] |
TIMESTAMP (setTimestamp) |
(2) |
Timestamp[ms] |
TIMESTAMP (setTimestamp) |
(2) |
Timestamp[us] |
TIMESTAMP (setTimestamp) |
(2) |
Timestamp[ns] |
TIMESTAMP (setTimestamp) |
(2) |
Utf8 字符串 |
VARCHAR (setString) |
(1) 长度超过 Integer.MAX_VALUE 字节(Java
byte[]的最大长度)的字符串将导致运行时异常。(2) 如果时间戳带有时区,JDBC 类型默认为 TIMESTAMP_WITH_TIMEZONE。如果时间戳没有时区,从 Arrow 值到 JDBC 值的转换在技术上是不准确的,因为 JDBC 时间戳位于 UTC 中,而我们没有时区信息。在这种情况下,默认的绑定器将调用 setTimestamp(int, Timestamp),这将导致驱动程序使用“默认时区”(即 Java 虚拟机所在时区)。
(3) 对于 Flight SQL JDBC 驱动程序,Arrow UUID 扩展类型(
arrow.uuid)映射到 JDBCOTHER,并作为java.util.UUID值展现。