Skip to content
ClickHouse Docs
ClickHouse DocsClickHouse Docs

Cliente Java

Biblioteca cliente Java para comunicação com um servidor de banco de dados por meio de seus protocolos. A implementação atual suporta apenas a interface HTTP. A biblioteca fornece sua própria API para enviar requisições a um servidor. Ela também oferece ferramentas para trabalhar com diferentes formatos de dados binários (RowBinary* & Native*).

Setup


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

Inicialização

O objeto Client é inicializado por com.clickhouse.client.api.Client.Builder#build(). Cada cliente tem seu próprio contexto e nenhum objeto é compartilhado entre eles. O Builder possui métodos de configuração para facilitar a configuração.

Exemplo:

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

Client é AutoCloseable e deve ser fechado quando não for mais necessário.

Autenticação

A autenticação é configurada por cliente na fase de inicialização. Há três métodos de autenticação suportados: por senha, por token de acesso e por certificado de cliente SSL.

A autenticação por senha requer a definição do nome de usuário e da senha por meio das chamadas setUsername(String) e setPassword(String):

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

A autenticação por token de acesso requer a configuração do token de acesso por meio da chamada setAccessToken(String):

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

A autenticação por certificado de cliente SSL requer a definição do nome de usuário, a habilitação da autenticação SSL, e a configuração de um certificado de cliente e uma chave de cliente por meio das chamadas setUsername(String), useSSLAuthentication(boolean), setClientCertificate(String) e setClientKey(String), respectivamente:

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

Configuração

Todas as configurações são definidas por métodos de instância (também conhecidos como métodos de configuração) que deixam o escopo e o contexto de cada valor explícitos. Os principais parâmetros de configuração são definidos em um único escopo (cliente ou operação) e não se substituem entre si.

A configuração é definida durante a criação do cliente. Consulte com.clickhouse.client.api.Client.Builder.

Configuração do Cliente

Método Argumentos Descrição Padrão Chave
addEndpoint(String endpoint) endpoint - endereço do servidor no formato de URL Adiciona um endpoint de servidor à lista de servidores disponíveis. Atualmente, apenas um endpoint é compatível. none none
addEndpoint(Protocol protocol, String host, int port, boolean secure) protocol - protocolo de conexão
host - IP ou hostname
secure - usa HTTPS
Adiciona um endpoint de servidor à lista de servidores disponíveis. Atualmente, apenas um endpoint é compatível. none none
enableConnectionPool(boolean enable) enable - flag para habilitar/desabilitar Define se o pool de conexões está habilitado true connection_pool_enabled
setMaxConnections(int maxConnections) maxConnections - número de conexões Define quantas conexões um cliente pode abrir para cada endpoint do servidor. 10 max_open_connections
setConnectionTTL(long timeout, ChronoUnit unit) timeout - valor do timeout
unit - unidade de tempo
Define o TTL da conexão, após o qual ela será considerada inativa -1 connection_ttl
setKeepAliveTimeout(long timeout, ChronoUnit unit) timeout - valor do timeout
unit - unidade de tempo
Define o tempo limite de keep-alive da conexão HTTP. Defina 0 para desabilitar o Keep-Alive. - http_keep_alive_timeout
setConnectionReuseStrategy(ConnectionReuseStrategy strategy) strategy - LIFO ou FIFO Seleciona qual estratégia o pool de conexões deve usar FIFO connection_reuse_strategy
setDefaultDatabase(String database) database - nome de um banco de dados Define o banco de dados padrão. default database

Identificação do Cliente

Há dois campos em um log de consulta que identificam a aplicação que originou uma requisição: client_name e http_user_agent. O protocolo TCP nativo usa client_name para identificar a aplicação. O protocolo HTTP usa http_user_agent para identificar a aplicação. O construtor do cliente possui o método setClientName para definir os valores corretos para ambos os protocolos. O campo http_user_agent é definido de acordo com o formato comum do cabeçalho User-Agent: application-name[/version] [(operating-system; architecture; ...)]. Esse conjunto de valores é repetido para cada camada: aplicação, biblioteca cliente, biblioteca cliente HTTP. O que é definido pelo método setClientName aparece primeiro na lista.

