Skip to content
ClickHouse Docs
ClickHouse DocsClickHouse Docs

Cliente de Java

Biblioteca cliente de Java para comunicarse con un servidor de base de datos a través de sus protocolos. La implementación actual solo admite la interfaz HTTP. La biblioteca ofrece su propia API para enviar solicitudes a un servidor, y también incluye herramientas para trabajar con distintos formatos de datos binarios (RowBinary* & Native*).

Configuración


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

Inicialización

El objeto Client se inicializa mediante com.clickhouse.client.api.Client.Builder#build(). Cada cliente tiene su propio contexto y no se comparten objetos entre ellos. El Builder dispone de métodos de configuración para facilitar la configuración.

Ejemplo:

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

Client es AutoCloseable y debe cerrarse cuando ya no se necesite.

Autenticación

La autenticación se configura por cliente en la fase de inicialización. Se admiten tres métodos de autenticación: por contraseña, por token de acceso y por certificado de cliente SSL.

La autenticación mediante contraseña requiere configurar el nombre de usuario y la contraseña llamando a setUsername(String) y setPassword(String):

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

La autenticación mediante un token de acceso requiere configurar el token de acceso llamando a setAccessToken(String):

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

La autenticación mediante un certificado de cliente SSL requiere configurar el nombre de usuario, habilitar la autenticación SSL, y establecer un certificado de cliente y una clave de cliente mediante las llamadas a setUsername(String), useSSLAuthentication(boolean), setClientCertificate(String) y setClientKey(String) respectivamente:

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

Configuración

Todas las configuraciones se definen mediante métodos de instancia (también conocidos como métodos de configuración) que hacen explícitos el alcance y el contexto de cada valor. Los principales parámetros de configuración se definen en un único ámbito (cliente u operación) y no se sobrescriben entre sí.

La configuración se define durante la creación del cliente. Consulte com.clickhouse.client.api.Client.Builder.

Configuración del cliente

Método Argumentos Descripción Predeterminado Clave
addEndpoint(String endpoint) endpoint - dirección del servidor en formato URL Añade un endpoint del servidor a la lista de servidores disponibles. Actualmente, solo se admite un endpoint. none none
addEndpoint(Protocol protocol, String host, int port, boolean secure) protocol - protocolo de conexión
host - IP o nombre de host
secure - usar HTTPS
Añade un endpoint del servidor a la lista de servidores disponibles. Actualmente, solo se admite un endpoint. none none
enableConnectionPool(boolean enable) enable - indicador para habilitar o deshabilitar Establece si el pool de conexiones está habilitado true connection_pool_enabled
setMaxConnections(int maxConnections) maxConnections - número de conexiones Establece cuántas conexiones puede abrir un cliente para cada endpoint del servidor. 10 max_open_connections
setConnectionTTL(long timeout, ChronoUnit unit) timeout - valor de tiempo de espera
unit - unidad de tiempo
Establece el TTL de la conexión, tras el cual se considerará inactiva -1 connection_ttl
setKeepAliveTimeout(long timeout, ChronoUnit unit) timeout - valor de tiempo de espera
unit - unidad de tiempo
Establece el tiempo de espera de Keep-Alive de la conexión HTTP. Establézcalo en 0 para desactivar Keep-Alive. - http_keep_alive_timeout
setConnectionReuseStrategy(ConnectionReuseStrategy strategy) strategy - LIFO o FIFO Selecciona qué estrategia debe usar el pool de conexiones FIFO connection_reuse_strategy
setDefaultDatabase(String database) database - nombre de una base de datos Establece la base de datos predeterminada. default database

Identificación del cliente

Hay dos campos en el log de consultas que identifican la aplicación que originó una solicitud: client_name y http_user_agent. El protocolo TCP nativo utiliza client_name para identificar la aplicación. El protocolo HTTP utiliza http_user_agent para identificar la aplicación. El constructor del cliente dispone del método setClientName para establecer los valores correctos en ambos protocolos. El campo http_user_agent se establece según el formato común del header User-Agent: application-name[/version] [(operating-system; architecture; ...)]. Este conjunto de valores se repite para cada capa: aplicación, biblioteca cliente y biblioteca cliente HTTP. Lo que establece el método setClientName aparece primero en la lista.

