Skip to content
ClickHouse Docs
ClickHouse DocsClickHouse Docs

Java-клиент

Клиентская библиотека Java для взаимодействия с сервером базы данных через его протоколы. Текущая реализация поддерживает только HTTP-интерфейс. Библиотека предоставляет собственный API для отправки запросов на сервер. Также библиотека содержит инструменты для работы с различными бинарными форматами данных (RowBinary* & Native*).

Setup


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

Инициализация

Объект Client инициализируется с помощью com.clickhouse.client.api.Client.Builder#build(). Каждый клиент имеет собственный контекст, и между клиентами не разделяются объекты. Builder предоставляет методы конфигурации для удобной настройки.

Пример:

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

Client реализует AutoCloseable и должен быть закрыт, когда больше не нужен.

Аутентификация

Аутентификация настраивается для каждого клиента на этапе инициализации. Поддерживаются три метода аутентификации: по паролю, по токену доступа, по SSL-сертификату клиента.

Аутентификация по паролю требует указания имени пользователя и пароля через вызовы setUsername(String) и setPassword(String):

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

Для аутентификации с помощью токена доступа необходимо задать токен доступа, вызвав setAccessToken(String):

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

Аутентификация с помощью SSL-сертификата клиента требует указания имени пользователя, включения SSL-аутентификации, а также задания клиентского сертификата и клиентского ключа посредством вызовов setUsername(String), useSSLAuthentication(boolean), setClientCertificate(String) и setClientKey(String) соответственно:

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

Конфигурация

Все настройки определяются методами экземпляра (они же методы конфигурации), которые чётко задают область видимости и контекст каждого значения. Основные параметры конфигурации определяются в одной области видимости (клиента или операции) и не переопределяют друг друга.

Конфигурация задаётся при создании клиента. См. com.clickhouse.client.api.Client.Builder.

Конфигурация клиента

Метод Аргументы Описание По умолчанию Ключ
addEndpoint(String endpoint) endpoint - адрес сервера в формате URL Добавляет конечную точку сервера в список доступных серверов. Сейчас поддерживается только одна конечная точка. none none
addEndpoint(Protocol protocol, String host, int port, boolean secure) protocol - протокол соединения
host - IP-адрес или имя хоста
secure - использовать HTTPS
Добавляет конечную точку сервера в список доступных серверов. Сейчас поддерживается только одна конечная точка. none none
enableConnectionPool(boolean enable) enable - флаг включения/отключения Определяет, включен ли пул соединений true connection_pool_enabled
setMaxConnections(int maxConnections) maxConnections - количество соединений Задает, сколько соединений клиент может открыть для каждой конечной точки сервера. 10 max_open_connections
setConnectionTTL(long timeout, ChronoUnit unit) timeout - значение тайм-аута
unit - единица времени
Задает TTL соединения, по истечении которого оно считается неактивным -1 connection_ttl
setKeepAliveTimeout(long timeout, ChronoUnit unit) timeout - значение тайм-аута
unit - единица времени
Задает тайм-аут Keep-Alive для HTTP-соединения. Установите 0, чтобы отключить Keep-Alive. - http_keep_alive_timeout
setConnectionReuseStrategy(ConnectionReuseStrategy strategy) strategy - LIFO или FIFO Выбирает стратегию, которую должен использовать пул соединений FIFO connection_reuse_strategy
setDefaultDatabase(String database) database - имя базы данных Задает базу данных по умолчанию. default database

Идентификация клиента

В журнале запросов есть два поля, по которым можно определить приложение, из которого был отправлен запрос: client_name и http_user_agent. Нативный TCP-протокол использует client_name для идентификации приложения. HTTP-протокол использует http_user_agent для идентификации приложения. В билдере клиента есть метод setClientName, который задаёт корректные значения для обоих протоколов. Поле http_user_agent задаётся в соответствии с общепринятым форматом заголовка User-Agent: application-name[/version] [(operating-system; architecture; ...)]. Этот набор значений повторяется для каждого уровня: приложение, клиентская библиотека, HTTP-клиентская библиотека. Значение, заданное методом setClientName, идёт первым в списке.

Например:

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

приведёт к следующему значению http_user_agent:

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

