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 驱动程序使用比例和精度为 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…
}
}
可以通过 JdbcToArrowConfig
覆盖从 JDBC 类型到 Arrow 类型的映射,但无法自定义从 JDBC 值到 Arrow 值本身的转换,也无法为不受支持的类型定义转换。
类型映射#
JDBC 到 Arrow 类型映射可以在运行时从 JdbcToArrowUtils.getArrowTypeFromJdbcType 获取。
JDBC 类型 |
Arrow 类型 |
备注 |
---|---|---|
ARRAY |
List |
(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 |
Struct |
(3) |
TIME |
Time32[ms] |
|
TIMESTAMP |
Timestamp[ms] |
(4) |
TINYINT |
Int8 |
|
VARBINARY |
Binary |
|
VARCHAR |
Utf8 |
(1) 列表值类型必须明确配置,不能推断。使用 setArraySubTypeByColumnIndexMap 或 setArraySubTypeByColumnNameMap。
(2) 默认情况下,十进制值的比例必须与类型中的比例完全匹配;精度允许为大于或等于类型精度的任何值。如果存在不匹配,则默认情况下会抛出异常。这可以通过使用 setBigDecimalRoundingMode 设置不同的 RoundingMode 来配置。
(3) 未完全支持:虽然已定义类型转换,但未定义值转换。请参阅 ARROW-17006。
(4) 如果提供了 Calendar,则时间戳将具有日历的时区,否则它将是无时区的时间戳。
VectorSchemaRoot 到 PreparedStatement 参数的转换#
适配器可以将来自 VectorSchemaRoot 的 Arrow 数据行绑定到 JDBC PreparedStatement 的参数。这可以通过 JdbcParameterBinder 类访问。每次调用 next() 都会绑定来自下一行数据的参数,然后应用程序可以根据需要执行语句、调用 addBatch() 等。空值将导致使用适当的 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 上的方法在运行时获取。
Arrow 类型 |
JDBC 类型 |
备注 |
---|---|---|
Binary |
VARBINARY (setBytes) |
|
Bool |
BOOLEAN (setBoolean) |
|
Date32 |
DATE (setDate) |
|
Date64 |
DATE (setDate) |
|
Decimal128 |
DECIMAL (setBigDecimal) |
|
Decimal256 |
DECIMAL (setBigDecimal) |
|
FixedSizeBinary |
BINARY (setBytes) |
|
Float32 |
REAL (setFloat) |
|
Int8 |
TINYINT (setByte) |
|
Int16 |
SMALLINT (setShort) |
|
Int32 |
INTEGER (setInt) |
|
Int64 |
BIGINT (setLong) |
|
LargeBinary |
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 VM 的时区)。