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

  • (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();

类型映射#

可以通过 ColumnBinder 上的方法在运行时获取 Arrow 到 JDBC 的类型映射。

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 的时区)。