Приложение может задать собственный HTTP-заголовок User-Agent для идентификации. При этом строка clickhouse-java-v2/0.9.6-SNAPSHOT будет добавлена в конец заголовка.

Идентификация операции

Log запросов содержит ещё два поля — query_id и log_comment, — которые можно использовать для идентификации операции и добавления дополнительных сведений в log запросов.

query_id — уникальный идентификатор операции. Его можно задать в приложении, вызвав метод setQueryId класса QuerySettings.

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

log_comment — это комментарий, который можно добавить в лог запросов. Его можно задать в приложении, вызвав метод logComment класса QuerySettings.

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

Настройки сервера

Настройки на стороне сервера можно задать на уровне клиента один раз при создании (см. метод serverSetting класса Builder) и на уровне операции (см. serverSetting для класса настроек операции).

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

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

        .build()) {

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

	...
}

⚠️ Если параметры задаются через метод setOption (будь то Client.Builder или класс настроек операции), имя настройки сервера должно иметь префикс clickhouse_setting_. В этом случае может пригодиться com.clickhouse.client.api.ClientConfigProperties#serverSetting().

Пользовательский HTTP-заголовок

Пользовательские HTTP-заголовки можно задать для всех операций (на уровне клиента) или для отдельной операции (на уровне операции).


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

Если параметры задаются через метод setOption (будь то Client.Builder или класс настроек операции), имя пользовательского заголовка должно начинаться с префикса http_header_. В этом случае может пригодиться метод com.clickhouse.client.api.ClientConfigProperties#httpHeader().

Общие определения

ClickHouseFormat

Перечисление поддерживаемых форматов. Включает все форматы, поддерживаемые ClickHouse.

  • raw - пользователь должен перекодировать необработанные данные
  • full - клиент может самостоятельно перекодировать данные и принимает поток необработанных данных
  • - - операция не поддерживается ClickHouse для этого формата

Эта версия клиента поддерживает:

Формат Ввод Вывод
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 вставки

insert(String tableName, InputStream data, ClickHouseFormat format)

Принимает данные в виде InputStream байтов в указанном формате. Предполагается, что data закодированы в формате format.

Сигнатуры

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

Параметры

tableName — имя целевой таблицы.

data — входной поток закодированных данных.

format — формат, в котором закодированы данные.

settings - параметры запроса.

Возвращаемое значение

Future типа InsertResponse — результат операции и дополнительная информация, например метрики на стороне сервера.

Примеры

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

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

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

Отправляет запрос на запись в базу данных. Список объектов преобразуется в эффективный формат и затем отправляется на сервер. Класс элементов списка должен быть заранее зарегистрирован с помощью метода register(Class, TableSchema).

Сигнатуры

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

Параметры

tableName — имя целевой таблицы.

data — коллекция объектов DTO (Data Transfer Object).

settings - параметры запроса.

Возвращаемое значение

Future типа InsertResponse — результат операции и дополнительная информация, например метрики на стороне сервера.

Примеры

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

List<ArticleViewEvent> events = loadBatch();

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

InsertSettings

Параметры для операций вставки.

Методы конфигурации

Метод Описание
setQueryId(String queryId) Устанавливает идентификатор запроса, который будет присвоен операции. По умолчанию: null.
setDeduplicationToken(String token) Устанавливает токен дедупликации. Этот токен будет отправлен на сервер и может использоваться для идентификации запроса. По умолчанию: null.
setInputStreamCopyBufferSize(int size) Размер буфера копирования. Буфер используется при операциях записи для копирования данных из входного потока, заданного пользователем, в выходной поток. По умолчанию: 8196.
serverSetting(String name, String value) Устанавливает отдельные настройки сервера для операции.
serverSetting(String name, Collection values) Устанавливает отдельные настройки сервера с несколькими значениями для операции. Элементы коллекции должны иметь тип String.
setDBRoles(Collection dbRoles) Задает роли БД, которые будут установлены перед выполнением операции. Элементы коллекции должны быть строковыми значениями String.
setOption(String option, Object value) Задаёт параметр конфигурации в исходном формате. Это не настройка сервера.

InsertResponse

Объект ответа, содержащий результат операции вставки. Доступен только в том случае, если клиент получил ответ от сервера.