Por exemplo:

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

resultará no seguinte valor de 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

O aplicativo pode definir seu próprio cabeçalho HTTP User-Agent para se identificar. Porém, a parte clickhouse-java-v2/0.9.6-SNAPSHOT será adicionada ao final do cabeçalho.

Identificação de Operação

O log de consultas tem mais dois campos, query_id e log_comment, que podem ser usados para identificar uma operação e adicionar mais informações ao log de consultas.

query_id é um identificador único de uma operação. Ele pode ser definido pela aplicação ao chamar o método setQueryId da classe QuerySettings.

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

log_comment é um comentário que pode ser adicionado ao log de consultas. Ele pode ser definido pela aplicação ao chamar o método logComment da classe QuerySettings.

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

Configurações do Servidor

As configurações do lado do servidor podem ser definidas no nível do cliente uma única vez durante a criação (consulte o método serverSetting do Builder) e no nível da operação (consulte serverSetting na classe de configurações de operação).

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

	...
}

⚠️ Quando as opções são definidas por meio do método setOption (seja na classe Client.Builder ou na classe de configurações de operação), o nome das server settings deve ser prefixado com clickhouse_setting_. O método com.clickhouse.client.api.ClientConfigProperties#serverSetting() pode ser útil nesse caso.

HTTP Header Personalizado

HTTP headers personalizados podem ser definidos para todas as operações (nível de cliente) ou para uma única (nível de operação).


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

Quando as opções são definidas por meio do método setOption (seja no Client.Builder ou na classe de configurações da operação), o nome do cabeçalho personalizado deve ser prefixado com http_header_. O método com.clickhouse.client.api.ClientConfigProperties#httpHeader() pode ser útil nesse caso.

Definições Comuns

ClickHouseFormat

Enum dos formatos compatíveis. Inclui todos os formatos suportados pelo ClickHouse.

  • raw - o usuário deve transcodificar os dados brutos
  • full - o cliente pode transcodificar os dados por conta própria e aceita um fluxo de dados brutos
  • - - operação não suportada pelo ClickHouse para este formato

Esta versão do cliente é compatível com:

Formato Entrada Saída
TabSeparated raw raw
TabSeparatedRaw raw raw
TabSeparatedWithNames raw raw
TabSeparatedWithNamesAndTypes raw raw
TabSeparatedRawWithNames raw raw
TabSeparatedRawWithNamesAndTypes raw raw
Template raw raw
TemplateIgnoreSpaces raw -
CSV raw raw
CSVWithNames raw raw
CSVWithNamesAndTypes raw raw
CustomSeparated raw raw
CustomSeparatedWithNames raw raw
CustomSeparatedWithNamesAndTypes raw raw
SQLInsert - raw
Values raw raw
Vertical - raw
JSON raw raw
JSONAsString raw -
JSONAsObject raw -
JSONStrings raw raw
JSONColumns raw raw
JSONColumnsWithMetadata raw raw
JSONCompact raw raw
JSONCompactStrings - raw
JSONCompactColumns raw raw
JSONEachRow raw raw
PrettyJSONEachRow - raw
JSONEachRowWithProgress - raw
JSONStringsEachRow raw raw
JSONStringsEachRowWithProgress - raw
JSONCompactEachRow raw raw
JSONCompactEachRowWithNames raw raw
JSONCompactEachRowWithNamesAndTypes raw raw
JSONCompactStringsEachRow raw raw
JSONCompactStringsEachRowWithNames raw raw
JSONCompactStringsEachRowWithNamesAndTypes raw raw
JSONObjectEachRow raw raw
BSONEachRow raw raw
TSKV raw raw
Pretty - raw
PrettyNoEscapes - raw
PrettyMonoBlock - raw
PrettyNoEscapesMonoBlock - raw
PrettyCompact - raw
PrettyCompactNoEscapes - raw
PrettyCompactMonoBlock - raw
PrettyCompactNoEscapesMonoBlock - raw
PrettySpace - raw
PrettySpaceNoEscapes - raw
PrettySpaceMonoBlock - raw
PrettySpaceNoEscapesMonoBlock - raw
Prometheus - raw
Protobuf raw raw
ProtobufSingle raw raw
ProtobufList raw raw
Avro raw raw
AvroConfluent raw -
Parquet raw raw
ParquetMetadata raw -
Arrow raw raw
ArrowStream raw raw
ORC raw raw
One raw -
Npy raw raw
RowBinary full full
RowBinaryWithNames full full
RowBinaryWithNamesAndTypes full full
RowBinaryWithDefaults full -
Native full raw
Null - raw
XML - raw
CapnProto raw raw
LineAsString raw raw
Regexp raw -
RawBLOB raw raw
MsgPack raw raw
MySQLDump raw -
DWARF raw -
Markdown - raw
Form raw -