Por ejemplo:

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

dará como resultado el siguiente 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

La aplicación puede establecer su propio encabezado HTTP User-Agent para identificarse. No obstante, el fragmento clickhouse-java-v2/0.9.6-SNAPSHOT se añadirá al final del encabezado.

Identificación de operaciones

El registro de consultas cuenta con otros dos campos, query_id y log_comment, que pueden utilizarse para identificar una operación y agregar información adicional al registro de consultas.

query_id es un identificador único de una operación. La aplicación puede establecerlo llamando al método setQueryId de la clase QuerySettings.

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

log_comment es un comentario que se puede añadir al registro de consultas. Las aplicaciones pueden establecerlo llamando al método logComment de la clase QuerySettings.

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

Configuración del servidor

Los ajustes del lado del servidor pueden configurarse a nivel de cliente una sola vez durante la creación (consulte el método serverSetting de Builder) y a nivel de operación (consulte serverSetting en la clase de ajustes de operación).

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

	...
}

⚠️ Cuando las opciones se configuran mediante el método setOption (ya sea en Client.Builder o en la clase de configuración de operaciones), el nombre de los server settings debe ir precedido del prefijo clickhouse_setting_. En este caso, com.clickhouse.client.api.ClientConfigProperties#serverSetting() puede resultar de utilidad.

Encabezado HTTP personalizado

Se pueden configurar HTTP headers personalizados para todas las operaciones (nivel de cliente) o para una sola (nivel de operación).


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

Cuando las opciones se configuran mediante el método setOption (ya sea en Client.Builder o en la clase de configuración de operaciones), el nombre del encabezado personalizado debe llevar el prefijo http_header_. El método com.clickhouse.client.api.ClientConfigProperties#httpHeader() puede ser de utilidad en este caso.

Definiciones comunes

ClickHouseFormat

Enum de formatos compatibles. Incluye todos los formatos admitidos por ClickHouse.

  • raw - el usuario debe transcodificar los datos sin procesar
  • full - el cliente puede transcodificar datos por su cuenta y acepta un flujo de datos sin procesar
  • - - operación no admitida por ClickHouse para este formato

Esta versión del cliente admite:

Formato Entrada Salida
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 inserción

insert(String tableName, InputStream data, ClickHouseFormat format)

Acepta datos como un InputStream de bytes en el formato especificado. Se espera que data esté codificada en el format.

Firmas

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

Parámetros

tableName - nombre de la tabla de destino.

data - un flujo de entrada de datos codificados.

format - un formato en el que se codifican los datos.

settings - parámetros de la solicitud.

Valor de retorno

Future del tipo InsertResponse: resultado de la operación e información adicional como las métricas del lado del servidor.

Ejemplos

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)

Envía una solicitud de escritura a la base de datos. La lista de objetos se convierte a un formato eficiente y luego se envía al servidor. La clase de los elementos de la lista debe registrarse de antemano mediante el método register(Class, TableSchema).

Firmas

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

Parámetros

tableName - nombre de la tabla de destino.

data - objetos DTO (Data Transfer Object) de colección.

settings - parámetros de la solicitud.

Valor de retorno

Futuro del tipo InsertResponse: el resultado de la operación e información adicional como métricas del lado del servidor.

Ejemplos

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

Opciones de configuración para operaciones de inserción.

Métodos de configuración

Método Descripción
setQueryId(String queryId) Establece el ID de consulta que se asignará a la operación. Valor predeterminado: null.
setDeduplicationToken(String token) Establece el token de deduplicación. Este token se enviará al servidor y puede utilizarse para identificar la consulta. Valor predeterminado: null.
setInputStreamCopyBufferSize(int size) Tamaño del búfer de copia. El búfer se utiliza durante las operaciones de escritura para copiar datos del flujo de entrada proporcionado por el usuario a un flujo de salida. Valor predeterminado: 8196.
serverSetting(String name, String value) Establece ajustes individuales del servidor para una operación.
serverSetting(String name, Collection values) Establece opciones de configuración individuales del servidor con varios valores para una operación. Los elementos de la colección deben ser valores String.
setDBRoles(Collection dbRoles) Establece los roles de DB que deben configurarse antes de ejecutar una operación. Los elementos de la colección deben ser valores String.
setOption(String option, Object value) Establece una opción de configuración en formato raw. No es una configuración del servidor.

