clickhouse-jdbc implementa la interfaz estándar de JDBC con el cliente de Java más reciente.
Recomendamos usar directamente el cliente de Java más reciente si el rendimiento o el acceso directo son críticos.Requisitos del entorno
- OpenJDK versión >= 8
Setup
- Maven
- Gradle (Kotlin)
- Gradle
- Maven Central y agrégalo al classpath
- a partir de la versión
0.9.4, está disponible el artefacto https://mvnrepository.com/artifact/com.clickhouse/clickhouse-jdbc-all - use el calificador
allpara obtener el JAR con todas las dependencias empaquetadas incluidas.
- a partir de la versión
- o desde el repositorio oficial aquí
Configuración
Clase del driver:com.clickhouse.jdbc.ClickHouseDrivercom.clickhouse.jdbc.ClickHouseDriver es una clase de fachada para las implementaciones JDBC nueva y antigua. Usa la implementación JDBC nueva de forma predeterminada.
Puede usar la implementación JDBC antigua estableciendo en true la propiedad del sistema clickhouse.jdbc.v1. Esta propiedad debe establecerse antes de invocar la
clase Driver.Una forma alternativa de cambiar entre versiones es usar directamente las clases Driver de cada versión:com.clickhouse.jdbc.Driveres la implementación JDBC nueva (V2).com.clickhouse.jdbc.DriverV1es la implementación JDBC antigua (V1).
jdbc:(ch|clickhouse)[:<protocol>]://endpoint[:port][/<database>][?param1=value1¶m2=value2][#tag1,tag2,...], por ejemplo:jdbc:clickhouse:http://localhost:8123jdbc:clickhouse:https://localhost:8443?ssl=true
- solo se permite un único endpoint en la URL
- se debe especificar el protocolo cuando no sea el predeterminado - ‘HTTP’
- se debe especificar el puerto cuando no sea el predeterminado ‘8123’
- el driver no infiere el protocolo a partir del puerto, debe especificarlo explícitamente
- El parámetro
sslno se requiere cuando se especifica el protocolo.
Propiedades de conexión
Los principales parámetros de configuración se definen en el cliente de Java. Deben pasarse tal cual al driver. El driver tiene algunas propiedades propias que no forman parte de la configuración del cliente; estas se enumeran a continuación.Propiedades del driver:Configuración del servidorToda la configuración del servidor debe llevar el prefijo
clickhouse_setting_ (igual que en la configuración del cliente).readonly=2.
Lea más sobre los perfiles de solo lectura aquí.Identificación del cliente
Existen dos formas de identificar la aplicación que originó una solicitud: establecercom.clickhouse.client.api.ClientConfigProperties#CLIENT_NAME mediante
las propiedades de conexión o utilizar el método java.sql.Connection#setClientInfo(String name, String value).showLineNumbers
showLineNumbers
http_user_agent en el registro de consultas:app_name/version para la propiedad client_name, ya que facilita la identificación de la aplicación en el registro de consultas.Identificación de operaciones
El controlador JDBC genera unquery_id para cada operación (actualmente se incluye en las excepciones del servidor).Para establecer log_comment en una operación, utilice el método com.clickhouse.jdbc.StatementImpl#getLocalSettings. Para ello, es necesario hacer un cast de Statement o PreparedStatement a com.clickhouse.jdbc.StatementImpl primero.showLineNumbers
localSettings se comparte entre hilos.Tipos de datos compatibles
El JDBC driver admite los mismos formatos de datos que el java client subyacente.Correspondencia de tipos JDBC
La siguiente correspondencia se aplica a:ResultSet#getObject(columnIndex)- el método devuelve un objeto de la clase Java correspondiente. (Int8->java.lang.Byte,Int16->java.lang.Short, etc.)ResultSetMetaData#getColumnType(columnIndex)- el método devuelve el tipo JDBC correspondiente. (Int8->java.lang.Byte,Int16->java.lang.Short, etc.)
ResultSet#getObject(columnIndex, class)- el método intentará convertir el valor al tipoclass. Hay algunas limitaciones de conversión. Consulta cada sección para ver más detalles.
- los tipos numéricos son convertibles entre sí. Así,
Int8se puede obtener comoFloat64y viceversa.:rs.getObject(1, Float64.class)devolverá un valorFloat64de la columnaInt8.rs.getLong(1)devolverá un valorLongde la columnaInt8.rs.getByte(1)puede devolver un valorBytede la columnaInt16si cabe enByte.
- no se recomienda la conversión de un tipo de mayor capacidad a uno de menor capacidad debido al riesgo de corrupción de datos.
- El tipo
Booltambién funciona como número. - Todos los tipos numéricos pueden leerse como
java.lang.String. - Almacenar
Float.MAX_VALUEde Java comoFloatpresenta un problema (https://github.com/ClickHouse/clickhouse-java/issues/809). Guardar ese mismo valor comoDoublelo resuelve.
Stringsolo se puede leer comojava.lang.Stringobyte[].FixedStringse lee tal cual y se rellena con ceros hasta alcanzar la longitud de la columna. (Por ejemplo,FixedString(10)para'John'se leerá como'John\0\0\0\0\0\0\0\0\0'.)
Enum8yEnum16se asignan ajava.lang.Stringde manera predeterminada.- Los valores
enumpueden leerse como valores numéricos mediante el método getter correspondiente o el métodogetObject(columnIndex, Integer.class). Enum16se mapea internamente a short yEnum8se mapea a byte. Debe evitarse leerEnum16como byte debido al riesgo de corrupción de datos.- Los valores de
enumpueden establecerse como una cadena o un valor numérico enPreparedStatement.
- Los tipos Date / Time se asignan a tipos
java.sqlpara lograr una mejor compatibilidad con JDBC. Sin embargo, es posible obtenerjava.time.LocalDate,java.time.LocalDateTimeyjava.time.LocalTimemedianteResultSet#getObject(columnIndex, Class<T>), usando la clase correspondiente como segundo argumento.rs.getObject(1, java.time.LocalDate.class)devolverá un valorjava.time.LocalDatede la columnaDate.rs.getObject(1, java.time.LocalDateTime.class)devolverá un valorjava.time.LocalDateTimede la columnaDateTime.rs.getObject(1, java.time.LocalTime.class)devolverá un valorjava.time.LocalTimede la columnaTime.
Date,Date32,Time,Time64no se ven afectados por la zona horaria del servidor.DateTimeyDateTime64se ven afectados por la zona horaria del servidor o de la sesión.DateTimeyDateTime64se pueden recuperar comoZonedDateTimemediantegetObject(colIndex, ZonedDateTime.class).
Arrayse asigna ajava.sql.Arrayde forma predeterminada para mantener la compatibilidad con JDBC. Esto también se hace para proporcionar más información sobre el valor de array devuelto. Resulta útil para la inferencia de tipos.Arrayimplementa el métodogetResultSet()para devolver unjava.sql.ResultSetcon el mismo contenido que el array original.- Los tipos de colección no deberían interpretarse como
java.lang.String, ya que no es una forma válida de representar los datos (p. ej., en un array no se usan comillas para los valores de cadena). Mapse asigna aOTHERporque el valor solo puede leerse mediante el métodogetObject(columnIndex, Class<T>).Mapno es unjava.sql.Structporque no tiene columnas con nombres.
Tuplese mapea aObject[]porque puede contener distintos tipos y no es válido usarList.Tuplepuede leerse comoArraymediante el métodogetObject(columnIndex, Array.class). En este caso,Array#baseTypeNamedevolverá la definición de la columnaTuple.
Array.getBaseTypeName() devuelve el nombre del tipo de elemento de ClickHouse; Array.getBaseType() devuelve el código de tipo JDBC.
JDBC V2 conserva las firmas de tipo completas (tipos envoltorio, parámetros de tipo) que V1 elimina.Las reglas generales de correspondencia para los arrays son:Notas sobre las reglas anteriores:
- Los tipos de envoltorio (
Nullable,LowCardinality) se conservan engetBaseTypeName(), perogetBaseType()se resuelve como el código JDBC del tipo interno. - Los arrays anidados se aplanan en los metadatos:
getBaseTypeName()devuelve el tipo del elemento más interno que no sea un array, no el hijo inmediato. - Los tipos parametrizados (
FixedString(N), definiciones completas deEnum/Tuple) conservan sus parámetros engetBaseTypeName().
- En V2,
getBaseTypeName()conserva la firma de tipo completa, incluidos los tipos envoltorio (Nullable,LowCardinality) y los parámetros de tipo (FixedString(8), las definiciones completas deEnumyTuple). V1 los elimina y devuelve solo el nombre del tipo base. - Los arrays de
TupleusanOTHER (1111)en V2 en lugar deSTRUCT (2002)porque las tuplas de ClickHouse tienen campos con nombre, algo quejava.sql.Structno admite. - Los arrays de
UUIDusanOTHER (1111)en V2, en consonancia con la asignación escalar deUUID. - Los valores
Enumse asignan aVARCHAR: los miembros deenumse identifican por su nombre de cadena, independientemente de su codificación numérica subyacente.
java.sql.Connection#createArrayOf para instanciar el objeto java.sql.Array. Este objeto está diseñado para unificar el manejo de arrays en distintas bases de datos.
Se necesita una conexión para pasar la configuración al método de fábrica de Array.El método acepta dos argumentos:typeName- nombre del tipo de los elementos del array. Por ejemplo,Array(Int32)->"Int32".elements- los elementos reales del array. Por ejemplo[[1, 2, 3], [4, 5, 6]]->new Integer[][] {{1, 2, 3}, {4, 5, 6}}.
Object[] o como java.sql.Struct (consulte cómo escribir tuples más adelante).EjemploResultSet#getArray(columnIndex) para leer un objeto Array. El objeto puede utilizarse para acceder a arrays de cualquier profundidad de anidamiento.
El método Array#getResultSet() puede emplearse para leer los elementos del array de forma más uniforme como java.sql.ResultSet. Resulta útil
cuando se desconoce el tipo exacto de los elementos del array.Ejemplocom.clickhouse.data.Tuple y deben escribirse como dicho objeto mediante una llamada al método setObject(columnIndex, tuple).
Es posible usar el objeto java.sql.Struct para escribir tuples con mayor portabilidad.EjemplogetObject(columnIndex) devolverá Object[]. Los Tuples pueden leerse como java.sql.Array mediante el método getObject(columnIndex, Array.class).Ejemplojava.collections.Map porque este tipo requiere pares clave-valor (java.sql.Struct no admite pares clave-valor).Ejemplojava.collections.Map utilizando el método getObject(columnIndex, Map.class).Ejemplojava.sql.Connection#createStruct para instanciar el objeto java.sql.Struct. Este objeto está diseñado para unificar el manejo de estructuras anidadas en distintas bases de datos.
Se necesita una conexión para pasar la configuración al método de fábrica de Struct.El método acepta dos argumentos:typeName- nombre del tipo de los elementos anidados. Por ejemplo,Nested(Tuple(Int32, String))->"Nested(Tuple(Int32, String))".elements- los elementos anidados propiamente dichos. Por ejemplo[1, 'test']->new Object[] {1, 'test'}.
ResultSet#getStruct(columnIndex, StructDescriptor) para leer el objeto Nested. El objeto puede utilizarse para acceder a elementos anidados de cualquier nivel de profundidad.
El método Struct#getResultSet() puede emplearse para leer elementos anidados de forma más uniforme, como java.sql.ResultSet. Resulta útil
cuando el tipo exacto de los elementos anidados es desconocido.EjemploTipos Nullable y LowCardinality
NullableyLowCardinalityson tipos especiales que envuelven otros tipos.Nullableafecta a la forma en que se devuelven los nombres de tipo enResultSetMetaData
UUIDno es un tipo estándar de JDBC. Sin embargo, forma parte del JDK. De forma predeterminada, el métodogetObject()devuelvejava.util.UUID.UUIDpuede leerse y escribirse comoStringmediante el métodogetObject(columnIndex, String.class).IPv4eIPv6no son tipos estándar de JDBC. Sin embargo, forman parte del JDK. De forma predeterminada, el métodogetObject()devuelvejava.net.Inet4Addressyjava.net.Inet6Address.IPv4yIPv6se pueden leer y escribir comoStringmediante el métodogetObject(columnIndex, String.class).
Map<String, Object> de forma predeterminada, donde las claves son las claves del objeto JSON y los valores son los valores del objeto JSON.
Por ejemplo:jdbc_read_json_as_string=true a las propiedades de conexión.
Esto hace que el driver devuelva los valores JSON como String y permite procesarlos con cualquier biblioteca JSON.Manejo de fechas, horas y zonas horarias
Lea la Guía de Date/Time, que explica los errores más comunes y la lógica del driver al gestionar valores de Date/Time y timestamps.Creación de conexión
Suministro de credenciales y configuración
showLineNumbers
Instrucción simple
showLineNumbers
Insert
showLineNumbers
HikariCP
showLineNumbers
Más información
Para más información, consulte nuestro repositorio de GitHub y la documentación del cliente de Java.Solución de problemas
Registro
El driver utiliza slf4j para el logging y usará la primera implementación disponible en elclasspath.Resolución de timeout de JDBC en inserts de gran volumen
Al realizar inserciones grandes en ClickHouse con tiempos de ejecución prolongados, es posible que encuentre errores de timeout de JDBC como los siguientes:Mac OS
En Mac OS, se pueden ajustar los siguientes parámetros para resolver el problema:net.inet.tcp.keepidle: 60000net.inet.tcp.keepintvl: 45000net.inet.tcp.keepinit: 45000net.inet.tcp.keepcnt: 8net.inet.tcp.always_keepalive: 1
Linux
En Linux, la configuración equivalente por sí sola puede no resolver el problema. Se requieren pasos adicionales debido a las diferencias en la forma en que Linux gestiona la configuración de keep-alive de los sockets. Siga estos pasos:- Ajuste los siguientes parámetros del kernel de Linux en
/etc/sysctl.confo en un archivo de configuración relacionado:
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 (Puede considerar reducir este valor desde el valor predeterminado de 300 segundos)
- Después de modificar los parámetros del kernel, aplique los cambios ejecutando el siguiente comando:
Guía de migración
Cambios principales
- JDBC V2 se ha implementado para que sea más ligero y se han eliminado algunas funcionalidades.
- El streaming de datos no es compatible con JDBC V2 porque no forma parte de la especificación de JDBC ni de Java.
- JDBC V2 requiere una configuración explícita. No hay valores predeterminados para failover.
- El protocolo debe especificarse en la URL. No se detecta implícitamente a partir de los números de puerto.
Cambios de configuración
Solo hay dos enums:com.clickhouse.jdbc.DriverProperties- las propiedades de configuración del propio driver.com.clickhouse.client.api.ClientConfigProperties- las propiedades de configuración del cliente. Los cambios en la configuración del cliente se describen en la documentación del cliente Java.
- La URL se procesa primero para extraer las propiedades. Estas prevalecen sobre todas las demás propiedades.
- Las propiedades del driver no se pasan al cliente.
- Los endpoints (host, port, protocol) se extraen de la URL.
Cambios en los tipos de datos
Tipos numéricos- La principal diferencia es que los tipos sin signo se corresponden con tipos de Java para mejorar la portabilidad.
FixedStringse lee tal cual en ambas versiones. Por ejemplo,FixedString(10)para'John'se leerá como'John\0\0\0\0\0\0\0\0\0'.- Cuando se utiliza
PreparedStatement#setBytes, se convierte enunhex('<hex_string>')y luego se interpreta comoString. - Las cadenas se almacenan con codificación UTF-8.
TimeyTime64solo son compatibles en V2 como tipos nuevos.DateTimeyDateTime64se asignan ajava.sql.Timestamppara mejorar la compatibilidad con JDBC.
Tipos anidados
- En V2,
Arrayse asigna ajava.sql.Arrayde forma predeterminada para ser compatible con JDBC. Esto también se hace para proporcionar más información sobre el valor de array devuelto. Útil para la inferencia de tipos. - En V2,
Arrayimplementa el métodogetResultSet()para devolver unjava.sql.ResultSetcon el mismo contenido que el array original. - V1 usa
STRUCTparaMap, pero siempre devuelve un objetojava.util.Map. V2 corrige esto al mapearMapaJAVA_OBJECT. - V1 usa
STRUCTparaTuple, pero siempre devuelve un objetoList<Object>. V2 asignaTupleaOTHERy devuelveObject[]de forma predeterminada. - V2 introduce
com.clickhouse.data.Tuple#Tuplepara escribir tuplas. Simplifica la tarea de determinar si un valor es una tupla o un Array. PreparedStatement#setBytesyResultSet#getBytesno pueden usarse con tipos de colección. Estos métodos están diseñados para trabajar con cadenas binarias.- Normalmente,
java.sql.Arrayse utiliza para escribir y leer tipos Array. El controlador JDBC ofrece compatibilidad total con ello. - V2 asigna
NestedaArrayy lo presenta como un array de tuplas. - V2 ofrece compatibilidad parcial con
java.sql.Structporque es muy similar al tipoArrayy no admite pares clave-valor.Structpuede utilizarse para escribir valoresTuple.
Tipos Nullable y LowCardinality
NullableyLowCardinalityson tipos especiales que encapsulan otros tipos.- No se realizan cambios en estos tipos en la V2.
- V1 usa
VARCHARparaUUID, pero siempre devuelve un objetojava.util.UUID. V2 corrige esto al mapearUUIDaOTHERy devuelve un objetojava.util.UUID. - V1 usa
VARCHARparaIPv4eIPv6, pero siempre devuelve objetosjava.net.Inet4Addressyjava.net.Inet6Address. V2 corrige esto asignandoIPv4eIPv6aOTHERy devuelve objetosjava.net.Inet4Addressyjava.net.Inet6Address. DynamicyVariantson tipos nuevos en V2. No se admiten en V1.JSONse basa en el tipoDynamic. Por ello, solo se admite en V2.- Los valores IPv4 e IPv6 pueden leerse como
byte[]mediante el métodogetBytes(columnIndex). Sin embargo, se recomienda usar las clases específicas para estos tipos. - V2 no admite la lectura de direcciones IP como valores numéricos porque la conversión está mejor implementada en las clases InetAddress.
Cambios en los metadatos de la base de datos
- V2 usa únicamente el término
Schemapara referirse a las bases de datos. El términoCatalogqueda reservado para usos futuros. - V2 devuelve
falseparaDatabaseMetaData.supportsTransactions()yDatabaseMetaData.supportsSavepoints(). Esto cambiará en el futuro. - En
DatabaseMetaData.getTypeInfo(), las columnasLITERAL_PREFIXyLITERAL_SUFFIXahora devuelvennullpara los tipos de datos en los que no se esperan prefijos ni sufijos (por ejemplo, los tipos numéricos). En V1, estas columnas devolvían valores no nulos para esos tipos. Estas columnas deben usarse al generar consultas SQL para entrecomillar correctamente los valores literales según su tipo de dato.