Skip to content
ClickHouse Docs
ClickHouse DocsClickHouse Docs

Client Java

Bibliothèque client Java permettant de communiquer avec un serveur de base de données via ses protocoles. L'implémentation actuelle ne prend en charge que l'interface HTTP. La bibliothèque fournit sa propre API pour envoyer des requêtes à un serveur, ainsi que des outils pour travailler avec différents formats de données binaires (RowBinary* & Native*).

Configuration


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

Initialisation

L'objet Client est initialisé par com.clickhouse.client.api.Client.Builder#build(). Chaque client possède son propre contexte et aucun objet n'est partagé entre eux. Le Builder dispose de méthodes de configuration pour en simplifier la mise en œuvre.

Exemple :

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

Client est AutoCloseable et doit être fermé lorsqu'il n'est plus nécessaire.

Authentification

L'authentification est configurée par client lors de la phase d'initialisation. Trois méthodes d'authentification sont prises en charge : par mot de passe, par jeton d'accès ou par certificat client SSL.

L'authentification par mot de passe nécessite de renseigner le nom d'utilisateur et le mot de passe en appelant setUsername(String) et setPassword(String) :

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

L'authentification par jeton d'accès nécessite de configurer le jeton d'accès en appelant setAccessToken(String) :

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

L'authentification par certificat client SSL nécessite de définir le nom d'utilisateur, d'activer l'authentification SSL, de configurer un certificat client et une clé client en appelant respectivement setUsername(String), useSSLAuthentication(boolean), setClientCertificate(String) et setClientKey(String) :

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

Configuration

Tous les paramètres sont définis par des méthodes d'instance (aussi appelées méthodes de configuration) qui rendent explicites la portée et le contexte de chaque valeur. Les principaux paramètres de configuration sont définis dans une seule portée (client ou opération) et ne se substituent pas mutuellement.

La configuration est définie lors de la création du client. Voir com.clickhouse.client.api.Client.Builder.

Configuration du client

Méthode Arguments Description Par défaut Clé
addEndpoint(String endpoint) endpoint - adresse du serveur au format URL Ajoute un endpoint à la liste des serveurs disponibles. Actuellement, un seul endpoint est pris en charge. none none
addEndpoint(Protocol protocol, String host, int port, boolean secure) protocol - protocole de connexion
host - IP ou nom d'hôte
secure - utiliser HTTPS
Ajoute un endpoint à la liste des serveurs disponibles. Actuellement, un seul endpoint est pris en charge. none none
enableConnectionPool(boolean enable) enable - indicateur pour activer/désactiver Définit si un pool de connexions est activé true connection_pool_enabled
setMaxConnections(int maxConnections) maxConnections - nombre de connexions Définit le nombre de connexions qu'un client peut ouvrir vers chaque endpoint de serveur. 10 max_open_connections
setConnectionTTL(long timeout, ChronoUnit unit) timeout - valeur du délai d'expiration
unit - unité de temps
Définit le TTL de la connexion, au-delà duquel elle est considérée comme inactive -1 connection_ttl
setKeepAliveTimeout(long timeout, ChronoUnit unit) timeout - valeur du délai d'expiration
unit - unité de temps
Définit le délai d'expiration Keep-Alive de la connexion HTTP. Définissez 0 pour désactiver Keep-Alive. - http_keep_alive_timeout
setConnectionReuseStrategy(ConnectionReuseStrategy strategy) strategy - LIFO ou FIFO Sélectionne la stratégie à utiliser pour le pool de connexions FIFO connection_reuse_strategy
setDefaultDatabase(String database) database - nom d'une base de données Définit la base de données par défaut. default database

Identification du client

Le journal de requêtes contient deux champs permettant d'identifier l'application à l'origine d'une requête : client_name et http_user_agent. Le protocole TCP natif utilise client_name pour identifier l'application. Le protocole HTTP utilise http_user_agent pour identifier l'application. Le builder client dispose de la méthode setClientName pour définir les valeurs correctes pour les deux protocoles. Le champ http_user_agent est défini selon le format standard de l'en-tête User-Agent : application-name[/version] [(operating-system; architecture; ...)]. Cet ensemble de valeurs est répété pour chaque couche : application, bibliothèque client, bibliothèque client HTTP. Ce qui est défini par la méthode setClientName apparaît en premier dans la liste.

Par exemple :

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

