跳转到主内容
跳转到主内容

Java 客户端

用于通过其协议与数据库服务器通信的 Java 客户端库。当前实现仅支持 HTTP 接口。 该库提供自己的 API 用于向服务器发送请求,并提供用于处理不同二进制数据格式 (RowBinary* 和 Native*) 的工具。

设置


<dependency>
    <groupId>com.clickhouse</groupId>
    <artifactId>client-v2</artifactId>
    <version>0.9.7</version>
</dependency>

初始化

Client 对象由 com.clickhouse.client.api.Client.Builder#build() 初始化。每个客户端都有自己的上下文,客户端之间不共享对象。 Builder 提供了便于配置的方法。

示例:

 Client client = new Client.Builder()
                .addEndpoint("https://clickhouse-cloud-instance:8443/")
                .setUsername(user)
                .setPassword(password)
                .build();

Client 实现了 AutoCloseable 接口,不再需要时应将其关闭。

身份验证

身份验证在初始化阶段为每个客户端单独配置。支持三种身份验证方式:基于密码、基于访问令牌、基于 SSL 客户端证书。

使用密码进行身份验证时,需要通过调用 setUsername(String)setPassword(String) 方法来设置用户名和密码:

 Client client = new Client.Builder()
        .addEndpoint("https://clickhouse-cloud-instance:8443/")
        .setUsername(user)
        .setPassword(password)
        .build();

使用访问令牌进行身份验证时,需要调用 setAccessToken(String) 来设置访问令牌:

 Client client = new Client.Builder()
        .addEndpoint("https://clickhouse-cloud-instance:8443/")
        .setAccessToken(userAccessToken)
        .build();

通过 SSL 客户端证书进行身份验证时,需要设置用户名、启用 SSL 身份验证,并设置客户端证书和客户端密钥,可分别通过调用 setUsername(String)useSSLAuthentication(boolean)setClientCertificate(String)setClientKey(String) 来完成:

Client client = new Client.Builder()
        .useSSLAuthentication(true)
        .setUsername("some_user")
        .setClientCertificate("some_user.crt")
        .setClientKey("some_user.key")
注意

SSL 身份验证在生产环境中可能比较难以排查,因为许多来自 SSL 库的错误信息不够详细,无法提供足够的上下文。例如,如果客户端证书与私钥不匹配,服务器会立即终止连接 (对于 HTTP 来说,这发生在连接建立阶段,此时客户端尚未发送任何 HTTP 请求,因此也就不会收到任何响应) 。

请使用 openssl 等工具验证证书和密钥:

  • 校验密钥完整性:openssl rsa -in [key-file.key] -check -noout
  • 检查客户端证书中的 CN 是否与该 USER 匹配:
    • 从用户证书中获取 CN 值:openssl x509 -noout -subject -in [user.cert]
    • 确认在数据库中已设置相同的值:select name, auth_type, auth_params from system.users where auth_type = 'ssl_certificate' (查询会输出 auth_params 字段,其内容类似于 {"common_names":["some_user"]})

配置

所有设置均由实例方法 (亦称配置方法) 定义,从而使每个值的作用域和上下文一目了然。 主要配置参数在单一作用域 (客户端或操作) 中定义,互不覆盖。

客户端配置在创建时定义。请参阅 com.clickhouse.client.api.Client.Builder

客户端配置

MethodArgumentsDescriptionDefaultKey
addEndpoint(String endpoint)endpoint - URL 格式的服务器地址将服务器端点添加到可用服务器列表。目前仅支持一个端点。nonenone
addEndpoint(Protocol protocol, String host, int port, boolean secure)protocol - 连接协议
host - IP 或主机名
secure - 使用 HTTPS
将服务器端点添加到可用服务器列表。目前仅支持一个端点。nonenone
enableConnectionPool(boolean enable)enable - 启用/禁用标志设置是否启用连接池trueconnection_pool_enabled
setMaxConnections(int maxConnections)maxConnections - 连接数量设置客户端对每个服务器端点最多可以打开的连接数。10max_open_connections
setConnectionTTL(long timeout, ChronoUnit unit)timeout - 超时值
unit - 时间单位
设置连接的生存时间 (TTL),超过该时间后连接将被视为不再活动-1connection_ttl
setKeepAliveTimeout(long timeout, ChronoUnit unit)timeout - 超时值
unit - 时间单位
设置 HTTP 连接的 Keep-Alive 超时时间。设置为 0 以禁用 Keep-Alive。-http_keep_alive_timeout
setConnectionReuseStrategy(ConnectionReuseStrategy strategy)strategy - LIFOFIFO选择连接池应使用的连接复用策略FIFOconnection_reuse_strategy
setDefaultDatabase(String database)database - 数据库名称设置默认数据库。defaultdatabase

客户端标识

查询日志中有两个字段用于标识发起请求的应用程序:client_namehttp_user_agent。原生 TCP 协议使用 client_name 来标识应用程序。HTTP 协议使用 http_user_agent 来标识应用程序。Client builder 提供了 setClientName 方法,可为两种协议设置正确的值。 http_user_agent 字段按照 User-Agent 请求头的通用格式设置:application-name[/version] [(operating-system; architecture; ...)]。 这组值会在每一层重复出现:应用程序、客户端库、HTTP 客户端库。通过 setClientName 方法设置的内容在列表中排在最前面。

例如:

client.setClientName("my-app-01/1.0");

将得到如下 http_user_agent 值:

my-app-01/1.0 clickhouse-java-v2/0.9.6-SNAPSHOT (Linux; jvm:17.0.17) Apache-HttpClient/5.4.4

应用程序可以设置自定义 HTTP 请求头 User-Agent 来标识自身。但 clickhouse-java-v2/0.9.6-SNAPSHOT 部分将被追加到该请求头的末尾。

操作标识

查询日志还包含两个字段 query_idlog_comment,可用于标识某个操作并为查询日志添加额外信息。

query_id 是操作的唯一标识符。应用程序可以通过调用 QuerySettings 类的 setQueryId 方法来设置它。

QuerySettings querySettings = new QuerySettings();
querySettings.setQueryId("some-query-id");

log_comment 是可以添加到查询日志的注释。可以在应用程序中通过调用 QuerySettings 类的 logComment 方法来设置。

QuerySettings querySettings = new QuerySettings();
querySettings.logComment("some-comment");

服务器设置

服务器端设置可以在创建客户端时于客户端级别配置一次 (参见 BuilderserverSetting 方法) ,也可以在操作级别配置 (参见操作设置类的 serverSetting 方法) 。

 try (Client client = new Client.Builder().addEndpoint(Protocol.HTTP, "localhost", mockServer.port(), false)
        .setUsername("default")
        .setPassword(ClickHouseServerForTest.getPassword())
        .compressClientRequest(true)

        // Client level
        .serverSetting("max_threads", "10")
        .serverSetting("async_insert", "1")
        .serverSetting("roles", Arrays.asList("role1", "role2"))

        .build()) {

	// Operation level
	QuerySettings querySettings = new QuerySettings();
	querySettings.serverSetting("session_timezone", "Europe/Zurich");

	...
}