Метод Описание
OperationMetrics getMetrics() Возвращает объект с метриками операции.
String getQueryId() Возвращает Query id, назначенный операции приложением (через настройки операции) или сервером.

API запросов

query(String sqlQuery)

Отправляет sqlQuery как есть. Формат ответа задаётся настройками запроса. QueryResponse будет содержать ссылку на поток ответа, который должен быть прочитан (consumed) ридер-ом для поддерживаемого формата.

Сигнатуры

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

Параметры

sqlQuery — один SQL-оператор. Запрос отправляется на сервер как есть.

settings - параметры запроса.

Возвращаемое значение

Future типа QueryResponse — результирующий набор данных и дополнительная информация, например метрики на стороне сервера. Объект Response следует закрыть после обработки набора данных.

Примеры

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

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

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

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

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

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

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

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

Отправляет sqlQuery без изменений. Также отправляет параметры запроса, чтобы сервер мог скомпилировать SQL-выражение.

Сигнатуры

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

Параметры

sqlQuery — SQL-выражение с плейсхолдерами {}.

queryParams — набор переменных для подстановки в SQL-выражение на сервере.

settings - параметры запроса.

Возвращаемое значение

Future типа QueryResponse — результирующий набор данных и дополнительная информация, например метрики на стороне сервера. Объект Response следует закрыть после обработки набора данных.

Примеры


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

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

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

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

        // reading data
    }

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

queryAll(String sqlQuery)

Запрашивает данные в формате RowBinaryWithNamesAndTypes. Возвращает результат в виде коллекции. Производительность чтения аналогична reader, однако для хранения всего набора данных требуется больше памяти.

Подписи

List<GenericRecord> queryAll(String sqlQuery)

Параметры

sqlQuery — SQL-выражение для запроса данных на сервере.

Возвращаемое значение

Полный набор данных, представленный в виде списка объектов GenericRecord, обеспечивающих построчный доступ к результирующим данным.

Примеры

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

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

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

QuerySettings

Параметры конфигурации для операций с запросами.

Методы конфигурации

Метод Описание
setQueryId(String queryId) Устанавливает Query id, который будет присвоен операции.
setFormat(ClickHouseFormat format) Задает формат ответа. Полный список см. в RowBinaryWithNamesAndTypes.
setMaxExecutionTime(Integer maxExecutionTime) Устанавливает время выполнения операции на сервере. Не влияет на тайм-аут чтения.
waitEndOfQuery(Boolean waitEndOfQuery) Указывает серверу дождаться завершения запроса перед отправкой ответа.
setUseServerTimeZone(Boolean useServerTimeZone) Часовой пояс сервера (см. конфигурацию клиента) будет использоваться для разбора типов date/time в результате операции. По умолчанию — false.
setUseTimeZone(String timeZone) Просит сервер использовать timeZone для преобразования времени. См. session_timezone.
serverSetting(String name, String value) Устанавливает отдельные настройки сервера для операции.
serverSetting(String name, Collection values) Устанавливает отдельные настройки сервера с несколькими значениями для операции. Элементы коллекции должны иметь тип String.
setDBRoles(Collection dbRoles) Задает роли БД, которые будут установлены перед выполнением операции. Элементы коллекции должны быть строковыми значениями String.
setOption(String option, Object value) Задаёт параметр конфигурации в исходном формате. Это не настройка сервера.

QueryResponse

Объект ответа, содержащий результат выполнения запроса. Доступен только в том случае, если клиент получил ответ от сервера.

Метод Описание
ClickHouseFormat getFormat() Возвращает формат, в котором кодируются данные ответа.
InputStream getInputStream() Возвращает несжатый поток байтов с данными в указанном формате.
OperationMetrics getMetrics() Возвращает объект, содержащий метрики операции.
String getQueryId() Возвращает Query id, назначенный операции приложением (через настройки операции) или сервером.
TimeZone getTimeZone() Возвращает часовой пояс, который следует использовать для обработки типов Date/DateTime в ответе.

Примеры

Общий API

getTableSchema(String table)

Загружает схему таблицы для table.

Сигнатуры

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

Параметры

table - имя таблицы, для которой нужно получить данные схемы.