donnera la valeur http_user_agent suivante :

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

L'application peut définir son propre en-tête HTTP User-Agent pour s'identifier. Toutefois, la partie clickhouse-java-v2/0.9.6-SNAPSHOT sera ajoutée à la fin de l'en-tête.

Identification des opérations

Le journal de requêtes dispose de deux autres champs query_id et log_comment qui peuvent être utilisés pour identifier une opération et ajouter des informations supplémentaires au journal de requêtes.

query_id est un identifiant unique d'une opération. Il peut être défini par l'application en appelant la méthode setQueryId de la classe QuerySettings.

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

log_comment est un commentaire pouvant être ajouté au journal des requêtes. Il peut être défini par l'application en appelant la méthode logComment de la classe QuerySettings.

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

Paramètres du serveur

Les paramètres côté serveur peuvent être définis au niveau du client une seule fois lors de sa création (voir la méthode serverSetting du Builder) et au niveau de l'opération (voir serverSetting pour la classe de paramètres d'opération).

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

	...
}

⚠️ Lorsque des options sont définies via la méthode setOption (que ce soit via Client.Builder ou la classe de paramètres d'opération), le nom des paramètres serveur doit être préfixé de clickhouse_setting_. La méthode com.clickhouse.client.api.ClientConfigProperties#serverSetting() peut s'avérer utile dans ce cas.

En-tête HTTP personnalisé

Des en-têtes HTTP personnalisés peuvent être définis pour toutes les opérations (au niveau du client) ou pour une seule (au niveau de l'opération).


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

Lorsque les options sont définies via la méthode setOption (que ce soit via Client.Builder ou la classe de paramètres d'opération), le nom du header personnalisé doit être préfixé de http_header_. La méthode com.clickhouse.client.api.ClientConfigProperties#httpHeader() peut s'avérer utile dans ce cas.

Définitions courantes

ClickHouseFormat

Enum des formats pris en charge. Il inclut tous les formats supportés par ClickHouse.

  • raw - l’utilisateur doit transcoder les données brutes
  • full - le client peut transcoder lui-même les données et accepte un flux de données brutes
  • - - opération non prise en charge par ClickHouse pour ce format

Cette version du client prend en charge :

Format Entrée Sortie
TabSeparated brut brut
TabSeparatedRaw brut brut
TabSeparatedWithNames brut brut
TabSeparatedWithNamesAndTypes brut brut
TabSeparatedRawWithNames brut brut
TabSeparatedRawWithNamesAndTypes brut brut
Template brut brut
TemplateIgnoreSpaces brut -
CSV brut brut
CSVWithNames brut brut
CSVWithNamesAndTypes brut brut
CustomSeparated brut brut
CustomSeparatedWithNames brut brut
CustomSeparatedWithNamesAndTypes brut brut
SQLInsert - brut
Values brut brut
Vertical - brut
JSON brut brut
JSONAsString brut -
JSONAsObject brut -
JSONStrings brut brut
JSONColumns brut brut
JSONColumnsWithMetadata brut brut
JSONCompact brut brut
JSONCompactStrings - brut
JSONCompactColumns brut brut
JSONEachRow brut brut
PrettyJSONEachRow - brut
JSONEachRowWithProgress - brut
JSONStringsEachRow brut brut
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 d'insertion

insert(String tableName, InputStream data, ClickHouseFormat format)

Accepte des données sous forme d'InputStream d'octets dans le format spécifié. data doit être encodé dans le format.

Signatures

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

Paramètres

tableName - un nom de table cible.

data - un flux d'entrée de données encodées.

format - le format dans lequel les données sont encodées.

settings - paramètres de la requête.

Valeur de retour

Future du type InsertResponse — résultat de l'opération et informations supplémentaires telles que les métriques côté serveur.

Exemples

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)

Envoie une requête d'écriture à la base de données. La liste d'objets est convertie dans un format efficace, puis envoyée au serveur. La classe des éléments de la liste doit être enregistrée au préalable à l'aide de la méthode register(Class, TableSchema).

Signatures

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

Paramètres

tableName - nom de la table cible.

data - objets DTO (Data Transfer Object) de la collection.

settings - paramètres de la requête.

Valeur de retour

Future du type InsertResponse — le résultat de l'opération et des informations supplémentaires telles que les métriques côté serveur.

Exemples

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

Options de configuration pour les opérations d'insertion.

Méthodes de configuration

Méthode Description
setQueryId(String queryId) Définit l’ID de la requête qui sera attribué à l’opération. Par défaut : null.
setDeduplicationToken(String token) Définit le jeton de déduplication. Ce jeton est envoyé au serveur et peut être utilisé pour identifier la requête. Par défaut : null.
setInputStreamCopyBufferSize(int size) Taille du tampon de copie. Le tampon est utilisé lors des opérations d’écriture pour copier les données d’un flux d’entrée fourni par l’utilisateur vers un flux de sortie. Par défaut : 8196.
serverSetting(String name, String value) Définit individuellement les paramètres du serveur pour une opération.
serverSetting(String name, Collection values) Définit des paramètres serveur individuels avec plusieurs valeurs pour une opération. Les éléments de la collection doivent être des valeurs String.
setDBRoles(Collection dbRoles) Définit les rôles DB à définir avant l’exécution d’une opération. Les éléments de la collection doivent être des valeurs String.
setOption(String option, Object value) Définit une option de configuration au format brut. Il ne s'agit pas d'un paramètre du serveur.

InsertResponse

Objet de réponse contenant le résultat de l'opération d'insertion. Il n'est disponible que si le client a reçu une réponse du serveur.

Méthode Description
OperationMetrics getMetrics() Renvoie un objet contenant les métriques de l’opération.
String getQueryId() Renvoie l’ID de requête attribué à l’opération par l’application (via les paramètres de l’opération) ou par le serveur.

API de requête

query(String sqlQuery)

Envoie sqlQuery tel quel. Le format de réponse est défini par les paramètres de la requête. QueryResponse contiendra une référence au flux de réponse qui doit être consommé par un lecteur pour le format pris en charge.

Signatures

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

Paramètres

sqlQuery - une instruction SQL unique. La requête est envoyée telle quelle au serveur.

settings - paramètres de la requête.

Valeur de retour

Future du type QueryResponse — un jeu de données résultant ainsi que des informations supplémentaires telles que les métriques côté serveur. L'objet Response doit être fermé après consommation du jeu de données.

Exemples

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)

Envoie sqlQuery tel quel. Envoie également les paramètres de requête afin que le serveur puisse compiler l'expression SQL.

Signatures

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

Paramètres

sqlQuery - expression SQL avec des espaces réservés {}.

queryParams - map de variables permettant de compléter l'expression SQL sur le serveur.

settings - paramètres de la requête.

Valeur de retour

Future du type QueryResponse — un jeu de données résultant ainsi que des informations supplémentaires telles que les métriques côté serveur. L'objet Response doit être fermé après consommation du jeu de données.

Exemples


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

Interroge des données au format RowBinaryWithNamesAndTypes. Renvoie le résultat sous forme de collection. Les performances de lecture sont identiques à celles du lecteur, mais davantage de mémoire est nécessaire pour conserver l'intégralité du jeu de données.

Signatures

List<GenericRecord> queryAll(String sqlQuery)

Paramètres

sqlQuery - expression SQL pour interroger des données depuis un serveur.

Valeur de retour

Jeu de données complet représenté par une liste d'objets GenericRecord offrant un accès ligne par ligne aux données de résultat.

Exemples

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

Options de configuration pour les opérations de requête.

Méthodes de configuration

Méthode Description
setQueryId(String queryId) Définit l’ID de requête qui sera attribué à l’opération.
setFormat(ClickHouseFormat format) Définit le format de la réponse. Voir RowBinaryWithNamesAndTypes pour la liste complète.
setMaxExecutionTime(Integer maxExecutionTime) Définit le temps d’exécution de l’opération sur le serveur. N’affecte pas le délai d’expiration de lecture.
waitEndOfQuery(Boolean waitEndOfQuery) Demande au serveur d’attendre la fin de la requête avant d’envoyer une réponse.
setUseServerTimeZone(Boolean useServerTimeZone) Le fuseau horaire du serveur (voir la config du client) sera utilisé pour interpréter les types date/heure dans le résultat d'une opération. Par défaut : false.
setUseTimeZone(String timeZone) Demande au serveur d’utiliser timeZone pour la conversion de l’heure. Voir session_timezone.
serverSetting(String name, String value) Définit des paramètres serveur spécifiques pour une opération.
serverSetting(String name, Collection values) Définit des paramètres serveur individuels avec plusieurs valeurs pour une opération. Les éléments de la collection doivent être des valeurs String.
setDBRoles(Collection dbRoles) Définit les rôles DB à appliquer avant l’exécution d’une opération. Les éléments de la collection doivent être des valeurs String.
setOption(String option, Object value) Définit une option de configuration au format brut. Il ne s’agit pas d’un paramètre du serveur.

QueryResponse

Objet de réponse contenant le résultat de l'exécution de la requête. Il n'est disponible que si le client a reçu une réponse du serveur.

Méthode Description
ClickHouseFormat getFormat() Renvoie un format dans lequel les données de la réponse sont encodées.
InputStream getInputStream() Renvoie un flux d’octets non compressé de données au format spécifié.
OperationMetrics getMetrics() Renvoie un objet contenant les métriques de l’opération.
String getQueryId() Renvoie l’ID de requête attribué à l’opération par l’application (via les paramètres de l’opération ou par le serveur).
TimeZone getTimeZone() Renvoie le fuseau horaire à utiliser pour traiter les types Date/DateTime dans la réponse.

Exemples

API commune

getTableSchema(String table)

Récupère le schéma de la table table.

Signatures

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

Paramètres

table - nom de la table pour laquelle les données de schéma doivent être récupérées.

database - base de données dans laquelle la table cible est définie.

Valeur de retour

Renvoie un objet TableSchema contenant la liste des colonnes de la table.

getTableSchemaFromQuery(String sql)

Récupère le schéma à partir d'une instruction SQL.

Signatures

TableSchema getTableSchemaFromQuery(String sql)

Paramètres

sql - Instruction SQL "SELECT" dont le schéma doit être renvoyé.

Valeur de retour

Renvoie un objet TableSchema dont les colonnes correspondent à l'expression sql.

TableSchema

register(Class<?> clazz, TableSchema schema)

Compile la couche de sérialisation et de désérialisation pour la classe Java à utiliser pour l'écriture et la lecture de données avec schema. La méthode crée un sérialiseur et un désérialiseur pour la paire getter/setter et la colonne correspondante. La correspondance de colonne est déterminée en extrayant son nom à partir du nom de la méthode. Par exemple, getFirstName correspondra à la colonne first_name ou firstname.

Signatures

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

Paramètres

clazz - Classe représentant le POJO utilisé pour lire/écrire des données.

schema - Schéma de données à utiliser pour la mise en correspondance avec les propriétés POJO.

Exemples

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

Exemples d'utilisation

Le code des exemples complets est stocké dans un dossier 'example` du dépôt :

  • client-v2 - principal ensemble d’exemples.
  • demo-service - exemple d’utilisation du client dans une application Spring Boot.
  • demo-kotlin-service - exemple d’utilisation du client dans une application Ktor (Kotlin).

Lecture des données

Il existe deux façons courantes de lire les données :

  • Méthode query() qui renvoie un objet QueryResponse de bas niveau contenant un InputStream avec les données. Généralement combinée à ClickHouseBinaryFormatReader pour les lectures en streaming, elle peut aussi être utilisée avec toute autre implémentation personnalisée de lecteur. QueryResponse donne également accès aux métadonnées du jeu de résultats et aux métriques.
  • Méthode queryAll() avec utilisation de GenericRecord pour un accès pratique aux lignes. Dans ce cas, l’intégralité du jeu de résultats est chargée en mémoire.
  • Méthode queryRecords() qui renvoie com.clickhouse.client.api.query.Records - un itérateur pour les objets GenericRecord. Cette méthode repose sur une approche en streaming (aucune donnée n’est chargée en mémoire) et utilise GenericRecord pour accéder aux données.

Note : l'approche en streaming nécessite une lecture rapide, faute de quoi elle peut provoquer un timeout d'écriture sur le serveur, car les données sont lues directement depuis le flux réseau.

Lecture des tableaux

Méthodes de ClickHouseBinaryFormatReader

  • getList(...) - lit n’importe quel Array(...) comme List<T>. Bon choix par défaut pour une lecture typée flexible. Prend en charge les tableaux imbriqués.
  • getByteArray(...), getShortArray(...), getIntArray(...), getLongArray(...), getFloatArray(...), getDoubleArray(...), getBooleanArray(...) - idéal pour les tableaux 1D de valeurs compatibles avec les types primitifs.
  • getStringArray(...) - pour Array(String) (et les valeurs d’enum représentées par leur nom).
  • getObjectArray(...) - option générique pour tout type d’élément de Array(...), y compris les tableaux imbriqués. À utiliser pour lire des tableaux contenant des valeurs Nullable et des tableaux imbriqués.

Des surcharges par index et par nom sont disponibles pour toutes les méthodes. L'index est indexé à partir de 1. Les surcharges par index accèdent directement à une colonne. Les méthodes par nom nécessitent une recherche via l'index à chaque appel.

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éthodes de GenericRecord

  • getList(...) - lit n'importe quel Array(...) en tant que List<T>. Bon choix par défaut pour une lecture à typage flexible. Prend en charge les tableaux imbriqués.
  • getByteArray(...), getShortArray(...), getIntArray(...), getLongArray(...), getFloatArray(...), getDoubleArray(...), getBooleanArray(...) - idéal pour les tableaux 1D de valeurs compatibles avec les types primitifs.
  • getStringArray(...) - pour Array(String) (et les valeurs d'enum représentées par leur nom).
  • getObjectArray(...) - option générique pour tout type d'élément de Array(...), y compris les tableaux imbriqués. À utiliser pour lire des tableaux contenant des valeurs NULL et des tableaux imbriqués.

Des surcharges par index et par nom sont disponibles pour toutes les méthodes. L'index est indexé à partir de 1. Les surcharges par index accèdent directement à une colonne. Les méthodes par nom nécessitent une recherche via l'index à chaque appel.

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

Guide de migration

L'ancien client (V1) utilisait com.clickhouse.client.ClickHouseClient#builder comme point de départ. Le nouveau client (V2) suit un pattern similaire avec com.clickhouse.client.api.Client.Builder. Les principales différences sont :

  • aucun service loader n'est utilisé pour charger l'implémentation. Le com.clickhouse.client.api.Client sert de classe façade pour toutes sortes d'implémentations à l'avenir.
  • moins de sources de configuration : l'une est fournie au builder et l'autre via les paramètres d'opération (QuerySettings, InsertSettings). La version précédente avait une configuration par nœud et chargeait les variables d'environnement dans certains cas.

Correspondance des paramètres de configuration

Il existe 3 classes enum liées à la configuration dans V1 :

  • com.clickhouse.client.config.ClickHouseDefaults - paramètres de configuration à définir dans la plupart des cas d'utilisation, comme USER et PASSWORD.
  • com.clickhouse.client.config.ClickHouseClientOption - paramètres de configuration spécifiques au client, comme HEALTH_CHECK_INTERVAL.
  • com.clickhouse.client.http.config.ClickHouseHttpOption - paramètres de configuration spécifiques à l'interface HTTP, comme RECEIVE_QUERY_PROGRESS.

Ils ont été conçus pour regrouper les paramètres et assurer une séparation claire. Cependant, dans certains cas, cela a pu prêter à confusion (y a-t-il une différence entre com.clickhouse.client.config.ClickHouseDefaults#ASYNC et com.clickhouse.client.config.ClickHouseClientOption#ASYNC ?). Le nouveau client V2 utilise com.clickhouse.client.api.Client.Builder comme dictionnaire unique de toutes les options de configuration possibles du client. La classe com.clickhouse.client.api.ClientConfigProperties recense tous les noms de paramètres de configuration.

Le tableau ci-dessous indique quelles options de l'ancienne version sont prises en charge par le nouveau client et leur nouvelle signification.

Légende : ✔ = pris en charge, ✗ = supprimé

Configuration V1 Méthode Builder V2 Commentaires
ClickHouseDefaults#HOST Client.Builder#addEndpoint
ClickHouseDefaults#PROTOCOL Seul HTTP est pris en charge avec 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

Différences générales

  • Client V2 utilise moins de classes propriétaires afin d’améliorer la portabilité. Par exemple, V2 fonctionne avec n’importe quelle implémentation de java.io.InputStream pour envoyer des données à un serveur.
  • Le paramètre async de Client V2 est off par défaut. Cela signifie qu’il n’y a pas de thread supplémentaire et que l’application garde davantage de contrôle sur le client. Ce paramètre doit être off dans la majorité des cas d’utilisation. L’activation de async crée un thread distinct pour chaque requête. Cela n’a de sens que si vous utilisez un executor contrôlé par l’application (voir com.clickhouse.client.api.Client.Builder#setSharedOperationExecutor)

Écriture des données

  • utilisez n’importe quelle implémentation de java.io.InputStream. La V1 com.clickhouse.data.ClickHouseInputStream est prise en charge, mais n’est PAS recommandée.
  • une fois la fin du flux d’entrée détectée, elle est traitée comme telle. Auparavant, il fallait fermer le flux de sortie d’une requête.

V1 Insérer des données au format 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 Insertion de données au format 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
}
  • il n’y a qu’une seule méthode à appeler. Il n’est pas nécessaire de créer un objet de corps de requête supplémentaire.
  • le flux du corps de la requête est fermé automatiquement une fois toutes les données copiées.
  • une nouvelle API de bas niveau est 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 conçu pour implémenter une logique d’écriture de données personnalisée. Par exemple, pour lire des données depuis une file d’attente.

Lecture des données

  • Les données sont lues au format RowBinaryWithNamesAndTypes par défaut. À l'heure actuelle, seul ce format est pris en charge lorsqu'une liaison de données est nécessaire.
  • Les données peuvent être lues sous forme de collection d'enregistrements à l'aide de la méthode List<GenericRecord> com.clickhouse.client.api.Client#queryAll(java.lang.String). Cette méthode lit les données en mémoire puis libère la connexion. Aucune gestion supplémentaire n'est nécessaire. GenericRecord donne accès aux données et prend en charge certaines conversions.
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");
}

Bibliothèque client Java permettant de communiquer avec un serveur de base de données via ses protocoles. L'implémentation actuelle ne prend en charge que l'interface HTTP. La bibliothèque fournit sa propre API pour envoyer des requêtes à un serveur.

Configuration

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

Depuis la version 0.5.0, le driver utilise une nouvelle bibliothèque HTTP cliente qui doit être ajoutée en tant que dépendance.

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

Initialisation

Format de l'URL de connexion : protocol://host[:port][/database][?param[=value][&param[=value]][#tag[,tag]], par exemple :

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

Se connecter à un nœud unique :

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

Se connecter à un cluster avec plusieurs nœuds :

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 requête

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 requête 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();
            // type conversion
            String str = r.getValue(0).asString();
            LocalDate date = r.getValue(0).asDate();
        }
}