InsertResponse

Objeto de respuesta que contiene el resultado de la operación de inserción. Solo está disponible si el cliente recibió una respuesta de un servidor.

Método Descripción
OperationMetrics getMetrics() Devuelve un objeto con las métricas de la operación.
String getQueryId() Devuelve el ID de consulta asignado a la operación por la aplicación (mediante la configuración de la operación o por el servidor).

API de consultas

query(String sqlQuery)

Envía sqlQuery tal cual. El formato de respuesta se determina mediante la configuración de la consulta. QueryResponse contendrá una referencia al flujo de respuesta que debe ser consumido por un lector para el formato compatible.

Firmas

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

Parámetros

sqlQuery - una única sentencia SQL. La consulta se envía tal como está al servidor.

settings - parámetros de la solicitud.

Valor de retorno

Future del tipo QueryResponse: un conjunto de datos de resultado e información adicional como las métricas del lado del servidor. El objeto Response debe cerrarse tras consumir el conjunto de datos.

Ejemplos

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)

Envía sqlQuery tal cual. Además, enviará los parámetros de consulta para que el servidor pueda compilar la expresión SQL.

Firmas

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

Parámetros

sqlQuery - expresión SQL con marcadores de posición {}.

queryParams - mapa de variables para completar la expresión SQL en el servidor.

settings - parámetros de la solicitud.

Valor de retorno

Future del tipo QueryResponse: un conjunto de datos de resultado e información adicional como las métricas del lado del servidor. El objeto Response debe cerrarse tras consumir el conjunto de datos.

Ejemplos


// 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 datos en formato RowBinaryWithNamesAndTypes. Devuelve el resultado como una colección. El rendimiento de lectura es el mismo que con el lector, pero se necesita más memoria para almacenar el conjunto de datos completo.

Firmas

List<GenericRecord> queryAll(String sqlQuery)

Parámetros

sqlQuery - expresión SQL para consultar datos desde un servidor.

Valor de retorno

Conjunto de datos completo representado por una lista de objetos GenericRecord que proporcionan acceso por filas a los datos del resultado.

Ejemplos

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

Opciones de configuración para operaciones de consulta.

Métodos de configuración

Método Descripción
setQueryId(String queryId) Establece el ID de la consulta que se asignará a la operación.
setFormat(ClickHouseFormat format) Establece el formato de la respuesta. Consulte RowBinaryWithNamesAndTypes para ver la lista completa.
setMaxExecutionTime(Integer maxExecutionTime) Establece el tiempo de ejecución de la operación en el servidor. No afecta al tiempo de espera de lectura.
waitEndOfQuery(Boolean waitEndOfQuery) Solicita al servidor que espere a que finalice la consulta antes de enviar una respuesta.
setUseServerTimeZone(Boolean useServerTimeZone) La zona horaria del servidor (consulte la configuración del cliente) se utilizará para interpretar los tipos de fecha/hora en el resultado de una operación. Valor predeterminado: false.
setUseTimeZone(String timeZone) Solicita al servidor que use timeZone para la conversión de hora. Consulte session_timezone.
serverSetting(String name, String value) Establece ajustes individuales del servidor para una operación.
serverSetting(String name, Collection values) Establece opciones de configuración individuales del servidor con varios valores para una operación. Los elementos de la colección deben ser valores String.
setDBRoles(Collection dbRoles) Establece los roles de DB que deben configurarse antes de ejecutar una operación. Los elementos de la colección deben ser valores String.
setOption(String option, Object value) Establece una opción de configuración en formato raw. No es una configuración del servidor.

QueryResponse

Objeto de respuesta que contiene el resultado de la ejecución de la consulta. Solo está disponible si el cliente recibió una respuesta del servidor.