database — база данных, в которой определена целевая таблица.

Возвращаемое значение

Возвращает объект TableSchema со списком столбцов таблицы.

getTableSchemaFromQuery(String sql)

Извлекает схему из SQL-оператора.

Сигнатуры

TableSchema getTableSchemaFromQuery(String sql)

Параметры

sql - оператор SQL "SELECT", схему которого необходимо вернуть.

Возвращаемое значение

Возвращает объект TableSchema со столбцами, соответствующими выражению sql.

TableSchema

register(Class<?> clazz, TableSchema schema)

Компилирует слой сериализации и десериализации для Java-класса, используемого при записи/чтении данных с помощью schema. Метод создаёт сериализатор и десериализатор для пары геттер/сеттер и соответствующего столбца. Соответствие столбца определяется путём извлечения его имени из имени метода. Например, getFirstName будет соответствовать столбцу first_name или firstname.

Сигнатуры

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

Параметры

clazz — класс, представляющий POJO, используемый для чтения/записи данных.

schema — схема данных для сопоставления со свойствами POJO.

Примеры

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

Примеры использования

Полный код примеров хранится в репозитории в папке 'example`:

  • client-v2 — основной набор примеров.
  • demo-service — пример использования клиента в приложении Spring Boot.
  • demo-kotlin-service — пример использования клиента в приложении на Ktor (Kotlin).

Чтение данных

Есть два распространённых способа чтения данных:

  • метод query(), который возвращает низкоуровневый объект QueryResponse, содержащий InputStream с данными. Обычно используется вместе с ClickHouseBinaryFormatReader для потокового чтения, но может использоваться и с любой другой пользовательской реализацией ридера. QueryResponse также предоставляет доступ к метаданным результирующего набора и метрикам.
  • Метод queryAll() и использование GenericRecord для удобного доступа к строкам. В этом случае весь результирующий набор загружается в память.
  • Метод queryRecords(), который возвращает com.clickhouse.client.api.query.Records, — это итератор по объектам GenericRecord. Этот метод использует потоковый подход (данные не загружаются в память) и GenericRecord для доступа к данным.

Примечание: стриминговый подход требует быстрого чтения данных, иначе может возникнуть тайм-аут записи на сервере, так как данные считываются напрямую из сетевого потока.

Чтение массивов

Методы ClickHouseBinaryFormatReader

  • getList(...) — читает любой Array(...) как List<T>. Хороший вариант по умолчанию для чтения с гибкой типизацией. Поддерживает вложенные массивы.
  • getByteArray(...), getShortArray(...), getIntArray(...), getLongArray(...), getFloatArray(...), getDoubleArray(...), getBooleanArray(...) — лучше всего подходят для одномерных массивов значений, совместимых с примитивными типами.
  • getStringArray(...) — для Array(String) (и значений enum, представленных в виде имён).
  • getObjectArray(...) — универсальный вариант для Array(...) с любым типом элементов, включая вложенные массивы. Используйте для чтения массивов с Nullable-значениями и вложенных массивов.

Для всех методов доступны перегрузки по индексу и по имени. Индексация начинается с 1. Перегрузки по индексу обеспечивают прямой доступ к столбцу. Методы по имени требуют поиска по индексу при каждом обращении.