⚠️ 当通过 setOption 方法设置选项时 (无论是在 Client.Builder 还是在操作设置类中) ,服务器设置名称应以 clickhouse_setting_ 为前缀。在这种情况下,com.clickhouse.client.api.ClientConfigProperties#serverSetting() 可能会很有用。

自定义 HTTP 请求头

可以在客户端级别为所有操作设置自定义 HTTP 请求头,也可以在操作级别仅为单个操作设置。


QuerySettings settings = new QuerySettings()
    .httpHeader(HttpHeaders.REFERER, clientReferer)
    .setQueryId(qId);

当通过 setOption 方法设置选项时 (无论是 Client.Builder 还是操作设置类) ,自定义头名称应以 http_header_ 为前缀。此情况下,方法 com.clickhouse.client.api.ClientConfigProperties#httpHeader() 可能会很有用。

常用术语定义

ClickHouseFormat

支持的格式 的枚举类型,包含 ClickHouse 支持的所有格式。

  • raw - 用户需要自行对原始数据进行转码
  • full - 客户端可以自行对数据进行转码,并可接收原始数据流
  • - - ClickHouse 不支持对该格式执行此操作

此客户端版本支持:

格式输入输出
TabSeparatedrawraw
TabSeparatedRawrawraw
TabSeparatedWithNamesrawraw
TabSeparatedWithNamesAndTypesrawraw
TabSeparatedRawWithNamesrawraw
TabSeparatedRawWithNamesAndTypesrawraw
Templaterawraw
TemplateIgnoreSpacesraw*
CSVrawraw
CSVWithNamesrawraw
CSVWithNamesAndTypesrawraw
CustomSeparatedrawraw
CustomSeparatedWithNamesrawraw
CustomSeparatedWithNamesAndTypesrawraw
SQLInsert-raw
Valuesrawraw
Vertical*raw
JSONrawraw
JSONAsStringraw-
JSONAsObjectraw*
JSONStringsrawraw
JSONColumnsrawraw
JSONColumnsWithMetadatarawraw
JSONCompactrawraw
JSONCompactStrings-raw
JSONCompactColumnsrawraw
JSONEachRowrawraw
PrettyJSONEachRow*raw
JSONEachRowWithProgress-raw
JSONStringsEachRowrawraw
JSONStringsEachRowWithProgress*raw
JSONCompactEachRowrawraw
JSONCompactEachRowWithNamesrawraw
JSONCompactEachRowWithNamesAndTypesrawraw
JSONCompactStringsEachRowrawraw
JSONCompactStringsEachRowWithNamesrawraw
JSONCompactStringsEachRowWithNamesAndTypesrawraw
JSONObjectEachRowrawraw
BSONEachRowrawraw
TSKV原始原始
Pretty-原始
PrettyNoEscapes*原始
PrettyMonoBlock-原始
PrettyNoEscapesMonoBlock*原始
PrettyCompact-原始
PrettyCompactNoEscapes*原始
PrettyCompactMonoBlock-原始
PrettyCompactNoEscapesMonoBlock*原始
PrettySpace-原始
PrettySpaceNoEscapes*原始
PrettySpaceMonoBlock-原始
PrettySpaceNoEscapesMonoBlock*原始
Prometheus-raw
Protobufrawraw
ProtobufSinglerawraw
ProtobufListrawraw
Avrorawraw
AvroConfluentraw*
Parquetrawraw
ParquetMetadataraw-
Arrowrawraw
ArrowStreamrawraw
ORCrawraw
Oneraw*
Npyrawraw
RowBinaryfullfull
RowBinaryWithNamesfullfull
RowBinaryWithNamesAndTypesfullfull
RowBinaryWithDefaultsfull-
Nativefullraw
Null*raw
XML-raw
CapnProtorawraw
LineAsStringrawraw
Regexpraw*
RawBLOBrawraw
MsgPackrawraw
MySQLDumpraw-
DWARFraw*
Markdown-raw
Formraw*

插入 API

insert(String tableName, InputStream data, ClickHouseFormat format)

接受以指定格式编码的字节输入流 (InputStream) 形式的数据。假定 data 已按 format 进行编码。

签名

CompletableFuture<InsertResponse> insert(String tableName, InputStream data, ClickHouseFormat format, InsertSettings settings)
CompletableFuture<InsertResponse> insert(String tableName, InputStream data, ClickHouseFormat format)

参数

tableName - 目标表的名称。

data - 已编码数据的输入流。

format - 数据编码所使用的格式。

settings - 请求设置。

返回值

InsertResponse 类型的 Future,包含操作结果以及服务器端指标等附加信息。

示例

try (InputStream dataStream = getDataStream()) {
    try (InsertResponse response = client.insert(TABLE_NAME, dataStream, ClickHouseFormat.JSONEachRow,
            insertSettings).get(3, TimeUnit.SECONDS)) {

        log.info("Insert finished: {} rows written", response.getMetrics().getMetric(ServerMetrics.NUM_ROWS_WRITTEN).getLong());
    } catch (Exception e) {
        log.error("Failed to write JSONEachRow data", e);
        throw new RuntimeException(e);
    }
}

insert(String tableName, List<?> data, InsertSettings settings)

向数据库发送写入请求。对象列表会被转换为高效的内部格式,然后发送到服务器。列表项所属的类应事先使用 register(Class, TableSchema) 方法进行注册。

方法签名

client.insert(String tableName, List<?> data, InsertSettings settings)
client.insert(String tableName, List<?> data)

参数

tableName - 目标表名称。

data - DTO (数据传输对象) 对象集合。

settings - 请求设置。

返回值

返回 InsertResponse 类型的 Future,包含操作结果以及服务器端指标等附加信息。

示例

// Important step (done once) - register class to pre-compile object serializer according to the table schema.
client.register(ArticleViewEvent.class, client.getTableSchema(TABLE_NAME));

List<ArticleViewEvent> events = loadBatch();

try (InsertResponse response = client.insert(TABLE_NAME, events).get()) {
    // handle response, then it will be closed and connection that served request will be released.
}

InsertSettings

插入操作的配置选项。

配置方法

方法说明
setQueryId(String queryId)设置将要分配给该操作的查询 ID。默认值:null
setDeduplicationToken(String token)设置去重令牌。该令牌将发送到服务器,并可用于标识查询。默认值:null
setInputStreamCopyBufferSize(int size)复制缓冲区的大小。在写入操作期间,该缓冲区用于将用户提供的输入流中的数据复制到输出流。默认值:8196
serverSetting(String name, String value)为某次操作设置单个服务器端配置项。
serverSetting(String name, Collection values)为某个操作设置可包含多个值的单个服务器配置项。集合中的元素应为 String 值。
setDBRoles(Collection dbRoles)设置在执行操作前要生效的数据库角色。集合中的元素应为 String 值。
setOption(String option, Object value)以原始格式设置配置选项。这不是服务器端设置。

