clickhouse-jdbc реализует стандартный интерфейс JDBC. Будучи построенным на основе clickhouse-client, он предоставляет дополнительные возможности, такие как пользовательское сопоставление типов, поддержка транзакций и стандартные синхронные команды UPDATE и DELETE, что позволяет легко использовать его с устаревшими приложениями и инструментами.
Примечание
Последняя версия JDBC (0.7.2) использует Client-V1
API clickhouse-jdbc является синхронным и, как правило, имеет больше накладных расходов (например, парсинг SQL и маппинг/преобразование типов и т. д.). Используйте clickhouse-client, когда производительность критична или если вы предпочитаете более прямой способ доступа к ClickHouse.
Требования к среде
Настройка
<!-- https://mvnrepository.com/artifact/com.clickhouse/clickhouse-jdbc -->
<dependency>
<groupId>com.clickhouse</groupId>
<artifactId>clickhouse-jdbc</artifactId>
<version>0.7.2</version>
<!-- используйте uber-jar, включающий все зависимости; для уменьшения размера jar измените classifier на http -->
<classifier>shaded-all</classifier>
</dependency>
// https://mvnrepository.com/artifact/com.clickhouse/clickhouse-jdbc
// используйте uber-jar, включающий все зависимости; для уменьшения размера jar измените classifier на http
implementation("com.clickhouse:clickhouse-jdbc:0.7.2:shaded-all")
// https://mvnrepository.com/artifact/com.clickhouse/clickhouse-jdbc
// используйте uber-jar, включающий все зависимости; для уменьшения размера jar измените classifier на http
implementation 'com.clickhouse:clickhouse-jdbc:0.7.2:shaded-all'
Начиная с версии 0.5.0 используется Apache HTTP Client, встроенный в клиент. Поскольку общая версия пакета отсутствует, необходимо добавить логгер в качестве зависимости.
<!-- https://mvnrepository.com/artifact/org.slf4j/slf4j-api -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>2.0.16</version>
</dependency>
// https://mvnrepository.com/artifact/org.slf4j/slf4j-api
implementation("org.slf4j:slf4j-api:2.0.16")
// https://mvnrepository.com/artifact/org.slf4j/slf4j-api
implementation 'org.slf4j:slf4j-api:2.0.16'
Конфигурация
Класс драйвера: com.clickhouse.jdbc.ClickHouseDriver
Синтаксис URL: jdbc:(ch|clickhouse)[:<protocol>]://endpoint1[,endpoint2,...][/<database>][?param1=value1¶m2=value2][#tag1,tag2,...], например:
jdbc:ch://localhost эквивалентно jdbc:clickhouse:http://localhost:8123
jdbc:ch:https://localhost эквивалентен jdbc:clickhouse:http://localhost:8443?ssl=true&sslmode=STRICT
jdbc:ch:grpc://localhost — это то же самое, что и jdbc:clickhouse:grpc://localhost:9100
Свойства соединения:
| Параметр | Значение по умолчанию | Описание |
|---|
continueBatchOnError | false | Продолжать ли пакетную обработку при возникновении ошибки |
createDatabaseIfNotExist | false | Создавать базу данных, если она не существует |
custom_http_headers | | пользовательские HTTP-заголовки, перечисленные через запятую, например: User-Agent=client1,X-Gateway-Id=123 |
custom_http_params | | пользовательские HTTP-параметры запроса, перечисленные через запятую, например: extremes=0,max_result_rows=100 |
nullAsDefault | 0 | 0 - обрабатывать значение null как есть и выбрасывать исключение при вставке null в не-Nullable столбец; 1 - обрабатывать значение null как есть и отключать проверку на null при вставке; 2 - заменять null на значение по умолчанию соответствующего типа данных как при выполнении запроса, так и при вставке |
jdbcCompliance | true | Нужно ли поддерживать стандартные синхронные операции UPDATE/DELETE и эмуляцию транзакций |
typeMappings | | Настройте сопоставление между типом данных ClickHouse и классом Java, которое повлияет на результаты как getColumnType(), так и getObject(Class<>?>). Например: UInt128=java.lang.String,UInt256=java.lang.String |
wrapperObject | false | Определяет, будет ли getObject() возвращать java.sql.Array / java.sql.Struct для Array / Tuple. |
Примечание: подробнее см. в разделе конфигурация JDBC.
Поддерживаемые типы данных
Драйвер JDBC поддерживает те же форматы данных, что и клиентская библиотека.
Примечание
- AggregatedFunction - ⚠️ не поддерживает запросы вида
SELECT * FROM table ...
- Decimal -
SET output_format_decimal_trailing_zeros=1 в версии 21.9+ для согласованного вывода
- Enum — может использоваться и как строка, и как целое число
- UInt64 — сопоставляется с типом
long (в client-v1)
Создание соединения
String url = "jdbc:ch://my-server/system"; // use http protocol and port 8123 by default
Properties properties = new Properties();
ClickHouseDataSource dataSource = new ClickHouseDataSource(url, properties);
try (Connection conn = dataSource.getConnection("default", "password");
Statement stmt = conn.createStatement()) {
}
Простое выражение
try (Connection conn = dataSource.getConnection(...);
Statement stmt = conn.createStatement()) {
ResultSet rs = stmt.executeQuery("select * from numbers(50000)");
while(rs.next()) {
// ...
}
}
Insert
Примечание
- Используйте
PreparedStatement вместо Statement
Проще в использовании, но имеет более низкую производительность по сравнению с функцией input (см. ниже):
try (PreparedStatement ps = conn.prepareStatement("insert into mytable(* except (description))")) {
ps.setString(1, "test"); // id
ps.setObject(2, LocalDateTime.now()); // timestamp
ps.addBatch(); // parameters will be write into buffered stream immediately in binary format
...
ps.executeBatch(); // stream everything on-hand into ClickHouse
}
Вариант с высокой производительностью:
try (PreparedStatement ps = conn.prepareStatement(
"insert into mytable select col1, col2 from input('col1 String, col2 DateTime64(3), col3 Int32')")) {
// The column definition will be parsed so the driver knows there are 3 parameters: col1, col2 and col3
ps.setString(1, "test"); // col1
ps.setObject(2, LocalDateTime.now()); // col2, setTimestamp is slow and not recommended
ps.setInt(3, 123); // col3
ps.addBatch(); // parameters will be write into buffered stream immediately in binary format
...
ps.executeBatch(); // stream everything on-hand into ClickHouse
}
Вставка с плейсхолдерами
Этот вариант рекомендуется только для небольших вставок, поскольку потребуется длинное SQL-выражение (которое будет парситься на стороне клиента и потреблять ресурсы CPU и памяти):
try (PreparedStatement ps = conn.prepareStatement("insert into mytable values(trim(?),?,?)")) {
ps.setString(1, "test"); // id
ps.setObject(2, LocalDateTime.now()); // timestamp
ps.setString(3, null); // description
ps.addBatch(); // append parameters to the query
...
ps.executeBatch(); // issue the composed query: insert into mytable values(...)(...)...(...)
}
Обработка типа DateTime и часовых поясов
Используйте java.time.LocalDateTime или java.time.OffsetDateTime вместо java.sql.Timestamp и java.time.LocalDate вместо java.sql.Date.
try (PreparedStatement ps = conn.prepareStatement("select date_time from mytable where date_time > ?")) {
ps.setObject(2, LocalDateTime.now());
ResultSet rs = ps.executeQuery();
while(rs.next()) {
LocalDateTime dateTime = (LocalDateTime) rs.getObject(1);
}
...
}
Работа с AggregateFunction
Примечание
В настоящее время поддерживается только groupBitmap.
// batch insert using input function
try (ClickHouseConnection conn = newConnection(props);
Statement s = conn.createStatement();
PreparedStatement stmt = conn.prepareStatement(
"insert into test_batch_input select id, name, value from input('id Int32, name Nullable(String), desc Nullable(String), value AggregateFunction(groupBitmap, UInt32)')")) {
s.execute("drop table if exists test_batch_input;"
+ "create table test_batch_input(id Int32, name Nullable(String), value AggregateFunction(groupBitmap, UInt32))engine=Memory");
Object[][] objs = new Object[][] {
new Object[] { 1, "a", "aaaaa", ClickHouseBitmap.wrap(1, 2, 3, 4, 5) },
new Object[] { 2, "b", null, ClickHouseBitmap.wrap(6, 7, 8, 9, 10) },
new Object[] { 3, null, "33333", ClickHouseBitmap.wrap(11, 12, 13) }
};
for (Object[] v : objs) {
stmt.setInt(1, (int) v[0]);
stmt.setString(2, (String) v[1]);
stmt.setString(3, (String) v[2]);
stmt.setObject(4, v[3]);
stmt.addBatch();
}
int[] results = stmt.executeBatch();
...
}
// use bitmap as query parameter
try (PreparedStatement stmt = conn.prepareStatement(
"SELECT bitmapContains(my_bitmap, toUInt32(1)) as v1, bitmapContains(my_bitmap, toUInt32(2)) as v2 from {tt 'ext_table'}")) {
stmt.setObject(1, ClickHouseExternalTable.builder().name("ext_table")
.columns("my_bitmap AggregateFunction(groupBitmap,UInt32)").format(ClickHouseFormat.RowBinary)
.content(new ByteArrayInputStream(ClickHouseBitmap.wrap(1, 3, 5).toBytes()))
.asTempTable()
.build());
ResultSet rs = stmt.executeQuery();
Assert.assertTrue(rs.next());
Assert.assertEquals(rs.getInt(1), 1);
Assert.assertEquals(rs.getInt(2), 0);
Assert.assertFalse(rs.next());
}
Настройка HTTP-библиотеки
JDBC-коннектор ClickHouse поддерживает три HTTP-библиотеки: HttpClient, HttpURLConnection и Apache HttpClient.
Примечание
HttpClient поддерживается только в JDK 11 и выше.
Драйвер JDBC по умолчанию использует HttpClient. Вы можете изменить HTTP-библиотеку, используемую коннектором JDBC для ClickHouse, задав следующее свойство:
properties.setProperty("http_connection_provider", "APACHE_HTTP_CLIENT");
Полный список соответствующих значений:
| Значение свойства | Библиотека HTTP |
|---|
| HTTP_CLIENT | HttpClient |
| HTTP_URL_CONNECTION | HttpURLConnection |
| APACHE_HTTP_CLIENT | Apache HttpClient |
Подключение к ClickHouse по SSL
Для установки защищенного JDBC-соединения с ClickHouse через SSL необходимо настроить свойства JDBC, включив в них параметры SSL. Как правило, это подразумевает указание таких свойств SSL, как sslmode и sslrootcert, в JDBC URL или объекте Properties.
Свойства SSL
| Имя | Значение по умолчанию | Допустимые значения | Описание |
|---|
ssl | false | true, false | Нужно ли включать SSL/TLS для подключения |
sslmode | strict | strict, none | Проверять ли сертификат SSL/TLS |
sslrootcert | | | Путь к корневым сертификатам SSL/TLS |
sslcert | | | Путь к сертификату SSL/TLS |
sslkey | | | RSA-ключ в формате PKCS#8 |
key_store_type | | JKS, PKCS12 | Задает тип или формат файла KeyStore/TrustStore |
trust_store | | | Путь к файлу TrustStore |
key_store_password | | | Пароль, необходимый для доступа к файлу KeyStore, указанному в конфигурации KeyStore |
Эти свойства обеспечивают обмен данными между вашим Java-приложением и сервером ClickHouse по зашифрованному соединению, повышая безопасность данных при передаче.
String url = "jdbc:ch://your-server:8443/system";
Properties properties = new Properties();
properties.setProperty("ssl", "true");
properties.setProperty("sslmode", "strict"); // NONE to trust all servers; STRICT for trusted only
properties.setProperty("sslrootcert", "/mine.crt");
try (Connection con = DriverManager
.getConnection(url, properties)) {
try (PreparedStatement stmt = con.prepareStatement(
// place your code here
}
}
Устранение таймаута JDBC при больших вставках данных
При выполнении больших вставок в ClickHouse с длительным временем выполнения могут возникать ошибки тайм-аута JDBC, например:
Caused by: java.sql.SQLException: Read timed out, server myHostname [uri=https://hostname.aws.clickhouse.cloud:8443]
Эти ошибки могут нарушить процесс вставки данных и повлиять на стабильность системы. Чтобы устранить эту проблему, необходимо настроить несколько параметров таймаута в операционной системе клиента.
macOS
В macOS можно настроить следующие параметры для решения проблемы:
net.inet.tcp.keepidle: 60000
net.inet.tcp.keepintvl: 45000
net.inet.tcp.keepinit: 45000
net.inet.tcp.keepcnt: 8
net.inet.tcp.always_keepalive: 1
Linux
В Linux одних только эквивалентных настроек может быть недостаточно для решения проблемы. Требуются дополнительные действия из-за различий в обработке параметров keep-alive для сокетов в Linux. Выполните следующие действия:
- Настройте следующие параметры ядра Linux в
/etc/sysctl.conf или другом соответствующем конфигурационном файле:
net.inet.tcp.keepidle: 60000
net.inet.tcp.keepintvl: 45000
net.inet.tcp.keepinit: 45000
net.inet.tcp.keepcnt: 8
net.inet.tcp.always_keepalive: 1
net.ipv4.tcp_keepalive_intvl: 75
net.ipv4.tcp_keepalive_probes: 9
net.ipv4.tcp_keepalive_time: 60 (имеет смысл снизить это значение по сравнению со значением по умолчанию — 300 секунд)
- После изменения параметров ядра примените их, выполнив следующую команду:
После настройки этих параметров необходимо убедиться, что ваш клиент включает опцию Keep Alive для сокета:
properties.setProperty("socket_keepalive", "true");
Примечание
В настоящее время для настройки socket keep-alive необходимо использовать библиотеку Apache HTTP Client, так как две другие HTTP-клиентские библиотеки, поддерживаемые clickhouse-java, не позволяют настраивать параметры сокетов. Подробное руководство см. в разделе Настройка HTTP-библиотеки.
Также можно добавить эквивалентные параметры в JDBC URL.
По умолчанию таймаут сокета и подключения для драйвера JDBC составляет 30 секунд. Таймаут можно увеличить для поддержки операций вставки больших объёмов данных. Используйте метод options объекта ClickHouseClient вместе с параметрами SOCKET_TIMEOUT и CONNECTION_TIMEOUT, определёнными в ClickHouseClientOption:
final int MS_12H = 12 * 60 * 60 * 1000; // 12 h in ms
final String sql = "insert into table_a (c1, c2, c3) select c1, c2, c3 from table_b;";
try (ClickHouseClient client = ClickHouseClient.newInstance(ClickHouseProtocol.HTTP)) {
client.read(servers).write()
.option(ClickHouseClientOption.SOCKET_TIMEOUT, MS_12H)
.option(ClickHouseClientOption.CONNECTION_TIMEOUT, MS_12H)
.query(sql)
.executeAndWait();
}