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

类型映射#

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