InsertResponse

用于保存插入操作结果的响应对象。仅当客户端从服务器收到响应时才可用。

注意

应尽快关闭该对象以释放连接,因为在完全读取完之前,该连接无法复用。

方法说明
OperationMetrics getMetrics()返回一个包含该操作各项指标的对象。
String getQueryId()返回为该操作分配的查询 ID,该 ID 由应用程序 (通过操作设置) 指定,或由服务器生成。

查询 API

query(String sqlQuery)

按原样发送 sqlQuery。响应格式由查询设置指定。QueryResponse 将持有对响应流的引用,该响应流应由能够处理该格式的读取器来消费。

方法签名

CompletableFuture<QueryResponse> query(String sqlQuery, QuerySettings settings)
CompletableFuture<QueryResponse> query(String sqlQuery)

参数

sqlQuery - 单条 SQL 语句。该查询按原样发送到服务器。

settings - 请求相关设置。

返回值

类型为 QueryResponse 的 Future —— 结果数据集以及服务器端指标等附加信息。使用完数据集后应关闭 Response 对象。

示例

final String sql = "select * from " + TABLE_NAME + " where title <> '' limit 10";

// Default format is RowBinaryWithNamesAndTypesFormatReader so reader have all information about columns
try (QueryResponse response = client.query(sql).get(3, TimeUnit.SECONDS);) {

    // Create a reader to access the data in a convenient way
    ClickHouseBinaryFormatReader reader = client.newBinaryFormatReader(response);

    while (reader.hasNext()) {
        reader.next(); // Read the next record from stream and parse it

        // get values
        double id = reader.getDouble("id");
        String title = reader.getString("title");
        String url = reader.getString("url");

        // collecting data
    }
} catch (Exception e) {
    log.error("Failed to read data", e);
}

// put business logic outside of the reading block to release http connection asap.

query(String sqlQuery, Map<String, Object> queryParams, QuerySettings settings)

按原样发送 sqlQuery,并同时发送查询参数,以便服务器能够编译该 SQL 表达式。

签名

CompletableFuture<QueryResponse> query(String sqlQuery, Map<String, Object> queryParams, QuerySettings settings)

参数

sqlQuery - 带有占位符 {} 的 SQL 表达式。

queryParams - 变量到值的映射,用于在服务器端补全 SQL 表达式。

settings - 请求设置。

返回值

QueryResponse 类型的 Future —— 包含结果数据集以及如服务器端指标等附加信息。使用完数据集后,应关闭 Response 对象。

示例


// define parameters. They will be sent to the server along with the request.
Map<String, Object> queryParams = new HashMap<>();
queryParams.put("param1", 2);

try (QueryResponse response =
        client.query("SELECT * FROM " + table + " WHERE col1 >= {param1:UInt32}", queryParams, new QuerySettings()).get()) {

    // Create a reader to access the data in a convenient way
    ClickHouseBinaryFormatReader reader = client.newBinaryFormatReader(response);

    while (reader.hasNext()) {
        reader.next(); // Read the next record from stream and parse it

        // reading data
    }

} catch (Exception e) {
    log.error("Failed to read data", e);
}

queryAll(String sqlQuery)

RowBinaryWithNamesAndTypes 格式查询数据。结果以集合形式返回。读取性能与使用 reader 相同,但需要更多内存来保存整个数据集。

签名

List<GenericRecord> queryAll(String sqlQuery)

参数

sqlQuery - 用于从服务器查询数据的 SQL 表达式。

返回值

完整数据集由 GenericRecord 对象列表表示,这些对象以按行的方式提供对结果数据的访问。

示例

try {
    log.info("Reading whole table and process record by record");
    final String sql = "select * from " + TABLE_NAME + " where title <> ''";

    // Read whole result set and process it record by record
    client.queryAll(sql).forEach(row -> {
        double id = row.getDouble("id");
        String title = row.getString("title");
        String url = row.getString("url");

        log.info("id: {}, title: {}, url: {}", id, title, url);
    });
} catch (Exception e) {
    log.error("Failed to read data", e);
}

QuerySettings

查询操作的配置选项。

配置方法

方法说明
setQueryId(String queryId)设置将分配给该操作的查询 ID。
setFormat(ClickHouseFormat format)设置响应格式。完整列表请参见 RowBinaryWithNamesAndTypes
setMaxExecutionTime(Integer maxExecutionTime)设置服务器端该操作的最大执行时间,不影响读取超时。
waitEndOfQuery(Boolean waitEndOfQuery)请求服务器在查询完成后再发送响应。
setUseServerTimeZone(Boolean useServerTimeZone)服务端时区 (参见客户端配置) 将用于解析操作结果中的日期/时间类型值。默认值为 false
setUseTimeZone(String timeZone)请求服务器在时间转换时使用 timeZone。详见 session_timezone
serverSetting(String name, String value)为某个操作设置独立的服务器端配置。
serverSetting(String name, Collection values)为某个操作设置可包含多个值的单个服务器端设置。集合中的项应为 String 类型的值。
setDBRoles(Collection dbRoles)设置在执行操作前要生效的数据库角色。集合中的元素应为 String 值。
setOption(String option, Object value)以原始格式设置配置选项。这不是服务器端设置。

QueryResponse

用于保存查询执行结果的响应对象。仅在客户端已从服务器收到响应时才可用。

注意

应尽快关闭此对象以释放连接,因为在完全读取完上一个响应的全部数据之前,该连接无法被复用。

方法说明
ClickHouseFormat getFormat()返回响应数据的编码格式。
InputStream getInputStream()返回按指定格式编码的未压缩数据字节流。
OperationMetrics getMetrics()返回包含该操作指标的对象。
String getQueryId()返回为该操作分配的查询 ID,该 ID 可由应用程序 (通过操作设置) 或服务器指定。
TimeZone getTimeZone()返回在处理响应中的 Date/DateTime 类型时应使用的时区。

示例

通用 API

getTableSchema(String table)

获取 table 的表结构。

方法签名

TableSchema getTableSchema(String table)
TableSchema getTableSchema(String table, String database)

参数

table - 要获取其 schema 数据的表名。

database - 定义目标表的数据库。

返回值

返回一个 TableSchema 对象,其中包含该表的列列表。

getTableSchemaFromQuery(String sql)

从 SQL 语句中获取表结构。

签名

TableSchema getTableSchemaFromQuery(String sql)

参数

sql - “SELECT” SQL 语句,其 schema 将被返回。

返回值

返回一个列与 sql 表达式匹配的 TableSchema 对象。

TableSchema

register(Class<?> clazz, TableSchema schema)

为 Java 类编译一个序列化/反序列化层,以便在基于 schema 进行数据读写时使用。该方法将为 getter/setter 方法对及其对应的列创建序列化器和反序列化器。 列匹配是通过从方法名中提取列名来完成的。例如,getFirstName 将对应列 first_namefirstname

签名

void register(Class<?> clazz, TableSchema schema)

参数