API de Insert

insert(String tableName, InputStream data, ClickHouseFormat format)

Aceita dados como um InputStream de bytes no formato especificado. Espera-se que data esteja codificado em format.

Assinaturas

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

Parâmetros

tableName - nome da tabela de destino.

data - um fluxo de entrada de dados codificados.

format - um formato no qual os dados são codificados.

settings - configurações da requisição.

Valor de retorno

Futuro do tipo InsertResponse - resultado da operação e informações adicionais, como métricas do lado do servidor.

Exemplos

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)

Envia uma requisição de escrita ao banco de dados. A lista de objetos é convertida em um formato eficiente e então enviada ao servidor. A classe dos itens da lista deve ser registrada previamente usando o método register(Class, TableSchema).

Assinaturas

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

Parâmetros

tableName - nome da tabela de destino.

data - objetos DTO (Data Transfer Object) de coleção.

settings - configurações da requisição.

Valor de retorno

Future do tipo InsertResponse - o resultado da operação e informações adicionais, como métricas do lado do servidor.

Exemplos

// 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

Opções de configuração para operações de inserção.

Métodos de configuração

Método Descrição
setQueryId(String queryId) Define o ID da consulta a ser atribuído à operação. Padrão: null.
setDeduplicationToken(String token) Define o token de desduplicação. Esse token será enviado ao servidor e pode ser usado para identificar a consulta. Padrão: null.
setInputStreamCopyBufferSize(int size) Tamanho do buffer de cópia. O buffer é usado durante operações de escrita para copiar dados do fluxo de entrada fornecido pelo usuário para o fluxo de saída. Padrão: 8196.
serverSetting(String name, String value) Define configurações individuais do servidor para uma operação.
serverSetting(String name, Collection values) Define configurações individuais do servidor com vários valores para uma operação. Os itens da coleção devem ser do tipo String.
setDBRoles(Collection dbRoles) Define os roles de DB a serem configurados antes da execução de uma operação. Os itens da coleção devem ser valores String.
setOption(String option, Object value) Define uma opção de configuração em formato bruto. Não se trata de uma configuração do servidor.

InsertResponse

Objeto de resposta que contém o resultado da operação de inserção. Ele só estará disponível se o cliente receber uma resposta do servidor.

Método Descrição
OperationMetrics getMetrics() Retorna um objeto com as métricas da operação.
String getQueryId() Retorna o ID da consulta atribuído à operação pela aplicação (por meio das configurações da operação ou pelo servidor).

API de Consulta

query(String sqlQuery)

Envia sqlQuery sem modificações. O formato da resposta é definido pelas configurações da consulta. QueryResponse manterá uma referência ao stream de resposta que deve ser consumido por um leitor para o formato suportado.

Assinaturas

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

Parâmetros

sqlQuery - uma única instrução SQL. A consulta é enviada sem modificações para um servidor.