Método Descripción
ClickHouseFormat getFormat() Devuelve el formato en el que están codificados los datos de la respuesta.
InputStream getInputStream() Devuelve un flujo de bytes sin comprimir con los datos en el formato especificado.
OperationMetrics getMetrics() Devuelve un objeto con las métricas de la operación.
String getQueryId() Devuelve el ID de consulta asignado a la operación por la aplicación (mediante la configuración de la operación) o por el servidor.
TimeZone getTimeZone() Devuelve la zona horaria que debe usarse para procesar los tipos Date/DateTime en la respuesta.

Ejemplos

API común

getTableSchema(String table)

Obtiene el esquema de la tabla table.

Firmas

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

Parámetros

table - nombre de la tabla de la que se deben obtener los datos del esquema.

database - base de datos donde se define la tabla de destino.

Valor de retorno

Devuelve un objeto TableSchema con la lista de columnas de la tabla.

getTableSchemaFromQuery(String sql)

Obtiene el esquema a partir de una instrucción SQL.

Firmas

TableSchema getTableSchemaFromQuery(String sql)

Parámetros

sql - Sentencia SQL "SELECT" cuyo esquema se desea obtener.

Valor de retorno

Devuelve un objeto TableSchema con columnas que coinciden con la expresión sql.

TableSchema

register(Class<?> clazz, TableSchema schema)

Compila la capa de serialización y deserialización para la clase Java que se utilizará para escribir y leer datos con schema. El método creará un serializador y un deserializador para el par getter/setter y la columna correspondiente. La coincidencia de columna se determina extrayendo su nombre a partir del nombre del método. Por ejemplo, getFirstName corresponderá a la columna first_name o firstname.

Firmas

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

Parámetros

clazz - Clase que representa el POJO utilizado para leer/escribir datos.

schema - Esquema de datos que se usará para hacer coincidir con las propiedades POJO.

Ejemplos

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

Ejemplos de uso

El código de ejemplos completo está almacenado en el repositorio en la carpeta 'example`:

  • client-v2 - colección principal de ejemplos.
  • demo-service - ejemplo de cómo utilizar el cliente en una aplicación Spring Boot.
  • demo-kotlin-service - ejemplo de cómo usar el cliente en una aplicación de Ktor (Kotlin).

Lectura de datos

Hay dos formas comunes de leer datos:

  • método query() que devuelve un objeto QueryResponse de bajo nivel que contiene un InputStream con datos. Normalmente se combina con ClickHouseBinaryFormatReader para lecturas en streaming, pero puede utilizarse con cualquier otra implementación personalizada de lector. QueryResponse también proporciona acceso a los metadatos del conjunto de resultados y a las métricas.
  • método queryAll() y uso de GenericRecord para acceder cómodamente a las filas. En este caso, todo el conjunto de resultados se carga en memoria.
  • método queryRecords() que devuelve com.clickhouse.client.api.query.Records: un iterador de objetos GenericRecord. Este método utiliza un enfoque de streaming (no se cargan datos en memoria) y usa GenericRecord para acceder a los datos.

Nota: el enfoque de streaming requiere una lectura rápida; de lo contrario, puede provocar un timeout de escritura en el servidor, ya que los datos se leen directamente desde el stream de red.

Lectura de Arrays

Métodos de ClickHouseBinaryFormatReader

  • getList(...) - lee cualquier Array(...) como List<T>. Es una buena opción predeterminada para lecturas flexibles con tipado. Admite arrays anidados.
  • getByteArray(...), getShortArray(...), getIntArray(...), getLongArray(...), getFloatArray(...), getDoubleArray(...), getBooleanArray(...) - mejor para arrays 1D de valores compatibles con tipos primitivos.
  • getStringArray(...) - para Array(String) (y valores de enum representados por nombres).
  • getObjectArray(...) - opción genérica para cualquier tipo de elemento de Array(...), incluidos los arrays anidados. Úselo para leer arrays con valores anulables y arrays anidados.