Consultez l'exemple de code complet dans le dépôt.

API d'insertion


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

Consultez l'exemple de code complet dans le dépôt.

Encodage RowBinary

Le format RowBinary est décrit sur sa page.

Voici un exemple de code.

Fonctionnalités

Compression

Le client utilise par défaut la compression LZ4, ce qui nécessite la dépendance suivante :

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

Vous pouvez également utiliser gzip en spécifiant compress_algorithm=gzip dans l'URL de connexion.

Vous pouvez également désactiver la compression de plusieurs façons.

  1. Désactivez en définissant compress=0 dans l’URL de connexion : http://localhost:8123/default?compress=0
  2. Désactivez via la configuration du client :
ClickHouseClient client = ClickHouseClient.builder()
   .config(new ClickHouseConfig(Map.of(ClickHouseClientOption.COMPRESS, false)))
   .nodeSelector(ClickHouseNodeSelector.of(ClickHouseProtocol.HTTP))
   .build();

Consultez la documentation sur la compression pour en savoir plus sur les différentes options de compression.

Requêtes multiples

Exécuter plusieurs requêtes dans un thread de travail, les unes après les autres, au sein de la même session :

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

Paramètres nommés

Vous pouvez passer des paramètres par nom plutôt que de vous fier uniquement à leur position dans la liste de paramètres. Cette fonctionnalité est disponible via la fonction 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()) {
            //...
        }
}