settings - configurações da requisição.

Valor de retorno

Future do tipo QueryResponse - um conjunto de dados de resultado e informações adicionais como métricas do lado do servidor. O objeto Response deve ser fechado após o consumo do conjunto de dados.

Exemplos

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)

Envia sqlQuery sem modificações. Além disso, enviará parâmetros de consulta para que o servidor possa compilar a expressão SQL.

Assinaturas

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

Parâmetros

sqlQuery - expressão SQL com placeholders {}.

queryParams - map de variáveis para completar a expressão SQL no servidor.

settings - configurações da requisição.

Valor de retorno

Futuro do tipo QueryResponse — um conjunto de dados de resultado e informações adicionais, como métricas do lado do servidor. O objeto Response deve ser fechado após consumir o conjunto de dados.

Exemplos


// 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)

Consulta dados no formato RowBinaryWithNamesAndTypes. Retorna o resultado como uma coleção. O desempenho de leitura é o mesmo que com o leitor, mas é necessária mais memória para armazenar o conjunto de dados completo.

Assinaturas

List<GenericRecord> queryAll(String sqlQuery)

Parâmetros

sqlQuery - expressão SQL para consultar dados em um servidor.

Valor de retorno

Conjunto de dados completo representado por uma lista de objetos GenericRecord que fornecem acesso no estilo de linha aos dados resultantes.

Exemplos

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

Opções de configuração para operações de consulta.

Métodos de configuração

Método Descrição
setQueryId(String queryId) Define o ID da consulta a ser atribuído à operação.
setFormat(ClickHouseFormat format) Define o formato da resposta. Consulte RowBinaryWithNamesAndTypes para ver a lista completa.
setMaxExecutionTime(Integer maxExecutionTime) Define o tempo de execução da operação no servidor. Não afeta o timeout de leitura.
waitEndOfQuery(Boolean waitEndOfQuery) Solicita ao servidor que aguarde o término da consulta antes de enviar uma resposta.
setUseServerTimeZone(Boolean useServerTimeZone) O fuso horário do servidor (consulte a configuração do cliente) será usado para fazer o parse dos tipos de data/hora no resultado de uma operação. O padrão é false.
setUseTimeZone(String timeZone) Solicita ao servidor que use timeZone para a conversão de horário. Consulte session_timezone.
serverSetting(String name, String value) Define configurações individuais do servidor para uma operação.
serverSetting(String name, Collection values) Define configurações individuais do servidor com vários valores para uma operação. Os itens da coleção devem ser do tipo String.
setDBRoles(Collection dbRoles) Define os roles de DB a serem configurados antes da execução de uma operação. Os itens da coleção devem ser valores String.
setOption(String option, Object value) Define uma opção de configuração em formato bruto. Não se trata de uma configuração do servidor.

QueryResponse

Objeto de resposta que contém o resultado da execução da consulta. Ele só estará disponível se o cliente receber uma resposta do servidor.

Método Descrição
ClickHouseFormat getFormat() Retorna o formato em que os dados da resposta são codificados.
InputStream getInputStream() Retorna o fluxo de bytes não comprimido dos dados no formato especificado.
OperationMetrics getMetrics() Retorna um objeto com as métricas da operação.
String getQueryId() Retorna o ID da consulta atribuído à operação pela aplicação (por meio das configurações da operação ou pelo servidor).
TimeZone getTimeZone() Retorna o fuso horário que deve ser usado para processar os tipos Date/DateTime na resposta.

Exemplos

API Comum

getTableSchema(String table)

Busca o esquema da tabela table.

Assinaturas

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

Parâmetros

table - nome da tabela para a qual os dados de esquema devem ser buscados.

database - banco de dados onde a tabela de destino está definida.

Valor de retorno

Retorna um objeto TableSchema com a lista de colunas da tabela.

getTableSchemaFromQuery(String sql)

Obtém o esquema a partir de uma instrução SQL.

Assinaturas

TableSchema getTableSchemaFromQuery(String sql)