Las sobrecargas basadas en índice y en nombre están disponibles para todos los métodos. El índice es base 1. Las sobrecargas basadas en índice realizan acceso directo a una columna. Los métodos basados en nombre requieren una búsqueda por índice en cada invocación.

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(...) - lee cualquier Array(...) como List<T>. Es una buena opción predeterminada para lecturas flexibles con tipado. Admite arrays anidados.
  • getByteArray(...), getShortArray(...), getIntArray(...), getLongArray(...), getFloatArray(...), getDoubleArray(...), getBooleanArray(...) - mejor para arrays 1D de valores compatibles con tipos primitivos.
  • getStringArray(...) - para Array(String) (y valores de enum representados por nombres).
  • getObjectArray(...) - opción genérica para cualquier tipo de elemento de Array(...), incluidos los arrays anidados. Úselo para leer arrays con valores anulables y arrays anidados.

Las sobrecargas basadas en índice y en nombre están disponibles para todos los métodos. El índice es base 1. Las sobrecargas basadas en índice realizan acceso directo a una columna. Los métodos basados en nombre requieren una búsqueda por índice en cada invocación.

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

Guía de migración

El cliente antiguo (V1) utilizaba com.clickhouse.client.ClickHouseClient#builder como punto de partida. El nuevo cliente (V2) usa un patrón similar con com.clickhouse.client.api.Client.Builder. Las principales diferencias son:

  • no se utiliza ningún cargador de servicios para obtener la implementación. com.clickhouse.client.api.Client es una clase fachada para todo tipo de implementaciones futuras.
  • menos fuentes de configuración: una se proporciona al builder y otra se encuentra en la configuración de operación (QuerySettings, InsertSettings). La versión anterior tenía una configuración por nodo y, en algunos casos, cargaba variables de entorno.

Coincidencia de parámetros de configuración

Hay 3 clases enum relacionadas con la configuración en V1:

  • com.clickhouse.client.config.ClickHouseDefaults - parámetros de configuración que suelen establecerse en la mayoría de los casos de uso, como USER y PASSWORD.
  • com.clickhouse.client.config.ClickHouseClientOption - parámetros de configuración específicos para el cliente, como HEALTH_CHECK_INTERVAL.
  • com.clickhouse.client.http.config.ClickHouseHttpOption - parámetros de configuración específicos de la interfaz HTTP, como RECEIVE_QUERY_PROGRESS.