Découverte des nœuds

Le client Java permet de détecter automatiquement les nœuds ClickHouse. La détection automatique est désactivée par défaut. Pour l'activer manuellement, définissez auto_discovery sur true :

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

Ou dans l'URL de connexion :

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

Si la découverte automatique est activée, il n'est pas nécessaire de spécifier tous les nœuds ClickHouse dans l'URL de connexion. Les nœuds indiqués dans l'URL seront traités comme des seeds, et le client Java découvrira automatiquement d'autres nœuds à partir des tables système et/ou de ClickHouse Keeper ou ZooKeeper.

Les options suivantes permettent de configurer la découverte automatique :

Propriété Par défaut Description
auto_discovery false Indique si le client doit détecter davantage de nœuds à partir des tables système et/ou de ClickHouse Keeper/ZooKeeper.
node_discovery_interval 0 Intervalle de détection des nœuds en millisecondes; une valeur nulle ou négative signifie une détection unique.
node_discovery_limit 100 Nombre maximal de nœuds pouvant être découverts à la fois ; une valeur nulle ou négative signifie qu’il n’y a pas de limite.

Équilibrage de charge

Le Java client choisit un nœud ClickHouse auquel envoyer des requêtes, conformément à la politique d'équilibrage de charge. En général, la politique d'équilibrage de charge est responsable des éléments suivants :

  1. Récupérez un nœud dans une liste de nœuds gérés.
  2. Gérer l’état du nœud.
  3. Planifiez éventuellement un processus en arrière-plan pour la découverte de nœuds (si l’auto-découverte est activée) et exécutez un contrôle d’intégrité.