Parâmetros

sql - instrução SQL "SELECT" cujo esquema deve ser retornado.

Valor de retorno

Retorna um objeto TableSchema com colunas que correspondem à expressão sql.

TableSchema

register(Class<?> clazz, TableSchema schema)

Compila a camada de serialização e desserialização para a classe Java usar na gravação/leitura de dados com o schema. O método criará um serializador e um desserializador para o par getter/setter e a coluna correspondente. A correspondência da coluna é feita extraindo seu nome do nome de um método. Por exemplo, getFirstName corresponderá à coluna first_name ou firstname.

Assinaturas

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

Parâmetros

clazz - Classe que representa o POJO usado para ler/gravar dados.

schema - Esquema de dados a ser usado para correspondência com propriedades POJO.

Exemplos

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

Exemplos de uso

O código completo dos exemplos está armazenado no repositório na pasta 'example`:

  • client-v2 - coleção principal de exemplos.
  • demo-service - exemplo de uso do cliente em uma aplicação Spring Boot.
  • demo-kotlin-service - exemplo de como usar o cliente em um aplicativo Ktor (Kotlin).

Lendo Dados

Existem duas maneiras comuns de ler dados:

  • método query() que retorna um objeto QueryResponse de baixo nível, que contém um InputStream com os dados. Geralmente combinado com ClickHouseBinaryFormatReader para leituras em streaming, mas pode ser usado com qualquer outra implementação personalizada de leitor. QueryResponse também fornece acesso aos metadados do conjunto de resultados e às métricas.
  • método queryAll() e uso de GenericRecord para acesso prático às linhas. Nesse caso, todo o conjunto de resultados é carregado na memória.
  • método queryRecords() que retorna com.clickhouse.client.api.query.Records — um iterador de objetos GenericRecord. Esse método usa uma abordagem de streaming (nenhum dado é carregado na memória) e utiliza GenericRecord para acessar os dados.

Nota: a abordagem de streaming exige leitura rápida; caso contrário, pode causar timeout de escrita no servidor, pois os dados são lidos diretamente do stream de rede.

Lendo Arrays

Métodos de ClickHouseBinaryFormatReader

  • getList(...) - lê qualquer Array(...) como List<T>. Boa opção padrão para leituras tipadas flexíveis. Suporta arrays aninhados.
  • getByteArray(...), getShortArray(...), getIntArray(...), getLongArray(...), getFloatArray(...), getDoubleArray(...), getBooleanArray(...) - mais indicados para arrays unidimensionais de valores compatíveis com tipos primitivos.
  • getStringArray(...) - para Array(String) (e valores de enum representados por nomes).
  • getObjectArray(...) - opção genérica para qualquer tipo de elemento de Array(...), incluindo arrays aninhados. Use-a para ler arrays com valores Nullable e arrays aninhados.

Sobrecargas baseadas em índice e em nome estão disponíveis para todos os métodos. O índice começa em 1. As sobrecargas baseadas em índice realizam acesso direto a uma coluna. Os métodos baseados em nome exigem uma busca pelo índice a cada chamada.

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];
    }
}

Métodos de GenericRecord

  • getList(...) - lê qualquer Array(...) como List<T>. Boa opção padrão para leituras tipadas flexíveis. Suporta arrays aninhados.
  • getByteArray(...), getShortArray(...), getIntArray(...), getLongArray(...), getFloatArray(...), getDoubleArray(...), getBooleanArray(...) - mais indicados para arrays unidimensionais de valores compatíveis com tipos primitivos.
  • getStringArray(...) - para Array(String) (e valores de enum representados por nomes).
  • getObjectArray(...) - opção genérica para qualquer tipo de elemento de Array(...), incluindo arrays aninhados. Use-a para ler arrays com valores Nullable e arrays aninhados.

Sobrecargas baseadas em índice e em nome estão disponíveis para todos os métodos. O índice começa em 1. As sobrecargas baseadas em índice realizam acesso direto a uma coluna. Os métodos baseados em nome exigem uma busca pelo índice a cada chamada.

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
    }
}