clazz - 表示用于读取/写入数据的 POJO 的 Class。

schema - 用于与 POJO 属性进行匹配的数据 schema。

示例

client.register(ArticleViewEvent.class, client.getTableSchema(TABLE_NAME));

使用示例

完整示例代码存放在代码仓库的 'example` 目录 中:

  • client-v2 - 主要示例代码集。
  • demo-service - 在 Spring Boot 应用中使用该客户端的示例项目。
  • demo-kotlin-service - 示例代码,演示如何在 Ktor (Kotlin) 应用中使用该客户端。

读取数据

读取数据通常有两种方式:

  • query() 方法返回底层的 QueryResponse 对象,该对象包含用于承载数据的 InputStream。通常与 ClickHouseBinaryFormatReader 搭配使用以进行流式读取,但 也可以与任意其他自定义读取器实现配合使用。QueryResponse 还提供对结果集元数据和指标的访问能力。
  • queryAll() 方法与 GenericRecord 搭配使用,可方便按行访问数据。在这种情况下,会将整个结果集加载到内存中。
  • queryRecords() 方法返回 com.clickhouse.client.api.query.Records —— 一个用于迭代 GenericRecord 对象的迭代器。该方法采用流式处理方式 (不会将数据加载到内存中) ,并通过 GenericRecord 来访问数据。

注意: 流式处理方式要求快速读取,否则由于数据直接从网络流读取,可能导致服务器写入超时。

读取数组

ClickHouseBinaryFormatReader 方法

  • getList(...) - 将任意 Array(...) 读取为 List<T>。适合作为类型灵活读取时的默认选择。支持嵌套数组。
  • getByteArray(...), getShortArray(...), getIntArray(...), getLongArray(...), getFloatArray(...), getDoubleArray(...), getBooleanArray(...) - 最适用于由与 Java 原始类型兼容的值构成的一维数组。
  • getStringArray(...) - 用于读取 Array(String) (以及以名称表示的枚举值) 。
  • getObjectArray(...) - 针对任意 Array(...) 元素类型 (包括嵌套数组) 的通用方法。用于读取包含 Nullable 值或嵌套数组的数组。

所有方法都提供基于索引和基于名称的重载。索引从 1 开始计数。基于索引的重载会直接访问对应的列。 基于名称的方法每次调用时都需要执行一次索引查找。

try (QueryResponse response = client.query("SELECT * FROM my_table").get()) {
    ClickHouseBinaryFormatReader reader = client.newBinaryFormatReader(response);
    while (reader.next() != null) {
        
        Object[] uint64 = reader.getObjectArray("uint64_arr"); // Array(UInt64) -> BigInteger[]
        Object[] arr2d = reader.getObjectArray("arr2d");       // Array(Array(Int64)) -> Object[]

        // nested arrays are returned as nested Object[]:
        Object[] firstInner = (Object[]) arr2d[0];
        Long firstValue = (Long) firstInner[0];
    }
}

GenericRecord 方法

  • getList(...) - 将任意 Array(...) 读取为 List<T>。适合作为灵活类型读取的默认选项。支持嵌套数组。
  • getByteArray(...), getShortArray(...), getIntArray(...), getLongArray(...), getFloatArray(...), getDoubleArray(...), getBooleanArray(...) - 最适用于由与 Java 原始类型兼容的值构成的一维数组。
  • getStringArray(...) - 用于 Array(String) 类型 (以及以名称表示的枚举值) 。
  • getObjectArray(...) - 适用于任意 Array(...) 元素类型 (包括嵌套数组) 的通用方法。用于读取包含 Nullable 值和嵌套数组的数组。

所有方法都提供基于索引和基于名称的重载。索引从 1 开始。基于索引的重载会直接访问列。 基于名称的方法每次都需要进行索引查找。

try (QueryResponse response = client.query("SELECT * FROM my_table").get()) {
    List<GenericRecord> rows = client.queryAll(
        "SELECT int_arr, arr2d_nullable FROM test_arrays ORDER BY id");

    for (GenericRecord row : rows) {
        Object[] intArr = row.getObjectArray("int_arr");                 // Array(Int32) -> Integer[]
        Object[] arr2d = row.getObjectArray("arr2d_nullable");           // Array(Array(Nullable(Int32)))

        Object[] inner = (Object[]) arr2d[0];
        Object maybeNull = inner[1]; // may be null
    }
}


## Migration Guide                   


Old client (V1) was using `com.clickhouse.client.ClickHouseClient#builder` as start point. The new client (V2) uses similar pattern with `com.clickhouse.client.api.Client.Builder`. Main
differences are:
- no service loader is used to grab implementation. The `com.clickhouse.client.api.Client` is facade class for all kinds of implementation in the future.
- a fewer sources of configuration: one is provided to the builder and one is with operation settings (`QuerySettings`, `InsertSettings`). Previous version had configuration per node and was loading
env. variables in some cases.

### Configuration Parameters Match                            

There are 3 enum classes related to configuration in V1:
- `com.clickhouse.client.config.ClickHouseDefaults` - configuration parameters that supposed to be set in most use cases. Like `USER` and `PASSWORD`.
- `com.clickhouse.client.config.ClickHouseClientOption` - configuration parameters specific for the client. Like `HEALTH_CHECK_INTERVAL`.
- `com.clickhouse.client.http.config.ClickHouseHttpOption` - configuration parameters specific for HTTP interface. Like `RECEIVE_QUERY_PROGRESS`.

They were designed to group parameters and provide clear separation. However in some cases it lead to a confusion (is there a difference between `com.clickhouse.client.config.ClickHouseDefaults#ASYNC` and
`com.clickhouse.client.config.ClickHouseClientOption#ASYNC`). The new V2 client uses `com.clickhouse.client.api.Client.Builder` as single dictionary of all possible client configuration options.There is
`com.clickhouse.client.api.ClientConfigProperties` where all configuration parameter names are listed.

Table below shows what old options are supported in the new client and their new meaning.

**Legend:** ✔ = supported, ✗ = dropped

<Tabs groupId="v1-migration">
<TabItem value="connection-auth" label="Connection & Auth">

| V1 Configuration | V2 Builder Method | Comments |
|------------------|-------------------|----------|
| `ClickHouseDefaults#HOST` | `Client.Builder#addEndpoint` | |
| `ClickHouseDefaults#PROTOCOL` | ✗ | Only HTTP supported in V2 |
| `ClickHouseDefaults#DATABASE`<br/>`ClickHouseClientOption#DATABASE` | `Client.Builder#setDefaultDatabase` | |
| `ClickHouseDefaults#USER` | `Client.Builder#setUsername` | |
| `ClickHouseDefaults#PASSWORD` | `Client.Builder#setPassword` | |
| `ClickHouseClientOption#CONNECTION_TIMEOUT` | `Client.Builder#setConnectTimeout` | |
| `ClickHouseClientOption#CONNECTION_TTL` | `Client.Builder#setConnectionTTL` | |
| `ClickHouseHttpOption#MAX_OPEN_CONNECTIONS` | `Client.Builder#setMaxConnections` | |
| `ClickHouseHttpOption#KEEP_ALIVE`<br/>`ClickHouseHttpOption#KEEP_ALIVE_TIMEOUT` | `Client.Builder#setKeepAliveTimeout` | |
| `ClickHouseHttpOption#CONNECTION_REUSE_STRATEGY` | `Client.Builder#setConnectionReuseStrategy` | |
| `ClickHouseHttpOption#USE_BASIC_AUTHENTICATION` | `Client.Builder#useHTTPBasicAuth` | |