Voici une liste d'options pour configurer l'équilibrage de charge :

Propriété Valeur par défaut Description
load_balancing_policy "" La politique d’équilibrage de charge peut être l’une des suivantes :
  • firstAlive - la requête est envoyée au premier nœud sain de la liste des nœuds gérés
  • random - la requête est envoyée à un nœud choisi aléatoirement dans la liste des nœuds gérés
  • roundRobin - la requête est envoyée à chaque nœud de la liste des nœuds gérés, à tour de rôle.
  • nom de classe pleinement qualifié implémentant ClickHouseLoadBalancingPolicy - politique d’équilibrage de charge personnalisée
  • Si elle n’est pas spécifiée, la requête est envoyée au premier nœud de la liste des nœuds gérés
    load_balancing_tags "" Tags d’équilibrage de charge permettant de filtrer les nœuds. Les requêtes sont envoyées uniquement aux nœuds portant les tags spécifiés
    health_check_interval 0 Intervalle de vérification de l’état en millisecondes ; une valeur nulle ou négative indique une exécution unique.
    health_check_method ClickHouseHealthCheckMethod.SELECT_ONE Méthode de vérification de l’état de santé. Peut être l’une des suivantes :
  • ClickHouseHealthCheckMethod.SELECT_ONE - vérification via la requête select 1
  • ClickHouseHealthCheckMethod.PING - vérification spécifique au protocole, généralement plus rapide
  • node_check_interval 0 Intervalle de vérification des nœuds en millisecondes ; un nombre négatif est traité comme zéro. L’état du nœud est vérifié si le délai spécifié s’est écoulé depuis la dernière vérification.
    La différence entre health_check_interval et node_check_interval est que l’option health_check_interval planifie la tâche d’arrière-plan qui vérifie l’état de la liste des nœuds (tous ou uniquement les nœuds défaillants), tandis que node_check_interval spécifie le délai écoulé depuis la dernière vérification de ce nœud particulier
    check_all_nodes false Indique s’il faut effectuer une vérification d’état sur tous les nœuds ou uniquement sur les nœuds défaillants.

    Basculement et nouvelle tentative

    Le Java client propose des options de configuration pour paramétrer le comportement de failover et de retry pour les requêtes en échec :

    Propriété Par défaut Description
    failover 0 Nombre maximal de fois où un basculement peut se produire pour une requête. Zéro ou une valeur négative signifie qu’il n’y a pas de basculement. En cas d’échec, le basculement envoie la requête vers un autre nœud (conformément à la politique d’équilibrage de charge).
    retry 0 Nombre maximal de tentatives pour une requête. Zéro ou une valeur négative signifie qu'il n'y a aucune tentative. Une tentative renvoie la requête au même nœud, uniquement si le ClickHouse server renvoie le code d'erreur NETWORK_ERROR
    repeat_on_session_lock true Indique s'il faut relancer l'exécution lorsque la session est verrouillée, jusqu'à expiration du délai (selon session_timeout ou connect_timeout). La requête ayant échoué est relancée si le ClickHouse server renvoie le code d'erreur SESSION_IS_LOCKED

    Ajout d'en-têtes HTTP personnalisés

    Le Java client prend en charge la couche de transport HTTP/S pour l'ajout de HTTP headers personnalisés à la requête. Il convient d'utiliser la propriété custom_http_headers ; les headers doivent être séparés par ,. La clé et la valeur du header doivent être séparées par =.

    Prise en charge du 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