Fueron diseñados para agrupar parámetros y ofrecer una separación clara. Sin embargo, en algunos casos esto generaba confusión (¿hay alguna diferencia entre com.clickhouse.client.config.ClickHouseDefaults#ASYNC y com.clickhouse.client.config.ClickHouseClientOption#ASYNC?). El nuevo cliente V2 utiliza com.clickhouse.client.api.Client.Builder como diccionario único de todas las opciones de configuración posibles del cliente. En com.clickhouse.client.api.ClientConfigProperties se encuentran listados todos los nombres de parámetros de configuración.

La siguiente tabla muestra qué opciones antiguas son compatibles con el nuevo cliente y su nuevo significado.

Leyenda: ✔ = compatible, ✗ = eliminado

Configuración de V1 Método del builder de V2 Comentarios
ClickHouseDefaults#HOST Client.Builder#addEndpoint
ClickHouseDefaults#PROTOCOL Solo se admite HTTP en 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

Diferencias generales

  • Client V2 utiliza menos clases propietarias para aumentar la portabilidad. Por ejemplo, V2 funciona con cualquier implementación de java.io.InputStream para escribir datos en un servidor.
  • La configuración async de Client V2 está off de forma predeterminada. Esto significa que no hay hilos adicionales y que la aplicación tiene un mayor control sobre el cliente. Esta configuración debería estar off en la mayoría de los casos de uso. Al habilitar async, se creará un hilo independiente para cada solicitud. Solo tiene sentido cuando se usa un executor controlado por la aplicación (consulte com.clickhouse.client.api.Client.Builder#setSharedOperationExecutor)

Escritura de datos

  • usa cualquier implementación de java.io.InputStream. Se admite V1 com.clickhouse.data.ClickHouseInputStream, pero NO se recomienda.
  • Una vez detectado el final del flujo de entrada, se actúa en consecuencia. Antes, debe cerrarse el flujo de salida de una solicitud.

V1 Insertar datos con 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 Insertar datos con 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
}
  • solo hay que llamar a un único método. No es necesario crear un objeto de solicitud adicional.
  • el flujo del cuerpo de la solicitud se cierra automáticamente cuando se copian todos los datos.
  • hay una nueva API de bajo nivel disponible: 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 está diseñada para implementar lógica personalizada de escritura de datos. Por ejemplo, leer datos de una cola.

Lectura de datos

  • Los datos se leen en el formato RowBinaryWithNamesAndTypes de forma predeterminada. Actualmente, solo se admite este formato cuando se requiere el enlace de datos.
  • Los datos pueden leerse como una colección de registros mediante el método List<GenericRecord> com.clickhouse.client.api.Client#queryAll(java.lang.String). Este método lee los datos en memoria y libera la conexión. No requiere ninguna gestión adicional. GenericRecord proporciona acceso a los datos e implementa algunas conversiones.
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 de Java para comunicarse con un servidor de base de datos a través de sus protocolos. La implementación actual solo admite la interfaz HTTP. La biblioteca ofrece su propia API para enviar solicitudes al servidor.

Configuración

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

Desde la versión 0.5.0, el driver utiliza una nueva biblioteca HTTP de cliente que debe agregarse como dependencia.

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

Inicialización

Formato de URL de conexión: protocol://host[:port][/database][?param[=value][&param[=value]][#tag[,tag]], por ejemplo:

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

Conectarse a un único nodo:

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

Conectarse a un clúster con múltiples nodos:

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 en 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();
            // conversión de tipos
            String str = r.getValue(0).asString();
            LocalDate date = r.getValue(0).asDate();
        }
}

Consulte el ejemplo de código completo en el repositorio.