Guia de Migração

O cliente antigo (V1) usava com.clickhouse.client.ClickHouseClient#builder como ponto de partida. O novo cliente (V2) utiliza um padrão semelhante com com.clickhouse.client.api.Client.Builder. As principais diferenças são:

  • nenhum carregador de serviço é usado para recuperar a implementação. A com.clickhouse.client.api.Client é uma classe de fachada para todos os tipos de implementação que vierem a existir.
  • menos fontes de configuração: uma é fornecida ao builder e a outra fica nas configurações da operação (QuerySettings, InsertSettings). A versão anterior tinha configuração por nó e, em alguns casos, carregava variáveis de ambiente.

Correspondência de Parâmetros de Configuração

Existem 3 classes enum relacionadas à configuração na V1:

  • com.clickhouse.client.config.ClickHouseDefaults - parâmetros de configuração que devem ser definidos na maioria dos casos de uso, como USER e PASSWORD.
  • com.clickhouse.client.config.ClickHouseClientOption - parâmetros de configuração específicos do cliente, como HEALTH_CHECK_INTERVAL.
  • com.clickhouse.client.http.config.ClickHouseHttpOption - parâmetros de configuração específicos da interface HTTP, como RECEIVE_QUERY_PROGRESS.

Eles foram projetados para agrupar parâmetros e fornecer uma separação clara. No entanto, em alguns casos isso gerava confusão (existe diferença entre com.clickhouse.client.config.ClickHouseDefaults#ASYNC e com.clickhouse.client.config.ClickHouseClientOption#ASYNC?). O novo cliente V2 utiliza com.clickhouse.client.api.Client.Builder como dicionário único de todas as opções de configuração possíveis do cliente. Em com.clickhouse.client.api.ClientConfigProperties estão listados todos os nomes de parâmetros de configuração.

A tabela abaixo mostra quais opções antigas são compatíveis com o novo cliente e seus novos significados.

Legenda: ✔ = compatível, ✗ = não compatível

Configuração V1 Método do Builder V2 Comentários
ClickHouseDefaults#HOST Client.Builder#addEndpoint
ClickHouseDefaults#PROTOCOL Apenas HTTP é compatível com V2
ClickHouseDefaults#DATABASE
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
ClickHouseHttpOption#KEEP_ALIVE_TIMEOUT
Client.Builder#setKeepAliveTimeout
ClickHouseHttpOption#CONNECTION_REUSE_STRATEGY Client.Builder#setConnectionReuseStrategy
ClickHouseHttpOption#USE_BASIC_AUTHENTICATION Client.Builder#useHTTPBasicAuth

Diferenças Gerais

  • O Client V2 usa menos classes proprietárias para aumentar a portabilidade. Por exemplo, o V2 funciona com qualquer implementação de java.io.InputStream para gravar dados em um servidor.
  • A configuração async do Client V2 vem como off por padrão. Isso significa que não há threads extras e que a aplicação tem mais controle sobre o cliente. Essa configuração deve permanecer off na maioria dos casos de uso. Ao habilitar async, uma thread separada será criada para cada solicitação. Isso só faz sentido ao usar um executor controlado pela aplicação (veja com.clickhouse.client.api.Client.Builder#setSharedOperationExecutor)

Gravação de dados

  • use qualquer implementação de java.io.InputStream. A versão V1 com.clickhouse.data.ClickHouseInputStream é compatível, mas NÃO é recomendada.
  • Uma vez detectado o fim do fluxo de entrada, ele é tratado adequadamente. Antes disso, o fluxo de saída de uma requisição deve ser fechado.

V1 Inserir dados no formato TSV.

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 Inserir dados no formato 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
}
  • há apenas um método para chamar. Não é necessário criar um objeto de requisição adicional.
  • o stream do corpo da requisição é fechado automaticamente quando todos os dados são copiados.
  • há uma nova API de baixo nível disponível 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 foi projetado para implementar uma lógica personalizada de gravação de dados. Por exemplo, lendo dados de uma fila.

Lendo Dados

  • Os dados são lidos no formato RowBinaryWithNamesAndTypes por padrão. No momento, apenas esse formato é compatível quando o binding de dados é necessário.
  • Os dados podem ser lidos como uma coleção de registros usando o método List<GenericRecord> com.clickhouse.client.api.Client#queryAll(java.lang.String). Ele lê os dados para a memória e libera a conexão. Não é necessário nenhum tratamento adicional. GenericRecord fornece acesso aos dados e implementa algumas conversões.
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");
}