</TabItem>

<TabItem value="ssl" label="SSL & Security">

| V1 Configuration | V2 Builder Method | Comments |
|------------------|-------------------|----------|
| `ClickHouseDefaults#SSL_CERTIFICATE_TYPE` | ✗ | |
| `ClickHouseDefaults#SSL_KEY_ALGORITHM` | ✗ | |
| `ClickHouseDefaults#SSL_PROTOCOL` | ✗ | |
| `ClickHouseClientOption#SSL` | ✗ | See `Client.Builder#addEndpoint` |
| `ClickHouseClientOption#SSL_MODE` | ✗ | |
| `ClickHouseClientOption#SSL_ROOT_CERTIFICATE` | `Client.Builder#setRootCertificate` | SSL Auth should be enabled by `useSSLAuthentication` |
| `ClickHouseClientOption#SSL_CERTIFICATE` | `Client.Builder#setClientCertificate` | |
| `ClickHouseClientOption#SSL_KEY` | `Client.Builder#setClientKey` | |
| `ClickHouseClientOption#KEY_STORE_TYPE` | `Client.Builder#setSSLTrustStoreType` | |
| `ClickHouseClientOption#TRUST_STORE` | `Client.Builder#setSSLTrustStore` | |
| `ClickHouseClientOption#KEY_STORE_PASSWORD` | `Client.Builder#setSSLTrustStorePassword` | |
| `ClickHouseClientOption#SSL_SOCKET_SNI` | `Client.Builder#sslSocketSNI` | |
| `ClickHouseClientOption#CUSTOM_SOCKET_FACTORY` | ✗ | |
| `ClickHouseClientOption#CUSTOM_SOCKET_FACTORY_OPTIONS` | ✗ | See `Client.Builder#sslSocketSNI` to set SNI |

</TabItem>

<TabItem value="socket" label="Socket Options">

| V1 Configuration | V2 Builder Method | Comments |
|------------------|-------------------|----------|
| `ClickHouseClientOption#SOCKET_TIMEOUT` | `Client.Builder#setSocketTimeout` | |
| `ClickHouseClientOption#SOCKET_REUSEADDR` | `Client.Builder#setSocketReuseAddress` | |
| `ClickHouseClientOption#SOCKET_KEEPALIVE` | `Client.Builder#setSocketKeepAlive` | |
| `ClickHouseClientOption#SOCKET_LINGER` | `Client.Builder#setSocketLinger` | |
| `ClickHouseClientOption#SOCKET_IP_TOS` | ✗ | |
| `ClickHouseClientOption#SOCKET_TCP_NODELAY` | `Client.Builder#setSocketTcpNodelay` | |
| `ClickHouseClientOption#SOCKET_RCVBUF` | `Client.Builder#setSocketRcvbuf` | |
| `ClickHouseClientOption#SOCKET_SNDBUF` | `Client.Builder#setSocketSndbuf` | |

</TabItem>

<TabItem value="compression" label="Compression">

| V1 Configuration | V2 Builder Method | Comments |
|------------------|-------------------|----------|
| `ClickHouseClientOption#COMPRESS` | `Client.Builder#compressServerResponse` | See also `useHttpCompression` |
| `ClickHouseClientOption#DECOMPRESS` | `Client.Builder#compressClientRequest` | See also `useHttpCompression` |
| `ClickHouseClientOption#COMPRESS_ALGORITHM` | ✗ | `LZ4` for non-http. Http uses `Accept-Encoding` |
| `ClickHouseClientOption#DECOMPRESS_ALGORITHM` | ✗ | `LZ4` for non-http. Http uses `Content-Encoding` |
| `ClickHouseClientOption#COMPRESS_LEVEL` | ✗ | |
| `ClickHouseClientOption#DECOMPRESS_LEVEL` | ✗ | |

</TabItem>

<TabItem value="proxy" label="Proxy">

| V1 Configuration | V2 Builder Method | Comments |
|------------------|-------------------|----------|
| `ClickHouseClientOption#PROXY_TYPE` | `Client.Builder#addProxy` | |
| `ClickHouseClientOption#PROXY_HOST` | `Client.Builder#addProxy` | |
| `ClickHouseClientOption#PROXY_PORT` | `Client.Builder#addProxy` | |
| `ClickHouseClientOption#PROXY_USERNAME` | `Client.Builder#setProxyCredentials` | |
| `ClickHouseClientOption#PROXY_PASSWORD` | `Client.Builder#setProxyCredentials` | |

</TabItem>

<TabItem value="timeouts-retry" label="Timeouts & Retry">

| V1 Configuration | V2 Builder Method | Comments |
|------------------|-------------------|----------|
| `ClickHouseClientOption#MAX_EXECUTION_TIME` | `Client.Builder#setExecutionTimeout` | |
| `ClickHouseClientOption#RETRY` | `Client.Builder#setMaxRetries` | See also `retryOnFailures` |
| `ClickHouseHttpOption#AHC_RETRY_ON_FAILURE` | `Client.Builder#retryOnFailures` | |
| `ClickHouseClientOption#FAILOVER` | ✗ | |
| `ClickHouseClientOption#REPEAT_ON_SESSION_LOCK` | ✗ | |
| `ClickHouseClientOption#SESSION_ID` | ✗ | |
| `ClickHouseClientOption#SESSION_CHECK` | ✗ | |
| `ClickHouseClientOption#SESSION_TIMEOUT` | ✗ | |

</TabItem>

<TabItem value="timezone" label="Timezone">

| V1 Configuration | V2 Builder Method | Comments |
|------------------|-------------------|----------|
| `ClickHouseDefaults#SERVER_TIME_ZONE`<br/>`ClickHouseClientOption#SERVER_TIME_ZONE` | `Client.Builder#setServerTimeZone` | |
| `ClickHouseClientOption#USE_SERVER_TIME_ZONE` | `Client.Builder#useServerTimeZone` | |
| `ClickHouseClientOption#USE_SERVER_TIME_ZONE_FOR_DATES` | | |
| `ClickHouseClientOption#USE_TIME_ZONE` | `Client.Builder#useTimeZone` | |

</TabItem>

<TabItem value="buffers" label="Buffers & Performance">

