Arrow JDBC 适配器#

Arrow JDBC 适配器协助处理 JDBC 和 Arrow 数据。目前,它支持将 JDBC ResultSets 读取到 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 驱动程序可能会返回具有不一致 scale 的 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

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) 必须显式配置 list 值类型,不能推断。使用 setArraySubTypeByColumnIndexMapsetArraySubTypeByColumnNameMap

  • (2) 默认情况下,decimal 值的 scale 必须与类型中的 scale 完全匹配;精度允许为大于或等于类型精度的任何值。如果存在不匹配,默认情况下将抛出异常。可以通过使用 setBigDecimalRoundingMode 设置不同的 RoundingMode 来配置此行为。

  • (3) 不完全支持:虽然定义了类型转换,但未定义值转换。请参阅 ARROW-17006

  • (4) 如果提供了 Calendar,则时间戳将具有 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 Timestamp 采用 UTC,并且我们没有时区信息。 在这种情况下,默认的绑定器将调用 setTimestamp(int, Timestamp),这将导致驱动程序使用“默认时区”(Java VM 的时区)。