跳转到主要内容
clickhouse-jdbc 基于最新的 Java 客户端实现了标准 JDBC 接口。 如果对性能或直接访问有较高要求,我们建议直接使用最新的 Java 客户端。

环境要求

设置

如果您在应用程序中使用 JDBC 驱动,且该应用程序需要将 jar 添加到 classpath,则需要从以下地址下载 jar:

配置

驱动类: com.clickhouse.jdbc.ClickHouseDriver
com.clickhouse.jdbc.ClickHouseDriver 是新旧 JDBC 实现的门面类,默认使用新的 JDBC 实现。 你可以通过将 clickhouse.jdbc.v1 system 属性设置为 true 来使用旧版 JDBC 实现。该属性应在调用 Driver 类之前设置。另一种在不同版本之间切换的方法是直接使用各版本对应的 Driver 类:
  • com.clickhouse.jdbc.Driver 是新的 JDBC 实现 (V2) 。
  • com.clickhouse.jdbc.DriverV1 是旧的 JDBC 实现 (V1) 。
URL 语法jdbc:(ch|clickhouse)[:<protocol>]://endpoint[:port][/<database>][?param1=value1&param2=value2][#tag1,tag2,...],例如:
  • jdbc:clickhouse:http://localhost:8123
  • jdbc:clickhouse:https://localhost:8443?ssl=true
关于 URL 语法,有几点需要注意:
  • URL 中允许有一个端点
  • 当 protocol 不是默认协议 ‘HTTP’ 时,应显式指定
  • 端口不是默认值 ‘8123’ 时,应显式指定端口
  • 驱动程序不会根据端口来猜测协议,你需要明确指定协议
  • 指定了 protocol 时,不需要 ssl 参数。

连接属性

主要配置参数在 Java client 中定义,应原样传递给 driver。Driver 还有一些自有属性,不属于客户端配置的范畴,详见下文。驱动属性
服务器设置所有服务器设置都应加上 clickhouse_setting_ 前缀 (与客户端配置中的要求相同)。
示例配置
这等同于以下 JDBC URL:
注意:无需对 JDBC URL 或属性进行 URL 编码,系统会自动完成编码。只读 profile我们有意避免在连接属性中添加默认设置,以防止与只读 profile 产生冲突。 但某些用户需要传递格式设置 (例如将 JSON 读取为 String) ,对此我们建议使用 readonly=2 profile。 有关只读 profile 的更多信息,请参阅此处

客户端标识

有两种方式可以标识发起请求的应用程序:通过连接属性设置 com.clickhouse.client.api.ClientConfigProperties#CLIENT_NAME,或使用 java.sql.Connection#setClientInfo(String name, String value) 方法。
showLineNumbers
showLineNumbers
两种方式都会在查询日志中生成以下 http_user_agent 值:
注意: 建议将 client_name 属性设置为 app_name/version 格式,以便在查询日志中识别应用程序。

操作标识

JDBC 驱动为每个操作生成 query_id (目前该信息包含在服务器异常中) 。要为某个操作设置 log_comment,请使用 com.clickhouse.jdbc.StatementImpl#getLocalSettings 方法。这需要先将 StatementPreparedStatement 转型为 com.clickhouse.jdbc.StatementImpl
showLineNumbers
注意: 此方法仅适用于单线程执行语句的场景,因为 localSettings 在多个线程之间是共享的。

支持的数据类型

JDBC 驱动支持与底层 java client 相同的数据格式。

JDBC 类型映射

以下映射适用于:
  • ResultSet#getObject(columnIndex) - 此方法会返回对应 Java 类的对象。 (Int8 -> java.lang.ByteInt16 -> java.lang.Short,等等。)
  • ResultSetMetaData#getColumnType(columnIndex) - 该方法会返回对应的 JDBC 类型。 (Int8 -> java.lang.ByteInt16 -> java.lang.Short,等等。)
