clickhouse-jdbc реализует стандартный интерфейс JDBC на базе новейшего Java-клиента.
Если критически важны производительность и прямой доступ, рекомендуем использовать новейший Java-клиент напрямую.Требования к среде
- OpenJDK версии >= 8
Setup
- Maven
- Gradle (Kotlin)
- Gradle
- Maven Central и добавьте библиотеку в classpath
- начиная с версии
0.9.4доступен artifact https://mvnrepository.com/artifact/com.clickhouse/clickhouse-jdbc-all - используйте квалификатор
all, чтобы получить jar со всеми включёнными затенёнными зависимостями.
- начиная с версии
- или из официального репозитория по этой ссылке
Конфигурация
Класс драйвера:com.clickhouse.jdbc.ClickHouseDrivercom.clickhouse.jdbc.ClickHouseDriver — это фасадный класс для новой и старой реализаций JDBC. По умолчанию он использует новую реализацию JDBC.
Старую реализацию JDBC можно использовать, установив system-свойство clickhouse.jdbc.v1 в значение true. Это свойство нужно установить до обращения к
классу Driver.Альтернативный способ переключения между версиями — напрямую использовать классы Driver соответствующей версии:com.clickhouse.jdbc.Driver— новая реализация JDBC (V2).com.clickhouse.jdbc.DriverV1— старая реализация JDBC (V1).
jdbc:(ch|clickhouse)[:<protocol>]://endpoint[:port][/<database>][?param1=value1¶m2=value2][#tag1,tag2,...], например:jdbc:clickhouse:http://localhost:8123jdbc:clickhouse:https://localhost:8443?ssl=true
- В URL допускается только одна конечная точка
- протокол следует указывать, если используется не значение по умолчанию — ‘HTTP’
- порт следует указать, если используется не стандартный порт ‘8123’
- драйвер не пытается определить протокол по порту, его нужно указать явно
- Параметр
sslне требуется, если указан протокол.
Свойства подключения
Основные параметры конфигурации определены в разделе Java-клиента. Их следует передавать в драйвер без изменений. Драйвер также имеет ряд собственных свойств, не входящих в конфигурацию клиента; они перечислены ниже.Свойства драйвера:Настройки сервераВсе настройки сервера должны иметь префикс
clickhouse_setting_ (как и в конфигурации клиента).readonly=2.
Подробнее о профилях только для чтения читайте здесь.Идентификация клиента
Существует два способа идентифицировать приложение, из которого поступил запрос: задатьcom.clickhouse.client.api.ClientConfigProperties#CLIENT_NAME через
параметры подключения или использовать метод java.sql.Connection#setClientInfo(String name, String value).showLineNumbers
showLineNumbers
http_user_agent в журнале запросов:app_name/version для свойства client_name — это помогает идентифицировать приложение в журнале запросов.Идентификация операции
JDBC driver генерируетquery_id для каждой операции (в настоящее время он включён в исключения сервера).Чтобы задать log_comment для операции, используйте метод com.clickhouse.jdbc.StatementImpl#getLocalSettings. Для этого необходимо сначала привести Statement или PreparedStatement к типу com.clickhouse.jdbc.StatementImpl.showLineNumbers
localSettings является общим для всех потоков.Поддерживаемые типы данных
JDBC driver поддерживает те же форматы данных, что и базовый Java-клиент.Соответствие типов JDBC
Следующее соответствие применяется к:ResultSet#getObject(columnIndex)— метод возвращает объект соответствующего класса Java. (Int8->java.lang.Byte,Int16->java.lang.Short, и т. д.)ResultSetMetaData#getColumnType(columnIndex)— метод возвращает соответствующий тип JDBC. (Int8->java.lang.Byte,Int16->java.lang.Short, и т. д.)
ResultSet#getObject(columnIndex, class)— метод попытается преобразовать значение к типуclass. Существуют некоторые ограничения на преобразование. Подробнее см. в соответствующих разделах.
- числовые типы можно преобразовывать друг в друга. Поэтому
Int8можно получить какFloat64и наоборот.:rs.getObject(1, Float64.class)вернёт значение типаFloat64из столбцаInt8.rs.getLong(1)вернёт значение типаLongиз столбцаInt8.rs.getByte(1)может вернуть значение типаByteиз столбцаInt16, если оно помещается вByte.
- Преобразование из более широкого типа в более узкий не рекомендуется из-за риска повреждения данных.
- Тип
Boolтакже может использоваться как число. - Все числовые типы можно считывать как
java.lang.String. - При сохранении
Float.MAX_VALUEиз Java какFloatвозникает проблема (https://github.com/ClickHouse/clickhouse-java/issues/809). Если сохранить то же значение какDouble, проблема исчезает.
Stringможно считывать только какjava.lang.Stringилиbyte[].FixedStringсчитывается как есть и дополняется нулями до длины столбца. (Например,FixedString(10)для'John'будет считан как'John\0\0\0\0\0\0\0\0\0'.)
Enum8иEnum16по умолчанию соответствуют типуjava.lang.String.- Значения enum можно считывать как числовые, используя соответствующий геттер или метод
getObject(columnIndex, Integer.class). Enum16внутренне представлен как short, аEnum8— как byte. Следует избегать чтенияEnum16как byte из-за риска повреждения данных.- Значения enum можно задавать в
PreparedStatementкак строковые или числовые.
- Типы Date / Time сопоставляются с типами
java.sqlдля лучшей совместимости с JDBC. Однако можно получитьjava.time.LocalDate,java.time.LocalDateTimeиjava.time.LocalTime, используяResultSet#getObject(columnIndex, Class<T>)и указав соответствующий класс вторым аргументом.rs.getObject(1, java.time.LocalDate.class)вернёт значениеjava.time.LocalDateиз столбцаDate.rs.getObject(1, java.time.LocalDateTime.class)вернёт значениеjava.time.LocalDateTimeиз столбцаDateTime.rs.getObject(1, java.time.LocalTime.class)вернёт значениеjava.time.LocalTimeиз столбцаTime.
Date,Date32,Time,Time64не зависят от часового пояса сервера.- На
DateTimeиDateTime64влияет часовой пояс сервера или сеанса. DateTimeиDateTime64можно получать в видеZonedDateTimeс помощьюgetObject(colIndex, ZonedDateTime.class).
Arrayпо умолчанию сопоставляется сjava.sql.Arrayдля совместимости с JDBC. Это также позволяет предоставить больше информации о возвращаемом значении массива. Полезно для вывода типов.Arrayреализует методgetResultSet(), который возвращаетjava.sql.ResultSetс тем же содержимым, что и исходный массив.- Типы коллекций не следует считывать как
java.lang.String, поскольку это недопустимый способ представления данных (например, в массиве строковые значения не заключаются в кавычки). Mapсопоставляется сOTHER, потому что значение можно получить только с помощью методаgetObject(columnIndex, Class<T>).Mapне являетсяjava.sql.Struct, поскольку у него нет именованных столбцов.
Tupleсопоставляется сObject[], поскольку он может содержать значения разных типов, а использоватьListнельзя.Tupleможно считать какArrayс помощью методаgetObject(columnIndex, Array.class). В этом случаеArray#baseTypeNameвернёт определение столбцаTuple.
Array.getBaseTypeName() возвращает имя типа элемента ClickHouse; Array.getBaseType() возвращает код типа JDBC.
JDBC V2 сохраняет полные сигнатуры типов (типы-обёртки, параметры типов), которые V1 отбрасывает.Общие правила соответствия для массивов:Notes on the rules above:
- Типы-обёртки (
Nullable,LowCardinality) сохраняются вgetBaseTypeName(), ноgetBaseType()возвращает JDBC-код внутреннего типа. - Вложенные массивы в метаданных представлены в развёрнутом виде:
getBaseTypeName()возвращает тип самого глубоко вложенного элемента, не являющегося массивом, а не тип непосредственного дочернего элемента. - Параметризованные типы (
FixedString(N), полные определенияEnum/Tuple) сохраняют свои параметры вgetBaseTypeName().
- В V2
getBaseTypeName()сохраняет полную сигнатуру типа, включая типы-обёртки (Nullable,LowCardinality) и параметры типа (FixedString(8), полные определенияEnumиTuple). V1 убирает их и возвращает только имя базового типа. - Массивы
Tupleв V2 используютOTHER (1111)вместоSTRUCT (2002), потому что кортежи ClickHouse имеют именованные поля, аjava.sql.Structих не поддерживает. - Массивы
UUIDв V2 используютOTHER (1111), что соответствует сопоставлению скалярногоUUID. - Значения
Enumсопоставляются сVARCHAR— элементы enum идентифицируются по строковому имени независимо от базовой числовой кодировки.
java.sql.Connection#createArrayOf для создания экземпляра объекта java.sql.Array. Этот объект предназначен для унификации работы с массивами в различных базах данных.
Соединение необходимо для передачи конфигурации в фабричный метод Array.Метод принимает два аргумента:typeName— имя типа элементов массива. Например:Array(Int32)->"Int32".elements— сами элементы массива. Например,[[1, 2, 3], [4, 5, 6]]->new Integer[][] {{1, 2, 3}, {4, 5, 6}}.
Object[] или как java.sql.Struct (см. раздел о записи Tuple ниже).ПримерResultSet#getArray(columnIndex) для чтения объекта Array. Объект позволяет обращаться к массивам любой глубины вложенности.
Метод Array#getResultSet() можно использовать для чтения элементов массива в более унифицированном виде в качестве java.sql.ResultSet. Это удобно,
когда точный тип элементов массива неизвестен.Примерcom.clickhouse.data.Tuple и должны записываться как этот объект с помощью вызова метода setObject(columnIndex, tuple).
Для большей переносимости можно использовать объект java.sql.Struct для записи значений типа Tuple.ПримерgetObject(columnIndex) возвращает Object[]. Tuple можно считать как java.sql.Array с помощью метода getObject(columnIndex, Array.class).Примерjava.collections.Map, поскольку этот тип требует пар ключ-значение (java.sql.Struct не поддерживает пары ключ-значение).Примерjava.collections.Map с помощью метода getObject(columnIndex, Map.class).Примерjava.sql.Connection#createStruct для создания экземпляра объекта java.sql.Struct. Этот объект предназначен для унификации обработки вложенных структур в различных базах данных.
Соединение необходимо для передачи конфигурации в фабричный метод Struct.Метод принимает два аргумента:typeName— имя типа вложенных элементов. Например:Nested(Tuple(Int32, String))->"Nested(Tuple(Int32, String))".elements— сами вложенные элементы. Например,[1, 'test']->new Object[] {1, 'test'}.
ResultSet#getStruct(columnIndex, StructDescriptor) для чтения объекта Nested. Объект позволяет обращаться к вложенным элементам любой глубины вложенности.
Метод Struct#getResultSet() можно использовать для чтения вложенных элементов в более унифицированном виде — в форме java.sql.ResultSet. Это удобно,
когда точный тип вложенных элементов неизвестен.ПримерТипы Nullable и LowCardinality
NullableиLowCardinality— это специальные типы, которые являются оболочками для других типов.Nullableвлияет на то, как вResultSetMetaDataвозвращаются имена типов
UUIDне входит в стандарт JDBC. Однако он входит в состав JDK. По умолчанию методgetObject()возвращает объектjava.util.UUID.UUIDможно считывать и записывать какStringс помощью методаgetObject(columnIndex, String.class).IPv4иIPv6не относятся к стандартным типам JDBC. Однако они входят в JDK. По умолчанию методgetObject()возвращаетjava.net.Inet4Addressиjava.net.Inet6Address.IPv4иIPv6можно считывать и записывать какStringс помощью методаgetObject(columnIndex, String.class).
Map<String, Object>, где ключи — это ключи объекта JSON, а значения — значения объекта JSON.
Например:jdbc_read_json_as_string=true в свойства подключения.
Это заставляет драйвер возвращать значения JSON как String, которые затем можно разобрать с помощью любой JSON-библиотеки.Работа с датами, временем и часовыми поясами
Ознакомьтесь с руководством по Date/Time, в котором описаны типичные проблемы и логика работы драйвера при обработке значений Date/Time и временных меток.Создание подключения
Передача учётных данных и настроек
showLineNumbers
Простой оператор
showLineNumbers
Вставка
showLineNumbers
HikariCP
showLineNumbers
Дополнительная информация
Дополнительную информацию см. в нашем репозитории GitHub и документации по Java-клиенту.Устранение неполадок
Логирование
Драйвер использует slf4j для логирования и задействует первую доступную реализацию вclasspath.Устранение тайм-аута JDBC при больших вставках
При выполнении больших операций вставки в ClickHouse с длительным временем выполнения могут возникать ошибки тайм-аута JDBC, например:Mac OS
В Mac OS для решения этой проблемы можно изменить следующие настройки:net.inet.tcp.keepidle: 60000net.inet.tcp.keepintvl: 45000net.inet.tcp.keepinit: 45000net.inet.tcp.keepcnt: 8net.inet.tcp.always_keepalive: 1
Linux
В Linux одних только аналогичных настроек может оказаться недостаточно для решения проблемы. Из-за различий в том, как Linux обрабатывает настройки keep-alive для сокетов, потребуются дополнительные шаги. Выполните следующие действия:- Настройте следующие параметры ядра Linux в
/etc/sysctl.confили соответствующем файле конфигурации:
net.inet.tcp.keepidle: 60000net.inet.tcp.keepintvl: 45000net.inet.tcp.keepinit: 45000net.inet.tcp.keepcnt: 8net.inet.tcp.always_keepalive: 1net.ipv4.tcp_keepalive_intvl: 75net.ipv4.tcp_keepalive_probes: 9net.ipv4.tcp_keepalive_time: 60 (При необходимости можно уменьшить это значение по сравнению со значением по умолчанию — 300 секунд)
- После изменения параметров ядра примените их, выполнив следующую команду:
Руководство по миграции
Основные изменения
- JDBC V2 реализован в более облегчённом виде, и некоторые возможности были удалены.
- Потоковая передача данных не поддерживается в JDBC V2, поскольку она не предусмотрена спецификацией JDBC и Java.
- JDBC V2 требует явной конфигурации. Для аварийного переключения значения по умолчанию не предусмотрены.
- Протокол должен быть указан в URL. Не выполняется неявное определение протокола по номеру порта.
Изменения конфигурации
Существует только два enum-типа:com.clickhouse.jdbc.DriverProperties- собственные параметры конфигурации драйвера.com.clickhouse.client.api.ClientConfigProperties— свойства конфигурации клиента. Изменения в конфигурации клиента описаны в документации по Java-клиенту.
- Сначала URL разбирается на свойства. Они имеют приоритет над всеми остальными свойствами.
- Свойства драйвера не передаются клиенту.
- Параметры конечной точки (хост, порт, протокол) разбираются из URL.
Изменения типов данных
Числовые типы- Основное различие заключается в том, что беззнаковые типы сопоставляются с типами Java для лучшей переносимости.
FixedStringв обеих версиях читается как есть. Например,FixedString(10)для'John'будет прочитан как'John\0\0\0\0\0\0\0\0\0'.- При использовании
PreparedStatement#setBytesзначение преобразуется вunhex('<hex_string>'), а затем считывается какString. - Строки хранятся в кодировке UTF-8.
TimeиTime64поддерживаются в V2 только в качестве новых типов.DateTimeиDateTime64сопоставляются сjava.sql.Timestampдля обеспечения лучшей совместимости с JDBC.
Вложенные типы
- В V2
Arrayпо умолчанию сопоставляется сjava.sql.Arrayдля совместимости с JDBC. Это также сделано, чтобы предоставить больше информации о возвращаемом значении массива. Полезно для вывода типов. - В V2
Arrayреализует методgetResultSet(), который возвращаетjava.sql.ResultSetс тем же содержимым, что и исходный массив. - V1 использует
STRUCTдляMap, но всегда возвращает объект типаjava.util.Map. В V2 это исправлено:Mapсопоставляется сJAVA_OBJECT. - V1 использует
STRUCTдляTuple, но всегда возвращает объект типаList<Object>. V2 отображаетTupleкакOTHERи по умолчанию возвращаетObject[]. - V2 добавляет
com.clickhouse.data.Tuple#Tupleдля записи кортежей. Это упрощает определение того, является ли значение кортежем или массивом. PreparedStatement#setBytesиResultSet#getBytesнельзя использовать с типами коллекций. Эти методы предназначены для работы с двоичными данными.- Обычно
java.sql.Arrayиспользуется для записи и чтения значений типа Array. Драйвер JDBC полностью это поддерживает. - В V2
Nestedсопоставляется сArrayи представляется как массив кортежей. - V2 частично поддерживает
java.sql.Struct, поскольку этот тип очень похож на Array и не поддерживает пары «ключ-значение».Structможно использовать для записи значенийTuple.
Типы Nullable и LowCardinality
NullableиLowCardinality— специальные типы-обёртки над другими типами.- В V2 эти типы не изменяются.
- V1 использует
VARCHARдляUUID, но при этом всегда возвращает объектjava.util.UUID. В V2 это исправлено:UUIDсопоставляется сOTHER, и возвращается объектjava.util.UUID. - V1 использует
VARCHARдляIPv4иIPv6, но всегда возвращает объектыjava.net.Inet4Addressиjava.net.Inet6Address. В V2 это исправлено:IPv4иIPv6теперь сопоставляются сOTHER, а возвращаемыми значениями являются объектыjava.net.Inet4Addressиjava.net.Inet6Address. DynamicиVariant— новые типы в V2. В V1 не поддерживаются.JSONоснован на типеDynamic, поэтому поддерживается только в V2.- Значения IPv4 и IPv6 можно читать как
byte[]с помощью методаgetBytes(columnIndex). Однако для этих типов рекомендуется использовать специальные классы. - V2 не поддерживает чтение IP-адресов в виде числовых значений, поскольку такое преобразование лучше реализовано в классах InetAddress.
Изменения метаданных базы данных
- В V2 для обозначения баз данных используется только термин
Schema. ТерминCatalogзарезервирован для будущего использования. - V2 возвращает
falseдляDatabaseMetaData.supportsTransactions()иDatabaseMetaData.supportsSavepoints(). В дальнейшем это будет изменено. - В
DatabaseMetaData.getTypeInfo()столбцыLITERAL_PREFIXиLITERAL_SUFFIXтеперь возвращаютnullдля типов данных, для которых префиксы и суффиксы не используются (например, для числовых типов). В V1 эти столбцы возвращали ненулевые значения для таких типов. Эти столбцы следует использовать при генерации SQL-запросов, чтобы корректно заключать литеральные значения в кавычки в соответствии с их типом данных.