API de inserción

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` es la fuente de datos en formato RowBinary
        .executeAndWait()) {
            ClickHouseResponseSummary summary = response.getSummary();
            summary.getWrittenRows();
}

Consulte el ejemplo de código completo en el repositorio.

Codificación RowBinary

El formato RowBinary se describe en su página.

Hay un ejemplo de código.

Características

Compresión

De forma predeterminada, el cliente usará compresión LZ4, lo que requiere esta dependencia:

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

También puede optar por usar gzip configurando compress_algorithm=gzip en la URL de conexión.

También puede deshabilitar la compresión de varias maneras.

  1. Desactívalo estableciendo compress=0 en la URL de conexión: http://localhost:8123/default?compress=0
  2. Desactívelo en la configuración del cliente:
ClickHouseClient client = ClickHouseClient.builder()
   .config(new ClickHouseConfig(Map.of(ClickHouseClientOption.COMPRESS, false)))
   .nodeSelector(ClickHouseNodeSelector.of(ClickHouseProtocol.HTTP))
   .build();

Consulte la documentación de compresión para obtener más información sobre las distintas opciones de compresión.

Múltiples consultas

Ejecutar múltiples consultas en un hilo de trabajo una tras otra dentro de la misma sesión:

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 con nombre

Puede pasar parámetros por nombre en lugar de depender únicamente de su posición en la lista de parámetros. Esta capacidad está disponible mediante la función 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()) {
            //...
        }
}

Descubrimiento de nodos

El cliente Java permite detectar nodos de ClickHouse automáticamente. La detección automática está deshabilitada de forma predeterminada. Para habilitarla manualmente, establezca auto_discovery en true:

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

O en la URL de conexión:

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

Si el descubrimiento automático está habilitado, no es necesario especificar todos los nodos de ClickHouse en la URL de conexión. Los nodos especificados en la URL se tratarán como seeds, y el Java client descubrirá automáticamente más nodos a partir de las system tables y/o clickhouse-keeper o ZooKeeper.

Las siguientes opciones controlan la configuración de detección automática:

Propiedad Predeterminado Descripción
auto_discovery false Indica si el cliente debe descubrir más nodos a partir de las tablas del sistema y/o de clickhouse-keeper/zookeeper.
node_discovery_interval 0 Intervalo de descubrimiento de nodos en milisegundos; un valor cero o negativo significa descubrimiento de una sola vez.
node_discovery_limit 100 Número máximo de nodos que se pueden descubrir a la vez; un valor de cero o negativo significa que no hay límite.

Balanceo de carga

El cliente Java selecciona un nodo de ClickHouse al que enviar las solicitudes, de acuerdo con la política de balanceo de carga. En general, la política de balanceo de carga es responsable de lo siguiente:

  1. Obtén un nodo de una lista de nodos gestionados.
  2. Gestión del estado del nodo.
  3. De forma opcional, programe un proceso en segundo plano para el descubrimiento de nodos (si el descubrimiento automático está habilitado) y ejecute una comprobación de estado.

A continuación se muestra una lista de opciones para configurar el balanceo de carga:

Propiedad Valor predeterminado Descripción
load_balancing_policy "" La política de balanceo de carga puede ser una de las siguientes:
  • firstAlive - la solicitud se envía al primer nodo en buen estado de la lista de nodos administrados
  • random - la solicitud se envía a un nodo aleatorio de la lista de nodos administrados
  • roundRobin - la solicitud se envía a cada nodo de la lista de nodos administrados, por turnos.
  • nombre completo de la clase que implementa ClickHouseLoadBalancingPolicy - política de balanceo de carga personalizada
  • Si no se especifica, la solicitud se envía al primer nodo de la lista de nodos administrados
    load_balancing_tags "" Etiquetas de balanceo de carga para filtrar nodos. Las solicitudes se envían solo a los nodos que tienen las etiquetas especificadas
    health_check_interval 0 Intervalo de comprobación de estado en milisegundos; un valor cero o negativo significa una ejecución única.
    health_check_method ClickHouseHealthCheckMethod.SELECT_ONE Método de comprobación de estado. Puede ser uno de los siguientes:
  • ClickHouseHealthCheckMethod.SELECT_ONE - comprobación mediante la consulta select 1
  • ClickHouseHealthCheckMethod.PING - comprobación específica del protocolo, que suele ser más rápida
  • node_check_interval 0 Intervalo de comprobación de nodos en milisegundos; un número negativo se trata como cero. El estado del nodo se comprueba si ha transcurrido el tiempo especificado desde la última comprobación.
    La diferencia entre health_check_interval y node_check_interval es que la opción health_check_interval programa la tarea en segundo plano que comprueba el estado de la lista de nodos (todos o solo los que tienen fallos), mientras que node_check_interval especifica el tiempo transcurrido desde la última comprobación de un nodo concreto
    check_all_nodes false Si debe realizarse una comprobación de estado de todos los nodos o solo de los defectuosos.

    Failover y reintento

    El Java client ofrece opciones de configuración para definir el comportamiento de failover y retry ante queries fallidas:

    Propiedad Predeterminado Descripción
    failover 0 Número máximo de veces que puede producirse una conmutación por error en una solicitud. Un valor de cero o negativo significa que no hay conmutación por error. La conmutación por error envía la solicitud fallida a un nodo distinto (según la política de equilibrio de carga) para recuperarse del error.
    retry 0 Número máximo de veces que se puede reintentar una solicitud. Cero o un valor negativo significa que no hay reintentos. El reintento envía una solicitud al mismo nodo, y solo si el servidor de ClickHouse devuelve el código de error NETWORK_ERROR
    repeat_on_session_lock true Si se debe repetir la ejecución cuando la sesión esté bloqueada hasta que se agote el tiempo de espera (según session_timeout o connect_timeout). La solicitud fallida se repite si el servidor de ClickHouse devuelve el código de error SESSION_IS_LOCKED

    Agregar encabezados HTTP personalizados

    El cliente Java admite la capa de transporte HTTP/S para agregar encabezados HTTP personalizados a la solicitud. Se debe utilizar la propiedad custom_http_headers; los encabezados deben separarse con ,. El par clave/valor del encabezado debe dividirse con =

    Compatibilidad con Java Client

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

    JDBC Driver

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