try (QueryResponse response = client.query("SELECT * FROM my_table").get()) {
    ClickHouseBinaryFormatReader reader = client.newBinaryFormatReader(response);
    while (reader.next() != null) {

        Object[] uint64 = reader.getObjectArray("uint64_arr"); // Array(UInt64) -> BigInteger[]
        Object[] arr2d = reader.getObjectArray("arr2d");       // Array(Array(Int64)) -> Object[]

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

Методы GenericRecord

  • getList(...) — читает любой Array(...) как List<T>. Хороший вариант по умолчанию для чтения с гибкой типизацией. Поддерживает вложенные массивы.
  • getByteArray(...), getShortArray(...), getIntArray(...), getLongArray(...), getFloatArray(...), getDoubleArray(...), getBooleanArray(...) — лучше всего подходят для одномерных массивов значений, совместимых с примитивными типами.
  • getStringArray(...) — для Array(String) (и значений enum, представленных в виде имён).
  • getObjectArray(...) — универсальный вариант для Array(...) с любым типом элементов, включая вложенные массивы. Используйте для чтения массивов с Nullable-значениями и вложенных массивов.

Для всех методов доступны перегрузки по индексу и по имени. Индексация начинается с 1. Перегрузки по индексу обеспечивают прямой доступ к столбцу. Методы по имени требуют поиска по индексу при каждом обращении.

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

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

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

Руководство по миграции

Старый клиент (V1) использовал com.clickhouse.client.ClickHouseClient#builder в качестве точки входа. Новый клиент (V2) использует аналогичный подход с com.clickhouse.client.api.Client.Builder. Основные отличия:

  • Для получения реализации service loader не используется. com.clickhouse.client.api.Client — это класс-фасад для любых реализаций, которые могут появиться в будущем.
  • меньше источников конфигурации: один передаётся билдеру, а другой — через настройки операции (QuerySettings, InsertSettings). В предыдущей версии конфигурация задавалась для каждого узла, а в некоторых случаях также загружались переменные окружения.

Соответствие параметров конфигурации

В V1 есть 3 класса enum, связанных с конфигурацией:

  • com.clickhouse.client.config.ClickHouseDefaults — параметры конфигурации, которые обычно задаются в большинстве случаев. Например, USER и PASSWORD.
  • com.clickhouse.client.config.ClickHouseClientOption — параметры конфигурации, относящиеся непосредственно к клиенту. Например, HEALTH_CHECK_INTERVAL.
  • com.clickhouse.client.http.config.ClickHouseHttpOption — параметры конфигурации, характерные для HTTP-интерфейса. Например, RECEIVE_QUERY_PROGRESS.

Они были разработаны для группировки параметров и обеспечения чёткого разделения. Однако в некоторых случаях это приводило к путанице (например, есть ли разница между com.clickhouse.client.config.ClickHouseDefaults#ASYNC и com.clickhouse.client.config.ClickHouseClientOption#ASYNC). Новый клиент V2 использует com.clickhouse.client.api.Client.Builder как единый словарь всех возможных параметров конфигурации клиента. В классе com.clickhouse.client.api.ClientConfigProperties перечислены все имена параметров конфигурации.

В таблице ниже показано, какие старые параметры поддерживаются в новом клиенте и что они означают теперь.

Обозначения: ✔ = поддерживается, ✗ = удалено

Конфигурация V1 Метод Builder в V2 Комментарии
ClickHouseDefaults#HOST Client.Builder#addEndpoint
ClickHouseDefaults#PROTOCOL В V2 поддерживается только HTTP
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

Общие различия

  • Клиент V2 использует меньше проприетарных классов, что повышает переносимость. Например, V2 работает с любой реализацией java.io.InputStream для записи данных на сервер.
  • Настройка async в Client V2 по умолчанию имеет значение off. Это означает отсутствие дополнительных потоков и больший контроль приложения над клиентом. Для большинства сценариев использования эта настройка должна оставаться off. При включении async для запроса создаётся отдельный поток. Это имеет смысл только при использовании исполнителя, управляемого приложением (см. com.clickhouse.client.api.Client.Builder#setSharedOperationExecutor)

Запись данных

  • используйте любую реализацию java.io.InputStream. V1 com.clickhouse.data.ClickHouseInputStream поддерживается, но использовать её НЕ рекомендуется.
  • Как только обнаруживается конец входного потока, он обрабатывается соответствующим образом. Перед этим необходимо закрыть выходной поток запроса.

V1: вставка данных в формате 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 Вставка данных в формате 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
}
  • нужно вызвать только один метод. Нет необходимости создавать дополнительный объект запроса.
  • Поток с телом запроса автоматически закрывается после копирования всех данных.
  • Доступен новый низкоуровневый API com.clickhouse.client.api.Client#insert(java.lang.String, java.util.List<java.lang.String>, com.clickhouse.client.api.DataStreamWriter, com.clickhouse.data.ClickHouseFormat, com.clickhouse.client.api.insert.InsertSettings). com.clickhouse.client.api.DataStreamWriter предназначен для реализации пользовательской логики записи данных. Например, для чтения данных из очереди.

Чтение данных

  • По умолчанию данные читаются в формате RowBinaryWithNamesAndTypes. Сейчас при необходимости привязки данных поддерживается только этот формат.
  • Данные можно считать как набор записей с помощью метода List<GenericRecord> com.clickhouse.client.api.Client#queryAll(java.lang.String). Он считывает данные в память и закрывает соединение. Никакой дополнительной обработки не требуется. GenericRecord предоставляет доступ к данным и поддерживает некоторые преобразования.
Collection<GenericRecord> records = client.queryAll("SELECT * FROM table");
for (GenericRecord record : records) {
    int rowId = record.getInteger("rowID");
    String name = record.getString("name");
    LocalDateTime ts = record.getLocalDateTime("ts");
}

Библиотека Java-клиента для взаимодействия с сервером БД через его протоколы. Текущая реализация поддерживает только HTTP-интерфейс. Библиотека предоставляет собственный API для отправки запросов на сервер.

Настройка

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

Начиная с версии 0.5.0, драйвер использует новую клиентскую HTTP-библиотеку, которую необходимо добавить как зависимость.

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

Инициализация

Формат URL-адреса подключения: protocol://host[:port][/database][?param[=value][&param[=value]][#tag[,tag]], например:

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

Подключение к одному узлу:

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

Подключение к кластеру с несколькими узлами:

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

API запросов

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

API стриминговых запросов

try (ClickHouseClient client = ClickHouseClient.newInstance(ClickHouseProtocol.HTTP);
     ClickHouseResponse response = client.read(servers)
        .format(ClickHouseFormat.RowBinaryWithNamesAndTypes)
        .query("select * from numbers limit :limit")
        .params(1000)
        .executeAndWait()) {
            for (ClickHouseRecord r : response.records()) {
            int num = r.getValue(0).asInteger();
            // преобразование типов
            String str = r.getValue(0).asString();
            LocalDate date = r.getValue(0).asDate();
        }
}

См. полный пример кода в репозитории.

API вставки

try (ClickHouseClient client = ClickHouseClient.newInstance(ClickHouseProtocol.HTTP);
     ClickHouseResponse response = client.read(servers).write()
        .format(ClickHouseFormat.RowBinaryWithNamesAndTypes)
        .query("insert into my_table select c2, c3 from input('c1 UInt8, c2 String, c3 Int32')")
        .data(myInputStream) // `myInputStream` — источник данных в формате RowBinary
        .executeAndWait()) {
            ClickHouseResponseSummary summary = response.getSummary();
            summary.getWrittenRows();
}

См. полный пример кода в репозитории.

Кодирование RowBinary

Формат RowBinary описан на соответствующей странице.

Пример кода приведён здесь.

Возможности

Сжатие

По умолчанию клиент будет использовать сжатие LZ4, для которого требуется следующая зависимость:

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

Вы также можете использовать gzip, задав compress_algorithm=gzip в URL подключения.

Кроме того, сжатие можно отключить несколькими способами.

  1. Отключите это, указав compress=0 в URL подключения: http://localhost:8123/default?compress=0
  2. Отключите в конфигурации клиента:
ClickHouseClient client = ClickHouseClient.builder()
   .config(new ClickHouseConfig(Map.of(ClickHouseClientOption.COMPRESS, false)))
   .nodeSelector(ClickHouseNodeSelector.of(ClickHouseProtocol.HTTP))
   .build();

Подробнее о различных вариантах сжатия см. в документации по сжатию.

Несколько запросов

Выполнять несколько запросов в потоке-воркере подряд в рамках одного и того же сеанса:

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

Именованные параметры

Параметры можно передавать по имени, не полагаясь исключительно на их позицию в списке параметров. Эта возможность доступна с помощью функции params.

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

Обнаружение узлов

Java-клиент поддерживает автоматическое обнаружение узлов ClickHouse. По умолчанию эта функция отключена. Чтобы включить её вручную, задайте для auto_discovery значение true:

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

Или в URL подключения:

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

Если автообнаружение включено, нет необходимости указывать все узлы ClickHouse в URL подключения. Узлы, указанные в URL, будут использоваться как seed-узлы, а Java-клиент автоматически обнаружит дополнительные узлы из системных таблиц и/или clickhouse-keeper или zookeeper.

Следующие параметры отвечают за настройку автоматического обнаружения:

Свойство По умолчанию Описание
auto_discovery false Должен ли клиент обнаруживать дополнительные узлы через системные таблицы и/или clickhouse-keeper/zookeeper.
node_discovery_interval 0 Интервал обнаружения узлов в миллисекундах; нулевое или отрицательное значение означает однократное обнаружение.
node_discovery_limit 100 Максимальное число узлов, которые можно обнаружить за один раз; нулевое или отрицательное значение означает, что ограничение отсутствует.

Балансировка нагрузки

Java-клиент выбирает узел ClickHouse для отправки запросов согласно политике балансировки нагрузки. В общем случае политика балансировки нагрузки отвечает за следующее:

  1. Получить узел из списка управляемых узлов.
  2. Управление состоянием узла.
  3. При необходимости запланируйте выполнение фонового процесса для обнаружения узлов (если включено автообнаружение) и выполните проверку работоспособности.

Ниже приведён список параметров настройки балансировки нагрузки:

Параметр По умолчанию Описание
load_balancing_policy "" Политика балансировки нагрузки может быть одной из:
  • firstAlive — запрос отправляется на первый доступный узел из списка управляемых узлов
  • random — запрос отправляется на случайный узел из списка управляемых узлов
  • roundRobin — запрос по очереди отправляется на каждый узел из списка управляемых узлов.
  • полное имя класса, реализующего ClickHouseLoadBalancingPolicy — пользовательская политика балансировки нагрузки
  • Если она не указана, запрос отправляется на первый узел из списка управляемых узлов
    load_balancing_tags "" Теги балансировки нагрузки для фильтрации узлов. Запросы отправляются только на узлы с указанными тегами
    health_check_interval 0 Интервал проверки состояния в миллисекундах; нулевое или отрицательное значение означает однократную проверку.
    health_check_method ClickHouseHealthCheckMethod.SELECT_ONE Метод проверки работоспособности. Может быть одним из следующих:
  • ClickHouseHealthCheckMethod.SELECT_ONE — проверка с помощью запроса select 1
  • ClickHouseHealthCheckMethod.PING — проверка на уровне протокола, которая обычно выполняется быстрее
  • node_check_interval 0 Интервал проверки узла в миллисекундах; отрицательное число считается равным нулю. Состояние узла проверяется, если с момента последней проверки прошло указанное количество времени.
    Разница между health_check_interval и node_check_interval заключается в том, что параметр health_check_interval планирует фоновую задачу, которая проверяет состояние списка узлов (всех или неисправных), а node_check_interval задаёт, сколько времени должно пройти с момента последней проверки конкретного узла
    check_all_nodes false Выполнять ли проверку состояния для всех узлов или только для неисправных.

    Переключение при отказе и повторные попытки

    Java-клиент предоставляет параметры конфигурации для настройки поведения failover и retry при сбоях запросов:

    Свойство По умолчанию Описание
    failover 0 Максимальное число переключений при отказе для одного запроса. Нулевое или отрицательное значение означает, что переключение при отказе отключено. При переключении при отказе неудавшийся запрос отправляется на другой узел (в соответствии с политикой балансировки нагрузки), чтобы восстановить работоспособность после сбоя.
    retry 0 Максимальное количество повторных попыток для запроса. Нулевое или отрицательное значение означает, что повторные попытки не выполняются. Повторная попытка отправляет запрос на тот же узел и выполняется только в том случае, если ClickHouse server возвращает код ошибки NETWORK_ERROR
    repeat_on_session_lock true Следует ли повторять выполнение, пока сеанс заблокирован, до истечения тайм-аута (согласно session_timeout или connect_timeout). Неудачный запрос повторяется, если ClickHouse server возвращает код ошибки SESSION_IS_LOCKED

    Добавление пользовательских HTTP-заголовков

    Java-клиент поддерживает транспортный уровень HTTP/S и позволяет добавлять пользовательские HTTP-заголовки к запросу. Для этого используйте свойство custom_http_headers; заголовки перечисляются через ,. Ключ и значение заголовка разделяются символом =.

    Поддержка Java-клиента

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

    JDBC-драйвер

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