有几种方法可以更改映射关系:
  • ResultSet#getObject(columnIndex, class) - 该方法会尝试将该值转换为 class 类型。此类转换存在一些限制。详见各节。
数值类型
  • 数值类型之间可以相互转换。因此,Int8 可以获取为 Float64,反之亦然。:
    • rs.getObject(1, Float64.class) 将返回 Int8 列的 Float64 值。
    • rs.getLong(1) 将返回 Int8 列的 Long 值。
    • 如果 Int16 列的值可以放入 Byters.getByte(1) 就可以返回 Byte 值。
  • 不建议将较宽类型转换为较窄类型,因为存在数据损坏的风险。
  • Bool 类型也可视为数值。
  • 所有数值类型都可以作为 java.lang.String 读取。
  • 将 Java Float.MAX_VALUE 存储为 Float 时会出现问题 (https://github.com/ClickHouse/clickhouse-java/issues/809) 。将同一数值存储为 Double 即可解决该问题。
String 类型
  • String 只能作为 java.lang.Stringbyte[] 读取。
  • FixedString 会按原样读取,并以零填充至该列的长度。 (例如,FixedString(10) 中的 'John' 会被读取为 'John\0\0\0\0\0\0\0\0\0'。)
枚举类型
  • Enum8Enum16 默认会映射为 java.lang.String
  • 枚举值可以通过指定的 getter 方法或 getObject(columnIndex, Integer.class) 方法按数值读取。
  • Enum16 在内部映射为 short,Enum8 在内部映射为 byte。由于存在数据损坏风险,应避免将 Enum16 读取为 byte。
  • 枚举值可在 PreparedStatement 中设置为字符串或数值。
日期/时间类型
  • Date / Time 类型会映射为 java.sql 类型,以更好地兼容 JDBC。不过,也可以将相应的类作为第二个参数传给 ResultSet#getObject(columnIndex, Class<T>),从而获取 java.time.LocalDatejava.time.LocalDateTimejava.time.LocalTime
    • rs.getObject(1, java.time.LocalDate.class) 会返回 Date 列对应的 java.time.LocalDate 值。
    • rs.getObject(1, java.time.LocalDateTime.class) 会返回 DateTime 列对应的 java.time.LocalDateTime 值。
    • rs.getObject(1, java.time.LocalTime.class) 会返回 Time 列对应的 java.time.LocalTime 值。
  • DateDate32TimeTime64 不受服务器时区的影响。
  • DateTimeDateTime64 会受服务器时区或会话时区影响。
  • 通过 getObject(colIndex, ZonedDateTime.class),可将 DateTimeDateTime64ZonedDateTime 的形式获取。
嵌套类型
  • 默认情况下,为了兼容 JDBC,Array 会映射为 java.sql.Array。这样做还能提供有关返回数组值的更多信息,有助于类型推断。
  • Array 实现了 getResultSet() 方法,返回一个与原始数组内容相同的 java.sql.ResultSet
  • 集合类型不应读取为 java.lang.String,因为这不是表示该数据的有效方式 (例如,数组中的字符串值没有引号) 。
  • Map 会映射为 OTHER,因为该值只能通过 getObject(columnIndex, Class<T>) 方法读取。
    • Map 不是 java.sql.Struct,因为它不包含具名列。
  • Tuple 会映射为 Object[],因为它可以包含不同类型的值,而使用 List 并不成立。
  • 可以通过 getObject(columnIndex, Array.class) 方法将 Tuple 作为 Array 读取。在这种情况下,Array#baseTypeName 将返回 Tuple 的列定义。
数组元素类型元数据Array.getBaseTypeName() 返回 ClickHouse 元素类型名称;Array.getBaseType() 返回 JDBC 类型代码。 JDBC V2 保留了完整的类型签名 (包装类型、类型参数) ,而 V1 会将其去除。数组的通用映射规则如下:以上规则的注意事项:
  • 包装类型 (Nullable, LowCardinality) 会在 getBaseTypeName() 中保留,但 getBaseType() 解析得到的是内部类型的 JDBC 代码。
  • 元数据中的嵌套数组会被展平:getBaseTypeName() 返回最内层的非数组元素类型,而不是直接子类型。
  • 参数化类型 (FixedString(N)、完整的 Enum/Tuple 定义) 会在 getBaseTypeName() 中保留其参数。
示例:
  • 在 V2 中,getBaseTypeName() 会保留完整的类型签名,包括包装类型 (NullableLowCardinality) 和类型参数 (FixedString(8)、完整的 EnumTuple 定义)。V1 则会去掉这些内容,只返回基本类型名称。
  • Tuple 数组在 V2 中使用 OTHER (1111),而不是 STRUCT (2002),因为 ClickHouse 元组具有具名字段,而 java.sql.Struct 不支持这一点。
  • UUID 数组在 V2 中使用 OTHER (1111),与标量 UUID 的映射一致。
  • Enum 值会映射为 VARCHAR —— 枚举成员始终通过字符串名称标识,与其底层数值编码无关。
写入 Array使用 java.sql.Connection#createArrayOf 实例化 java.sql.Array 对象。该对象旨在统一不同数据库间的数组处理方式。 创建 Array 时需要通过连接将配置传递给其工厂方法。该方法接受两个参数:
  • typeName - 数组元素的类型名称。例如,Array(Int32) -> "Int32"
  • elements - 数组中的实际元素。例如,[[1, 2, 3], [4, 5, 6]] -> new Integer[][] {{1, 2, 3}, {4, 5, 6}}
Tuple 可以表示为 Object[]java.sql.Struct (有关如何写入 Tuple,请参阅下文) 。示例
读取 Arrays使用 ResultSet#getArray(columnIndex) 读取 Array 对象。该对象可用于访问任意嵌套深度的数组。 Array#getResultSet() 方法可以以更统一的方式将数组元素作为 java.sql.ResultSet 进行读取,适用于数组元素的确切类型未知的场景。示例
写入 TuplesTuples 会映射到 com.clickhouse.data.Tuple 对象,应通过调用 setObject(columnIndex, tuple) 方法将其写入该对象。 也可以使用 java.sql.Struct 对象来写入 Tuples,以提高可移植性。示例
读取 Tuples方法 getObject(columnIndex) 将返回 Object[]。Tuples 可通过 getObject(columnIndex, Array.class) 方法以 java.sql.Array 的形式读取。示例
写入 MapMap 只能以 java.collections.Map 对象的形式写入,因为该类型需要键值对 (java.sql.Struct 不支持键值对) 。示例
读取 Map可以使用 getObject(columnIndex, Map.class) 方法将 Map 作为 java.collections.Map 对象读取。示例
写入嵌套数据使用 java.sql.Connection#createStruct 实例化 java.sql.Struct 对象。该对象旨在统一不同数据库中的嵌套处理方式。 向 Struct 工厂方法传递配置时,需要提供 Connection 对象。该方法接受两个参数:
  • typeName - 嵌套元素的类型名。例如,Nested(Tuple(Int32, String)) -> "Nested(Tuple(Int32, String))"
  • elements - 实际的嵌套元素。例如,[1, 'test'] -> new Object[] {1, 'test'}
示例
读取嵌套数据使用 ResultSet#getStruct(columnIndex, StructDescriptor) 读取 Nested 对象。该对象可用于访问任意嵌套深度的嵌套内容。 Struct#getResultSet() 方法可用于以更统一的方式将嵌套元素作为 java.sql.ResultSet 读取。当嵌套元素的确切类型未知时,此方法非常有用。示例
地理空间类型Nullable 与 LowCardinality 类型
  • NullableLowCardinality 是对其他类型进行封装的特殊类型。
  • Nullable 会影响 ResultSetMetaData 中类型名称的返回方式
特殊类型
  • UUID 并不是 JDBC 标准类型,但它属于 JDK。默认情况下,getObject() 方法返回的是 java.util.UUID
  • 通过 getObject(columnIndex, String.class) 方法,可将 UUID 作为 String 类型读取或写入。
  • IPv4IPv6 不是 JDBC 标准类型,但它们属于 JDK。默认情况下,getObject() 方法会返回 java.net.Inet4Addressjava.net.Inet6Address
  • 通过 getObject(columnIndex, String.class) 方法,可以将 IPv4IPv6 作为 String 类型进行读写。
JSON 类型JSON 类型默认映射为 Map<String, Object>,其中键为 JSON 对象的键,值为 JSON 对象的值。 例如:
将映射为:
还有一种更便捷的方式,可通过将服务器设置 jdbc_read_json_as_string=true 传入连接属性,以 String 形式读取 JSON。 这样驱动程序会将 JSON 值作为 String 返回,可使用任意 JSON 库进行解析。
从 ClickHouse 25.8 版本起,数值默认不再加引号。对于旧版本,您可以通过将服务器设置传递给连接属性来禁用引号:

处理日期、时间与时区

请阅读 Date/Time Guide,其中说明了驱动程序在处理 Date/Time 和时间戳时的常见陷阱及处理逻辑。

创建连接

提供凭据与配置项

showLineNumbers

简单语句

showLineNumbers

插入

showLineNumbers

HikariCP

showLineNumbers

更多信息

如需了解更多信息,请参阅我们的 GitHub 代码仓库Java Client 文档

故障排查

日志

驱动程序使用 slf4j 进行日志记录,并会使用 classpath 中第一个可用的实现。

解决大批量插入时的 JDBC 超时问题

在 ClickHouse 中执行耗时较长的大批量插入操作时,您可能会遇到如下 JDBC 超时错误:
这些错误可能会中断数据插入过程并影响系统稳定性。要解决此问题,您可能需要调整客户端操作系统中的若干超时设置。

Mac OS

在 Mac OS 上,可以通过调整以下设置来解决该问题:
  • net.inet.tcp.keepidle: 60000
  • net.inet.tcp.keepintvl: 45000
  • net.inet.tcp.keepinit: 45000
  • net.inet.tcp.keepcnt: 8
  • net.inet.tcp.always_keepalive: 1

Linux

在 Linux 上,仅靠等效设置可能无法解决该问题。由于 Linux 处理套接字保活设置的方式有所不同,还需执行额外步骤。请按以下步骤操作:
  1. /etc/sysctl.conf 或相关配置文件中,调整以下 Linux 内核参数:
  • net.inet.tcp.keepidle: 60000
  • net.inet.tcp.keepintvl: 45000
  • net.inet.tcp.keepinit: 45000
  • net.inet.tcp.keepcnt: 8
  • net.inet.tcp.always_keepalive: 1
  • net.ipv4.tcp_keepalive_intvl: 75
  • net.ipv4.tcp_keepalive_probes: 9
  • net.ipv4.tcp_keepalive_time: 60 (可考虑将该值从默认的 300 秒调低)
  1. 修改内核参数后,运行以下命令使更改生效:
完成上述设置后,您需要确保客户端在套接字上启用 Keep-Alive 选项:

迁移指南

主要变更

  • JDBC V2 采用了更轻量的实现,因此移除了一些功能。
    • JDBC V2 不支持流式数据,因为流式数据不属于 JDBC 规范,也不是 Java 的一部分。
  • JDBC V2 需要显式配置。不默认启用故障转移。
    • 应在 URL 中指定协议。不支持根据端口号隐式检测协议。

配置变更

只有两个枚举:
  • com.clickhouse.jdbc.DriverProperties - 驱动自身的配置属性。
  • com.clickhouse.client.api.ClientConfigProperties - 客户端配置属性。有关客户端配置的更改,请参见 Java Client 文档
连接属性的解析方式如下:
  • 首先会解析 URL 中的属性,这些属性会覆盖所有其他属性。
  • 驱动属性不会传递给客户端。
  • 端点 (主机、端口、协议) 会从 URL 中解析出来。
示例:

数据类型变更

数值类型
  • 最大的区别在于,出于更好的可移植性考虑,无符号类型会映射到 Java 类型。
String 类型
  • FixedString 在两个版本中都会按原样读取。例如,'John'FixedString(10) 会读取为 'John\0\0\0\0\0\0\0\0\0'
  • 使用 PreparedStatement#setBytes 时,会先将其转换为 unhex('<hex_string>'),然后按 String 类型读取。
  • String 类型以 UTF-8 编码存储。
日期/时间类型
  • TimeTime64 仅在 V2 中作为新增类型受支持。
  • DateTimeDateTime64 会映射到 java.sql.Timestamp,以更好地兼容 JDBC。
枚举类型嵌套类型
  • 在 V2 中,Array 默认映射为 java.sql.Array,以兼容 JDBC。这样做也能提供有关返回数组值的更多信息,有助于类型推断。
  • 在 V2 中,Array 实现了 getResultSet() 方法,用于返回一个内容与原始数组相同的 java.sql.ResultSet
  • V1 将 Map 视为 STRUCT,但始终返回 java.util.Map 对象。V2 通过将 Map 映射为 JAVA_OBJECT 解决了这一问题。
  • V1 对 Tuple 使用 STRUCT,但始终返回 List<Object>。V2 将 Tuple 映射为 OTHER,默认返回 Object[]
  • V2 引入了 com.clickhouse.data.Tuple#Tuple,用于写入元组。它简化了判断某个值是元组还是数组的过程。
  • PreparedStatement#setBytesResultSet#getBytes 不能用于 collection 类型。这些方法是为处理 binary string 而设计的。
  • 通常使用 java.sql.Array 来读写 Array 类型。JDBC 驱动对此提供了全面支持。
  • V2 Nested 被映射为 Array,并以元组数组的形式呈现。
  • V2 对 java.sql.Struct 提供了部分支持,因为它与 Array 类型非常相似,且不支持键值对。Struct 可用于写入 Tuple 值。
地理空间类型Nullable 与 LowCardinality 类型
  • NullableLowCardinality 是用于封装其他类型的特殊类型。
  • 这些类型在 V2 中没有变化。
特殊类型
  • V1 将 UUID 视为 VARCHAR,但始终返回 java.util.UUID 对象。V2 通过将 UUID 映射为 OTHER 解决了这一问题,并返回 java.util.UUID 对象。
  • V1 对 IPv4IPv6 使用 VARCHAR,但始终返回 java.net.Inet4Addressjava.net.Inet6Address 对象。V2 通过将 IPv4IPv6 映射到 OTHER 解决了这一问题,并返回 java.net.Inet4Addressjava.net.Inet6Address 对象。
  • DynamicVariant 是 V2 中新增的类型,V1 不支持。
  • JSON 基于 Dynamic 类型,因此仅在 V2 中受支持。
  • IPv4 和 IPv6 的值可以通过 getBytes(columnIndex) 方法读取为 byte[]。不过,建议针对这些类型使用专门的类。
  • V2 不支持将 IP 地址作为数值读取,因为这类转换更适合在 InetAddress 类中实现。

数据库元数据变更

  • V2 仅使用 Schema 这一术语来表示数据库。Catalog 一词保留供将来使用。
  • V2 对 DatabaseMetaData.supportsTransactions()DatabaseMetaData.supportsSavepoints() 返回 false。后续开发中将对此进行调整。
  • DatabaseMetaData.getTypeInfo() 中,对于不应具有前缀和后缀的数据类型 (例如数值类型) ,LITERAL_PREFIXLITERAL_SUFFIX 列现在会返回 null。 在 V1 中,这些列对这类类型会返回非 null 值。在生成 SQL 查询时,应使用这些列根据数据类型正确地为字面量值添加引号。
最后修改于 2026年7月2日