| V1 Configuration | V2 Builder Method | Comments |
|------------------|-------------------|----------|
| `ClickHouseClientOption#BUFFER_SIZE` | `Client.Builder#setClientNetworkBufferSize` | |
| `ClickHouseClientOption#BUFFER_QUEUE_VARIATION` | ✗ | |
| `ClickHouseClientOption#READ_BUFFER_SIZE` | ✗ | |
| `ClickHouseClientOption#WRITE_BUFFER_SIZE` | ✗ | |
| `ClickHouseClientOption#REQUEST_CHUNK_SIZE` | ✗ | |
| `ClickHouseClientOption#REQUEST_BUFFERING` | ✗ | |
| `ClickHouseClientOption#RESPONSE_BUFFERING` | ✗ | |
| `ClickHouseClientOption#MAX_BUFFER_SIZE` | ✗ | |
| `ClickHouseClientOption#MAX_QUEUED_BUFFERS` | ✗ | |
| `ClickHouseClientOption#MAX_QUEUED_REQUESTS` | ✗ | |
| `ClickHouseClientOption#REUSE_VALUE_WRAPPER` | ✗ | |

</TabItem>

<TabItem value="threading" label="Threading & Async">

| V1 Configuration | V2 Builder Method | Comments |
|------------------|-------------------|----------|
| `ClickHouseDefaults#ASYNC`<br/>`ClickHouseClientOption#ASYNC` | `Client.Builder#useAsyncRequests` | |
| `ClickHouseDefaults#MAX_SCHEDULER_THREADS` | ✗ | see `setSharedOperationExecutor` |
| `ClickHouseDefaults#MAX_THREADS` | ✗ | see `setSharedOperationExecutor` |
| `ClickHouseDefaults#THREAD_KEEPALIVE_TIMEOUT` | see `setSharedOperationExecutor` | |
| `ClickHouseClientOption#MAX_THREADS_PER_CLIENT` | ✗ | |
| `ClickHouseClientOption#MAX_CORE_THREAD_TTL` | ✗ | |

</TabItem>

<TabItem value="http-headers" label="HTTP & Headers">

| V1 Configuration | V2 Builder Method | Comments |
|------------------|-------------------|----------|
| `ClickHouseHttpOption#CUSTOM_HEADERS` | `Client.Builder#httpHeaders` | |
| `ClickHouseHttpOption#CUSTOM_PARAMS` | ✗ | See `Client.Builder#serverSetting` |
| `ClickHouseClientOption#CLIENT_NAME` | `Client.Builder#setClientName` | |
| `ClickHouseHttpOption#CONNECTION_PROVIDER` | ✗ | |
| `ClickHouseHttpOption#DEFAULT_RESPONSE` | ✗ | |
| `ClickHouseHttpOption#SEND_HTTP_CLIENT_ID` | ✗ | |
| `ClickHouseHttpOption#AHC_VALIDATE_AFTER_INACTIVITY` | ✗ | Always enabled when Apache Http Client is used |

</TabItem>

<TabItem value="format-query" label="Format & Query">

| V1 Configuration | V2 Builder Method | Comments |
|------------------|-------------------|----------|
| `ClickHouseDefaults#FORMAT`<br/>`ClickHouseClientOption#FORMAT` | ✗ | Moved to operation settings (`QuerySettings` and `InsertSettings`) |
| `ClickHouseClientOption#QUERY_ID` | ✗ | See `QuerySettings` and `InsertSettings` |
| `ClickHouseClientOption#LOG_LEADING_COMMENT` | ✗ | See `QuerySettings#logComment` and `InsertSettings#logComment` |
| `ClickHouseClientOption#MAX_RESULT_ROWS` | ✗ | Is server side setting |
| `ClickHouseClientOption#RESULT_OVERFLOW_MODE` | ✗ | Is server side setting |
| `ClickHouseHttpOption#RECEIVE_QUERY_PROGRESS` | ✗ | Server side setting |
| `ClickHouseHttpOption#WAIT_END_OF_QUERY` | ✗ | Server side setting |
| `ClickHouseHttpOption#REMEMBER_LAST_SET_ROLES` | `Client#setDBRoles` | Runtime config now. See also `QuerySettings#setDBRoles` and `InsertSettings#setDBRoles` |

</TabItem>

<TabItem value="node-discovery" label="Node Discovery & Load Balancing">

| V1 Configuration | V2 Builder Method | Comments |
|------------------|-------------------|----------|
| `ClickHouseClientOption#AUTO_DISCOVERY` | ✗ | |
| `ClickHouseClientOption#LOAD_BALANCING_POLICY` | ✗ | |
| `ClickHouseClientOption#LOAD_BALANCING_TAGS` | ✗ | |
| `ClickHouseClientOption#HEALTH_CHECK_INTERVAL` | ✗ | |
| `ClickHouseClientOption#HEALTH_CHECK_METHOD` | ✗ | |
| `ClickHouseClientOption#NODE_DISCOVERY_INTERVAL` | ✗ | |
| `ClickHouseClientOption#NODE_DISCOVERY_LIMIT` | ✗ | |
| `ClickHouseClientOption#NODE_CHECK_INTERVAL` | ✗ | |
| `ClickHouseClientOption#NODE_GROUP_SIZE` | ✗ | |
| `ClickHouseClientOption#CHECK_ALL_NODES` | ✗ | |

</TabItem>

<TabItem value="misc" label="Miscellaneous">

| V1 Configuration | V2 Builder Method | Comments |
|------------------|-------------------|----------|
| `ClickHouseDefaults#AUTO_SESSION` | ✗ | Session support will be reviewed |
| `ClickHouseDefaults#BUFFERING` | ✗ | |
| `ClickHouseDefaults#MAX_REQUESTS` | ✗ | |
| `ClickHouseDefaults#ROUNDING_MODE` | | |
| `ClickHouseDefaults#SERVER_VERSION`<br/>`ClickHouseClientOption#SERVER_VERSION` | `Client.Builder#setServerVersion` | |
| `ClickHouseDefaults#SRV_RESOLVE` | ✗ | |
| `ClickHouseClientOption#CUSTOM_SETTINGS` | | |
| `ClickHouseClientOption#PRODUCT_NAME` | ✗ | Use client name |
| `ClickHouseClientOption#RENAME_RESPONSE_COLUMN` | ✗ | |
| `ClickHouseClientOption#SERVER_REVISION` | ✗ | |
| `ClickHouseClientOption#TRANSACTION_TIMEOUT` | ✗ | |
| `ClickHouseClientOption#WIDEN_UNSIGNED_TYPES` | ✗ | |
| `ClickHouseClientOption#USE_BINARY_STRING` | ✗ | |
| `ClickHouseClientOption#USE_BLOCKING_QUEUE` | ✗ | |
| `ClickHouseClientOption#USE_COMPILATION` | ✗ | |
| `ClickHouseClientOption#USE_OBJECTS_IN_ARRAYS` | ✗ | |
| `ClickHouseClientOption#MAX_MAPPER_CACHE` | ✗ | |
| `ClickHouseClientOption#MEASURE_REQUEST_TIME` | ✗ | |

</TabItem>
</Tabs>


### General Differences

