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

  • (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)映射到 JDBC OTHER,并作为 java.util.UUID 值展现。