Biblioteca cliente Java para comunicação com um servidor de banco de dados por meio de seus protocolos. A implementação atual suporta apenas a interface HTTP. A biblioteca disponibiliza uma API própria para enviar requisições ao servidor.

Setup

<!-- 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>

A partir da versão 0.5.0, o driver utiliza uma nova biblioteca HTTP de cliente que precisa ser adicionada como dependência.

<!-- 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>

Inicialização

Formato de URL de conexão: protocol://host[:port][/database][?param[=value][&param[=value]][#tag[,tag]], por exemplo:

  • http://localhost:8443?ssl=true&sslmode=NONE
  • https://(https://explorer@play.clickhouse.com:443

Conecte-se a um único nó:

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

Conecte-se a um cluster com múltiplos nós:

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 de Consulta

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 de Consulta em Streaming

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();
            // conversão de tipos
            String str = r.getValue(0).asString();
            LocalDate date = r.getValue(0).asDate();
        }
}

Veja o exemplo de código completo no repositório.

API de Insert

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` é a fonte de dados no formato RowBinary
        .executeAndWait()) {
            ClickHouseResponseSummary summary = response.getSummary();
            summary.getWrittenRows();
}

Consulte o exemplo de código completo no repositório.

Codificação RowBinary

O formato RowBinary é descrito em sua página.

Há um exemplo de código.

Funcionalidades

Compressão

Por padrão, o cliente usará compressão LZ4, o que exige esta dependência:

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

Você pode optar por usar gzip definindo compress_algorithm=gzip na URL de conexão.

Como alternativa, você pode desativar a compressão de algumas formas.

  1. Desative definindo compress=0 no URL da conexão: http://localhost:8123/default?compress=0
  2. Desative na configuração do cliente:
ClickHouseClient client = ClickHouseClient.builder()
   .config(new ClickHouseConfig(Map.of(ClickHouseClientOption.COMPRESS, false)))
   .nodeSelector(ClickHouseNodeSelector.of(ClickHouseProtocol.HTTP))
   .build();

Consulte a documentação de compressão para saber mais sobre as diferentes opções de compressão.

Múltiplas consultas

Execute múltiplas consultas em uma thread de trabalho, uma após a outra, dentro da mesma sessão:

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

Parâmetros Nomeados

É possível passar parâmetros pelo nome em vez de depender exclusivamente de sua posição na lista de parâmetros. Esse recurso está disponível por meio da função 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()) {
            //...
        }
}

Descoberta de nós

O Java client oferece a capacidade de descobrir nós do ClickHouse automaticamente. A descoberta automática está desabilitada por padrão. Para habilitá-la manualmente, defina auto_discovery como true:

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

Ou na URL de conexão:

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

Se a descoberta automática estiver habilitada, não é necessário especificar todos os nós do ClickHouse na URL de conexão. Os nós especificados na URL serão tratados como seeds, e o cliente Java descobrirá automaticamente mais nós a partir das system tables e/ou do clickhouse-keeper ou zookeeper.

As seguintes opções são responsáveis pela configuração de descoberta automática:

Propriedade Padrão Descrição
auto_discovery false Indica se o cliente deve descobrir mais nós a partir das tabelas do sistema e/ou do clickhouse-keeper/zookeeper.
node_discovery_interval 0 Intervalo de descoberta de nós, em milissegundos; um valor zero ou negativo significa descoberta única.
node_discovery_limit 100 Número máximo de nós que podem ser descobertos de uma só vez; um valor zero ou negativo significa que não há limite.

Balanceamento de Carga

O Java client escolhe um nó do ClickHouse para enviar as requisições, de acordo com a política de balanceamento de carga. Em geral, a política de balanceamento de carga é responsável pelos seguintes itens:

  1. Recupere um nó de uma lista de nós gerenciados.
  2. Gerenciando o status do nó.
  3. Opcionalmente, agende um processo em segundo plano para a descoberta de nós (se a descoberta automática estiver habilitada) e execute uma verificação de integridade.

A seguir, uma lista de opções para configurar o balanceamento de carga:

Propriedade Padrão Descrição
load_balancing_policy "" A política de balanceamento de carga pode ser uma das seguintes:
  • firstAlive - a solicitação é enviada ao primeiro nó saudável da lista de nós gerenciados
  • random - a solicitação é enviada a um nó aleatório da lista de nós gerenciados
  • roundRobin - a solicitação é enviada a cada nó da lista de nós gerenciados, em rodízio.
  • nome de classe totalmente qualificado que implementa ClickHouseLoadBalancingPolicy - política de balanceamento de carga personalizada
  • Se não for especificada, a solicitação será enviada ao primeiro nó da lista de nós gerenciados
    load_balancing_tags "" Tags de balanceamento de carga para filtrar nós. As solicitações são enviadas apenas aos nós que têm as tags especificadas
    health_check_interval 0 Intervalo de verificação de integridade em milissegundos; um valor zero ou negativo significa execução única.
    health_check_method ClickHouseHealthCheckMethod.SELECT_ONE Método de verificação de integridade. Pode ser um dos seguintes:
  • ClickHouseHealthCheckMethod.SELECT_ONE - verificação com a consulta select 1
  • ClickHouseHealthCheckMethod.PING - verificação específica do protocolo, geralmente mais rápida
  • node_check_interval 0 Intervalo de verificação do nó em milissegundos; números negativos são tratados como zero. O status do nó é verificado quando o intervalo de tempo especificado tiver decorrido desde a última verificação.
    A diferença entre health_check_interval e node_check_interval é que a opção health_check_interval agenda um job em segundo plano que verifica o status da lista de nós (todos ou com falha), enquanto node_check_interval especifica o intervalo de tempo decorrido desde a última verificação de um nó específico
    check_all_nodes false Se deve realizar uma verificação de integridade em todos os nós ou apenas nos nós com falha.

    Failover e retry

    O Java client fornece opções de configuração para definir o comportamento de failover e retry para queries com falha:

    Propriedade Padrão Descrição
    failover 0 Número máximo de vezes que pode ocorrer failover em uma solicitação. Zero ou um valor negativo significa que não há failover. O failover envia a solicitação que falhou para um nó diferente (de acordo com a política de balanceamento de carga) para se recuperar da falha.
    retry 0 Número máximo de vezes que uma nova tentativa pode ocorrer para uma solicitação. Zero ou um valor negativo significa que não haverá nova tentativa. A nova tentativa envia uma solicitação para o mesmo nó, e somente se o ClickHouse server retornar o código de erro NETWORK_ERROR
    repeat_on_session_lock true Se a execução deve ser repetida quando a sessão estiver bloqueada até ocorrer timeout (de acordo com session_timeout ou connect_timeout). A solicitação que falhou é repetida se o servidor ClickHouse retornar o código de erro SESSION_IS_LOCKED

    Adicionando headers HTTP personalizados

    O Java client oferece suporte à camada de transporte HTTP/S para adicionar HTTP headers personalizados à requisição. Utilize a propriedade custom_http_headers; os headers devem ser separados por ,. O par chave/valor do header deve ser separado com =

    Suporte ao Java Client

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

    Driver JDBC

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