- Client V2 uses less proprietary classes to increase portability. For example, V2 works with any implementation of `java.io.InputStream` for
writing data to a server.
- Client V2 `async` settings is `off` by default. It means no extra threads and more application control over client. This setting should be `off` for majority of use cases. Enabling `async` will create a separate thread for a request. It only make sense when using application controlled
executor (see `com.clickhouse.client.api.Client.Builder#setSharedOperationExecutor`)

### Writing Data

- use any implementation of `java.io.InputStream`. V1 `com.clickhouse.data.ClickHouseInputStream` is supported but NOT recommended.
- once end of input stream is detected it handled accordingly. Previously output stream of a request should be closed.

__V1 Insert TSV formatted data.__
```java
InputStream inData = getInData();
ClickHouseRequest.Mutation request = client.read(server)
        .write()
        .table(tableName)
        .format(ClickHouseFormat.TSV);
ClickHouseConfig config = request.getConfig();
CompletableFuture<ClickHouseResponse> future;
try (ClickHousePipedOutputStream requestBody = ClickHouseDataStreamFactory.getInstance()
        .createPipedOutputStream(config)) {
    // start the worker thread which transfer data from the input into ClickHouse
    future = request.data(requestBody.getInputStream()).execute();

    // Copy data from inData stream to requestBody stream

    // We need to close the stream before getting a response
    requestBody.close();

    try (ClickHouseResponse response = future.get()) {
        ClickHouseResponseSummary summary = response.getSummary();
        Assert.assertEquals(summary.getWrittenRows(), numRows, "Num of written rows");
    }
}

V2 以 TSV 格式插入数据。

InputStream inData = getInData();
InsertSettings settings = new InsertSettings().setInputStreamCopyBufferSize(8198 * 2); // set copy buffer size
try (InsertResponse response = client.insert(tableName, inData, ClickHouseFormat.TSV, settings).get(30, TimeUnit.SECONDS)) {

  // Insert is complete at this point

} catch (Exception e) {
 // Handle exception
}
  • 只需调用一个方法,无需额外创建请求对象。
  • 当所有数据复制完成后,请求体流会自动关闭。
  • new low-level API is available com.clickhouse.client.api.Client#insert(java.lang.String, java.util.List<java.lang.String>, com.clickhouse.client.api.DataStreamWriter, com.clickhouse.data.ClickHouseFormat, com.clickhouse.client.api.insert.InsertSettings)com.clickhouse.client.api.DataStreamWriter 用于实现自定义数据写入逻辑,例如从队列读取数据。

读取数据

  • 默认情况下,数据以 RowBinaryWithNamesAndTypes 格式读取。当需要进行数据绑定时,目前仅支持此格式。
  • 可以使用 List<GenericRecord> com.clickhouse.client.api.Client#queryAll(java.lang.String) 方法将数据读取为一组记录。该方法会将数据读入内存并自动释放连接,无需额外处理。GenericRecord 提供访问结果数据的接口,并实现了一些类型转换。
Collection<GenericRecord> records = client.queryAll("SELECT * FROM table");
for (GenericRecord record : records) {
    int rowId = record.getInteger("rowID");
    String name = record.getString("name");
    LocalDateTime ts = record.getLocalDateTime("ts");
}

用于通过其协议与数据库服务器通信的 Java 客户端库。当前实现仅支持 HTTP 接口。该库提供自己的 API,用于向服务器发送请求。

弃用

该库即将被弃用。对于新项目,请使用最新的 Java 客户端

设置

<!-- https://mvnrepository.com/artifact/com.clickhouse/clickhouse-http-client -->
<dependency>
    <groupId>com.clickhouse</groupId>
    <artifactId>clickhouse-http-client</artifactId>
    <version>0.7.2</version>
</dependency>

0.5.0 版本起,该驱动程序使用新的 HTTP 客户端库,需要将其作为依赖添加。

<!-- https://mvnrepository.com/artifact/org.apache.httpcomponents.client5/httpclient5 -->
<dependency>
    <groupId>org.apache.httpcomponents.client5</groupId>
    <artifactId>httpclient5</artifactId>
    <version>5.3.1</version>
</dependency>

初始化

连接 URL 的格式如下:protocol://host[:port][/database][?param[=value][&param[=value]][#tag[,tag]],例如:

连接到单个节点:

ClickHouseNode server = ClickHouseNode.of("http://localhost:8123/default?compress=0");

连接到多节点集群:

ClickHouseNodes servers = ClickHouseNodes.of(
    "jdbc:ch:http://server1.domain,server2.domain,server3.domain/my_db"
    + "?load_balancing_policy=random&health_check_interval=5000&failover=2");

查询 API

try (ClickHouseClient client = ClickHouseClient.newInstance(ClickHouseProtocol.HTTP);
     ClickHouseResponse response = client.read(servers)
        .format(ClickHouseFormat.RowBinaryWithNamesAndTypes)
        .query("select * from numbers limit :limit")
        .params(1000)
        .executeAndWait()) {
            ClickHouseResponseSummary summary = response.getSummary();
            long totalRows = summary.getTotalRowsToRead();
}

流式查询 API

try (ClickHouseClient client = ClickHouseClient.newInstance(ClickHouseProtocol.HTTP);
     ClickHouseResponse response = client.read(servers)
        .format(ClickHouseFormat.RowBinaryWithNamesAndTypes)
        .query("select * from numbers limit :limit")
        .params(1000)
        .executeAndWait()) {
            for (ClickHouseRecord r : response.records()) {
            int num = r.getValue(0).asInteger();
            // type conversion
            String str = r.getValue(0).asString();
            LocalDate date = r.getValue(0).asDate();
        }
}

请参阅代码仓库中的完整代码示例

插入 API


try (ClickHouseClient client = ClickHouseClient.newInstance(ClickHouseProtocol.HTTP);
     ClickHouseResponse response = client.read(servers).write()
        .format(ClickHouseFormat.RowBinaryWithNamesAndTypes)
        .query("insert into my_table select c2, c3 from input('c1 UInt8, c2 String, c3 Int32')")
        .data(myInputStream) // `myInputStream` is source of data in RowBinary format
        .executeAndWait()) {
            ClickHouseResponseSummary summary = response.getSummary();
            summary.getWrittenRows();
}

请参阅代码仓库中的完整代码示例

RowBinary 编码

RowBinary 格式在其页面中有说明。

可参考以下代码示例

功能

压缩

默认情况下,客户端使用 LZ4 压缩,需要添加以下依赖:

<!-- https://mvnrepository.com/artifact/org.lz4/lz4-java -->
<dependency>
    <groupId>org.lz4</groupId>
    <artifactId>lz4-java</artifactId>
    <version>1.8.0</version>
</dependency>

您也可以在连接 URL 中将 compress_algorithm 参数设置为 gzip,以改用 gzip 压缩。

此外,您还可以通过以下方式禁用压缩。

  1. 在连接 URL 中将 compress 设置为 0 来禁用压缩:http://localhost:8123/default?compress=0
  2. 在客户端配置中禁用:
ClickHouseClient client = ClickHouseClient.builder()
   .config(new ClickHouseConfig(Map.of(ClickHouseClientOption.COMPRESS, false)))
   .nodeSelector(ClickHouseNodeSelector.of(ClickHouseProtocol.HTTP))
   .build();

请参阅压缩文档,以进一步了解不同的压缩选项。

多条查询

在同一会话中,在一个工作线程内按顺序执行多条查询:

CompletableFuture<List<ClickHouseResponseSummary>> future = ClickHouseClient.send(servers.apply(servers.getNodeSelector()),
    "create database if not exists my_base",
    "use my_base",
    "create table if not exists test_table(s String) engine=Memory",
    "insert into test_table values('1')('2')('3')",
    "select * from test_table limit 1",
    "truncate table test_table",
    "drop table if exists test_table");
List<ClickHouseResponseSummary> results = future.get();

命名参数

您可以按名称传递参数,而不必仅依赖它们在参数列表中的位置。该功能可通过 params 函数实现。

try (ClickHouseClient client = ClickHouseClient.newInstance(ClickHouseProtocol.HTTP);
     ClickHouseResponse response = client.read(servers)
        .format(ClickHouseFormat.RowBinaryWithNamesAndTypes)
        .query("select * from my_table where name=:name limit :limit")
        .params("Ben", 1000)
        .executeAndWait()) {
            //...
        }
}
参数

所有涉及 String 类型的 params 签名 (StringString[]Map<String, String>) 均假定传入的键是有效的 ClickHouse SQL 字符串。例如:

try (ClickHouseClient client = ClickHouseClient.newInstance(ClickHouseProtocol.HTTP);
     ClickHouseResponse response = client.read(servers)
        .format(ClickHouseFormat.RowBinaryWithNamesAndTypes)
        .query("select * from my_table where name=:name")
        .params(Map.of("name","'Ben'"))
        .executeAndWait()) {
            //...
        }
}

如果您不想手动将 String 对象解析为 ClickHouse SQL,可以使用位于 com.clickhouse.data 的辅助函数 ClickHouseValues.convertToSqlExpression

try (ClickHouseClient client = ClickHouseClient.newInstance(ClickHouseProtocol.HTTP);
     ClickHouseResponse response = client.read(servers)
        .format(ClickHouseFormat.RowBinaryWithNamesAndTypes)
        .query("select * from my_table where name=:name")
        .params(Map.of("name", ClickHouseValues.convertToSqlExpression("Ben's")))
        .executeAndWait()) {
            //...
        }
}

在上述示例中,ClickHouseValues.convertToSqlExpression 会对内部的单引号进行转义,并在变量两侧加上一对有效的单引号。

其他类型 (如 IntegerUUIDArrayEnum) 会在 params 中自动转换。

节点发现

Java 客户端支持自动发现 ClickHouse 节点。自动发现功能默认禁用。要手动启用该功能,请将 auto_discovery 设置为 true

properties.setProperty("auto_discovery", "true");

或者在连接 URL 中:

jdbc:ch://my-server/system?auto_discovery=true

启用自动发现后,无需在连接 URL 中指定所有 ClickHouse 节点。URL 中指定的节点将作为种子节点,Java 客户端会自动从系统表以及 clickhouse-keeper 或 zookeeper 中发现更多节点。

以下选项用于配置自动发现功能:

属性默认值说明
auto_discoveryfalse客户端是否应从 system 表以及/或 clickhouse-keeper/zookeeper 中发现更多节点。
node_discovery_interval0节点发现的时间间隔 (毫秒) 。值为 0 或负数表示只进行一次发现。
node_discovery_limit100单次可发现的最大节点数;值为 0 或负数表示不限制。

负载均衡

Java 客户端会根据负载均衡策略选择 ClickHouse 节点来发送请求。一般来说,负载均衡策略负责以下工作:

  1. 从托管的节点列表中获取一个节点。
  2. 管理节点状态。
  3. (可选) 在启用自动发现时,为节点发现调度后台进程并执行健康检查。

以下是可用于配置负载均衡的一组选项:

属性默认值描述
load_balancing_policy""负载均衡策略可以是以下之一:
  • firstAlive - 请求将被发送到受管节点列表中第一个健康节点
  • random - 请求将被发送到受管节点列表中随机选取的一个节点
  • roundRobin - 请求将按照轮询方式依次发送到受管节点列表中的每个节点
  • 实现 ClickHouseLoadBalancingPolicy 的完全限定类名 - 自定义负载均衡策略
  • 如果未指定该策略,请求将被发送到受管节点列表中的第一个节点
    load_balancing_tags""用于筛选节点的负载均衡标签。仅向具有指定标签的节点发送请求。
    health_check_interval0健康检查的时间间隔 (毫秒) 。值为 0 或负数表示只执行一次检查。
    health_check_methodClickHouseHealthCheckMethod.SELECT_ONE健康检查方法。可以是以下之一:
  • ClickHouseHealthCheckMethod.SELECT_ONE - 通过执行 select 1 查询进行检查
  • ClickHouseHealthCheckMethod.PING - 协议级检查,通常更快
  • node_check_interval0以毫秒为单位的节点检查间隔,负数将被视为 0。仅当自上次检查以来已过去指定的时间时,才会检查该节点的状态。
    health_check_intervalnode_check_interval 的区别在于,health_check_interval 选项会调度一个后台任务,对节点列表 (全部节点或故障节点) 执行状态检查,而 node_check_interval 则指定针对某个特定节点,自上次检查以来两次检查之间至少需要间隔的时间。
    check_all_nodesfalse是否对所有节点执行健康检查,还是仅检查故障节点。

    故障转移和重试

    Java 客户端提供一些配置选项,用于设置故障转移和失败查询的重试行为:

    属性默认值说明
    failover0单个请求允许执行故障转移的最大次数。为零或负值时表示不进行故障转移。发生故障转移时,请求会根据负载均衡策略被发送到其他节点,以从故障中恢复。
    retry0单个请求允许重试的最大次数。为零或负值时表示不进行重试。仅当 ClickHouse 服务器返回 NETWORK_ERROR 错误码时,才会在同一节点上对该请求进行重试。
    repeat_on_session_locktrue当会话被锁定时,是否重复执行直到会话超时 (根据 session_timeoutconnect_timeout) 。如果 ClickHouse 服务器返回 SESSION_IS_LOCKED 错误码,则会对该失败请求进行重试。

    添加自定义 HTTP 请求头

    Java 客户端在使用 HTTP/S 传输层时支持为请求添加自定义 HTTP 头。 应使用 custom_http_headers 属性,多个请求头使用 , 分隔,单个请求头的键和值使用 = 分隔。

    Java 客户端支持

    options.put("custom_http_headers", "X-ClickHouse-Quota=test, X-ClickHouse-Test=test");
    

    JDBC 驱动

    properties.setProperty("custom_http_headers", "X-ClickHouse-Quota=test, X-ClickHouse-Test=test");