clickhouse-jdbc implementa a interface JDBC padrão usando o cliente Java mais recente.
Recomendamos usar diretamente o cliente Java mais recente se o desempenho/acesso direto for crítico.Requisitos de ambiente
- OpenJDK versão >= 8
Setup
- Maven
- Gradle (Kotlin)
- Gradle
- Maven Central e adicione-o ao classpath
- a partir da versão
0.9.4, há um artefato em https://mvnrepository.com/artifact/com.clickhouse/clickhouse-jdbc-all - use o qualificador
allpara obter o jar com todas as dependências empacotadas (shaded).
- a partir da versão
- ou no repositório oficial aqui
Configuração
Classe do Driver:com.clickhouse.jdbc.ClickHouseDrivercom.clickhouse.jdbc.ClickHouseDriver é uma classe de fachada para as implementações JDBC nova e antiga. Ela usa a nova implementação JDBC por padrão.
Você pode usar a implementação JDBC antiga definindo a propriedade system clickhouse.jdbc.v1 como true. Essa propriedade deve ser definida antes de chamar
a classe Driver.Uma forma alternativa de alternar entre as versões é usar diretamente as classes Driver de cada versão:com.clickhouse.jdbc.Driveré a nova implementação JDBC (V2).com.clickhouse.jdbc.DriverV1é a implementação JDBC antiga (V1).
jdbc:(ch|clickhouse)[:<protocol>]://endpoint[:port][/<database>][?param1=value1¶m2=value2][#tag1,tag2,...], por exemplo:jdbc:clickhouse:http://localhost:8123jdbc:clickhouse:https://localhost:8443?ssl=true
- apenas um endpoint é permitido na URL
- o protocolo deve ser especificado quando não for o padrão - ‘HTTP’
- a porta deve ser especificada quando não for a porta padrão ‘8123’
- o driver não detecta automaticamente o protocol pela porta, você precisa especificá-lo explicitamente
- O parâmetro
sslnão é necessário quando o protocolo é informado.
Propriedades de Conexão
Os principais parâmetros de configuração são definidos no java client. Eles devem ser repassados sem modificações ao driver. O driver possui algumas propriedades próprias que não fazem parte da configuração do cliente; elas estão listadas abaixo.Propriedades do driver:Configurações do servidorTodas as configurações do servidor devem ser prefixadas com
clickhouse_setting_ (assim como na configuração do cliente).readonly=2.
Leia mais sobre perfis read-only aqui.Identificação do Cliente
Há duas maneiras de identificar a aplicação que originou uma requisição: definircom.clickhouse.client.api.ClientConfigProperties#CLIENT_NAME por meio das
propriedades de conexão ou utilizar o método java.sql.Connection#setClientInfo(String name, String value).showLineNumbers
showLineNumbers
http_user_agent no log de consultas:app_name/version para a propriedade client_name, pois isso ajuda a identificar a aplicação no log de consultas.Identificação de Operação
O driver JDBC gera umquery_id para cada operação (atualmente incluído nas exceções do servidor).Para definir log_comment em uma operação, utilize o método com.clickhouse.jdbc.StatementImpl#getLocalSettings. Para isso, é necessário realizar o cast de Statement ou PreparedStatement para com.clickhouse.jdbc.StatementImpl primeiro.showLineNumbers
localSettings é compartilhado entre as threads.Tipos de dados suportados
O JDBC driver suporta os mesmos formatos de dados que o java client subjacente.Mapeamento de Tipos JDBC
O seguinte mapeamento se aplica a:ResultSet#getObject(columnIndex)- o método retorna um objeto da classe Java correspondente. (Int8->java.lang.Byte,Int16->java.lang.Short, etc.)ResultSetMetaData#getColumnType(columnIndex)- o método retorna o tipo JDBC correspondente. (Int8->java.lang.Byte,Int16->java.lang.Short, etc.)
ResultSet#getObject(columnIndex, class)- o método tentará converter o valor para o tipoclass. Há algumas limitações de conversão. Consulte cada seção para mais detalhes.
- tipos numéricos são conversíveis entre si. Portanto,
Int8pode ser convertido emFloat64e vice-versa.:rs.getObject(1, Float64.class)retornará um valorFloat64da colunaInt8.rs.getLong(1)retornará um valorLongda colunaInt8.rs.getByte(1)pode retornar um valorByteda colunaInt16se ele couber emByte.
- a conversão de um tipo mais amplo para um mais restrito não é recomendada devido ao risco de corrupção de dados.
- O tipo
Booltambém funciona como número. - Todos os tipos numéricos podem ser lidos como
java.lang.String. - Armazenar
Float.MAX_VALUEdo Java comoFloatcausa um problema (https://github.com/ClickHouse/clickhouse-java/issues/809). Salvar o mesmo valor comoDoubleresolve esse problema.
Stringsó pode ser lida comojava.lang.Stringoubyte[].FixedStringé lido como está e será preenchido com zeros até atingir o comprimento da coluna. (Por exemplo,FixedString(10)para'John'será lido como'John\0\0\0\0\0\0\0\0\0'.)
Enum8eEnum16são mapeados parajava.lang.Stringpor padrão.- Valores de
enumpodem ser lidos como valores numéricos usando o método getter apropriado ou o métodogetObject(columnIndex, Integer.class). Enum16é mapeado internamente para short, e Enum8 é mapeado para byte. Deve-se evitar lerEnum16como byte devido ao risco de corrupção de dados.- Valores de enum podem ser definidos como string ou valor numérico em
PreparedStatement.
- Os tipos Date / Time são mapeados para tipos
java.sqlpara garantir melhor compatibilidade com JDBC. No entanto, é possível obterjava.time.LocalDate,java.time.LocalDateTimeejava.time.LocalTimeusandoResultSet#getObject(columnIndex, Class<T>)com a classe correspondente como segundo argumento.rs.getObject(1, java.time.LocalDate.class)retorna o valorjava.time.LocalDateda colunaDate.rs.getObject(1, java.time.LocalDateTime.class)retorna o valorjava.time.LocalDateTimeda colunaDateTime.rs.getObject(1, java.time.LocalTime.class)retorna o valorjava.time.LocalTimeda colunaTime.
Date,Date32,Time,Time64não são afetados pelo fuso horário do servidor.DateTimeeDateTime64são afetados pelo fuso horário do servidor ou da sessão.DateTimeeDateTime64podem ser obtidos comoZonedDateTimeusandogetObject(colIndex, ZonedDateTime.class).
Arrayé mapeado parajava.sql.Arraypor padrão para garantir compatibilidade com JDBC. Isso também é feito para fornecer mais informações sobre o valor do array retornado. É útil para inferência de tipos.Arrayimplementa o métodogetResultSet()para retornar umjava.sql.ResultSetcom o mesmo conteúdo do array original.- Tipos de coleção não devem ser lidos como
java.lang.String, porque essa não é uma forma válida de representar os dados (ex.: em arrays, não há aspas para valores de string). Mapé mapeado paraOTHERporque o valor só pode ser lido com o métodogetObject(columnIndex, Class<T>).Mapnão é umjava.sql.Structporque não possui colunas nomeadas.
Tupleé mapeado paraObject[]porque pode conter diferentes tipos, e usarListnão é válido.Tuplepode ser interpretado comoArrayusando o métodogetObject(columnIndex, Array.class). Nesse caso,Array#baseTypeNameretornará a definição da colunaTuple.
Array.getBaseTypeName() retorna o nome do tipo de elemento do ClickHouse; Array.getBaseType() retorna o código de tipo JDBC.
O JDBC V2 preserva as assinaturas completas de tipo (tipos encapsulados, parâmetros de tipo) que o V1 remove.As regras gerais de mapeamento para arrays são:Notes on the rules above:
- Tipos encapsuladores (
Nullable,LowCardinality) são preservados emgetBaseTypeName(), masgetBaseType()é resolvido para o código JDBC do tipo interno. - Arrays aninhados são achatados nos metadados:
getBaseTypeName()retorna o tipo do elemento mais interno que não é array, e não o filho imediato. - Tipos parametrizados (
FixedString(N), definições completas deEnum/Tuple) mantêm seus parâmetros emgetBaseTypeName().
- Na V2,
getBaseTypeName()preserva a assinatura completa do tipo, incluindo tipos encapsuladores (Nullable,LowCardinality) e parâmetros de tipo (FixedString(8), definições completas deEnumeTuple). A V1 remove esses elementos e retorna apenas o nome do tipo básico. - Arrays de
TupleusamOTHER (1111)na V2 em vez deSTRUCT (2002)porque as tuplas do ClickHouse têm campos nomeados, o quejava.sql.Structnão suporta. - Arrays de
UUIDusamOTHER (1111)na V2, seguindo o mesmo mapeamento escalar deUUID. - Valores de
Enumsão mapeados paraVARCHAR— os membros do enum são identificados pelo nome da string, independentemente da codificação numérica subjacente.
java.sql.Connection#createArrayOf para instanciar o objeto java.sql.Array. Esse objeto foi projetado para unificar o tratamento de arrays em diferentes bancos de dados.
A conexão é necessária para passar a configuração ao método de fábrica do Array.O método aceita dois argumentos:typeName- nome do tipo dos elementos da matriz. Por exemplo,Array(Int32)->"Int32".elements- elementos do array propriamente ditos. Por exemplo[[1, 2, 3], [4, 5, 6]]->new Integer[][] {{1, 2, 3}, {4, 5, 6}}.
Object[] ou como java.sql.Struct (veja como escrever tuples abaixo).ExemploResultSet#getArray(columnIndex) para ler o objeto Array. O objeto pode ser usado para acessar arrays de qualquer nível de aninhamento.
O método Array#getResultSet() pode ser usado para ler elementos de array de forma mais unificada como java.sql.ResultSet. Isso é útil
quando o tipo exato dos elementos do array não é conhecido.Exemplocom.clickhouse.data.Tuple e devem ser escritos como esse objeto por meio da chamada ao método setObject(columnIndex, tuple).
É possível usar o objeto java.sql.Struct para escrever tuples com maior portabilidade.ExemplogetObject(columnIndex) retornará Object[]. Tuples podem ser lidas como java.sql.Array usando o método getObject(columnIndex, Array.class).Exemplojava.collections.Map porque esse tipo exige pares chave-valor (java.sql.Struct não suporta pares chave-valor).Exemplojava.collections.Map usando o método getObject(columnIndex, Map.class).Exemplojava.sql.Connection#createStruct para instanciar o objeto java.sql.Struct. Esse objeto foi projetado para unificar o tratamento de tipos aninhados em diferentes bancos de dados.
A conexão é necessária para passar a configuração ao método de fábrica do Struct.O método aceita dois argumentos:typeName- nome do tipo dos elementos internos. Por exemplo,Nested(Tuple(Int32, String))->"Nested(Tuple(Int32, String))".elements- os elementos aninhados reais. Por exemplo,[1, 'test']->new Object[] {1, 'test'}.
ResultSet#getStruct(columnIndex, StructDescriptor) para ler o objeto Nested. O objeto pode ser usado para acessar elementos aninhados em qualquer nível de profundidade.
O método Struct#getResultSet() pode ser usado para ler elementos aninhados de forma mais unificada como java.sql.ResultSet. Isso é útil
quando o tipo exato dos elementos aninhados não é conhecido.ExemploTipos Nullable e LowCardinality
NullableeLowCardinalitysão tipos especiais que envolvem outros tipos.Nullableafeta como os nomes dos tipos são retornados emResultSetMetaData
UUIDnão é um tipo padrão do JDBC. No entanto, faz parte do JDK. Por padrão,java.util.UUIDé retornado pelo métodogetObject().UUIDpode ser lido e gravado comoStringusando o métodogetObject(columnIndex, String.class).IPv4eIPv6não são tipos padrão do JDBC. No entanto, fazem parte do JDK. Por padrão,java.net.Inet4Addressejava.net.Inet6Addresssão retornados pelo métodogetObject().IPv4eIPv6podem ser lidos/gravados comoStringpor meio do métodogetObject(columnIndex, String.class).
Map<String, Object> por padrão, onde as chaves são as chaves do objeto JSON e os valores são os valores do objeto JSON.
Por exemplo:jdbc_read_json_as_string=true nas propriedades de conexão.
Isso faz com que o driver retorne valores JSON como String, que podem ser analisados usando qualquer biblioteca JSON.Tratamento de Datas, Horas e Fusos Horários
Leia o Guia de Date/Time, que explica armadilhas comuns e a lógica do driver ao lidar com Date/Time e timestamps.Criando Conexão
Fornecendo Credenciais e Configurações
showLineNumbers
Instrução Simples
showLineNumbers
Insert
showLineNumbers
HikariCP
showLineNumbers
Mais Informações
Para mais informações, consulte nosso repositório do GitHub e a documentação do Java Client.Solução de problemas
Logging
O driver usa slf4j para logging e usará a primeira implementação disponível noclasspath.Resolvendo Timeout JDBC em Inserts de Grande Volume
Ao realizar grandes inserções no ClickHouse com longos tempos de execução, você pode se deparar com erros de timeout JDBC como:Mac OS
No macOS, as seguintes configurações podem ser ajustadas para resolver o problema:net.inet.tcp.keepidle: 60000net.inet.tcp.keepintvl: 45000net.inet.tcp.keepinit: 45000net.inet.tcp.keepcnt: 8net.inet.tcp.always_keepalive: 1
Linux
No Linux, as configurações equivalentes por si só podem não resolver o problema. Etapas adicionais são necessárias devido às diferenças na forma como o Linux trata as configurações de keep-alive de socket. Siga estas etapas:- Ajuste os seguintes parâmetros do kernel do Linux em
/etc/sysctl.confou em um arquivo de configuração 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 (Você pode considerar reduzir esse valor em relação ao valor padrão de 300 segundos)
- Após modificar os parâmetros do kernel, aplique as alterações executando o seguinte comando:
Guia de Migração
Principais Alterações
- O JDBC V2 foi implementado para ser mais leve e algumas funcionalidades foram removidas.
- Dados de streaming não têm suporte no JDBC V2 porque não fazem parte da especificação do JDBC nem do Java.
- O JDBC V2 exige configuração explícita. Não há failover por padrão.
- O protocolo deve ser especificado na URL. Sem detecção implícita do protocolo com base em números de porta.
Alterações de Configuração
Existem apenas dois enums:com.clickhouse.jdbc.DriverProperties- as propriedades de configuração do driver.com.clickhouse.client.api.ClientConfigProperties- as propriedades de configuração do cliente. As alterações na configuração do cliente são descritas na documentação do cliente Java.
- A URL é analisada primeiro para identificar as propriedades. Elas sobrescrevem todas as demais propriedades.
- As propriedades do driver não são enviadas ao cliente.
- Os endpoints (host, port, protocol) são extraídos da URL.
Alterações nos Tipos de Dados
Tipos Numéricos- A principal diferença é que os tipos sem sinal são mapeados para tipos Java para melhor portabilidade.
FixedStringé lido exatamente como está em ambas as versões. Por exemplo,FixedString(10)para'John'será lido como'John\0\0\0\0\0\0\0\0\0'.- Quando
PreparedStatement#setBytesfor usado, ele será convertido emunhex('<hex_string>')e depois lido comoString. - Strings são armazenadas na codificação UTF-8.
TimeeTime64têm suporte no V2 apenas como novos tipos.DateTimeeDateTime64são mapeados parajava.sql.Timestamppara garantir melhor compatibilidade com JDBC.
Tipos Aninhados
- Na V2,
Arrayé mapeado parajava.sql.Arraypor padrão para manter a compatibilidade com o JDBC. Isso também é feito para fornecer mais informações sobre o valor de array retornado. Útil para inferência de tipos. - Na V2,
Arrayimplementa o métodogetResultSet()para retornar umjava.sql.ResultSetcom o mesmo conteúdo do array original. - A V1 usa
STRUCTparaMap, mas sempre retorna um objetojava.util.Map. A V2 corrige isso ao mapearMapparaJAVA_OBJECT. - A V1 usa
STRUCTparaTuple, mas sempre retorna umaList<Object>. A V2 mapeiaTupleparaOTHERe retornaObject[]por padrão. - A V2 introduz
com.clickhouse.data.Tuple#Tuplepara gravar tuplas. Isso facilita determinar se o valor é uma tupla ou um array. PreparedStatement#setByteseResultSet#getBytesnão podem ser usados com tipos de coleção. Esses métodos foram concebidos para funcionar com strings binárias.- Normalmente,
java.sql.Arrayé usado para gravar e ler tipos Array. O driver JDBC oferece suporte completo a isso. - O V2
Nestedé mapeado paraArraye é apresentado como um array de tuplas. - O V2 tem suporte parcial a
java.sql.Structporque é muito semelhante ao tipo Array e não oferece suporte a pares chave-valor.Structpode ser usado para escrever valoresTuple.
Tipos Nullable e LowCardinality
NullableeLowCardinalitysão tipos especiais que envolvem outros tipos.- Nenhuma alteração é feita nesses tipos na V2.
- A V1 usa
VARCHARparaUUID, mas sempre retorna um objetojava.util.UUID. A V2 corrige isso mapeandoUUIDparaOTHERe retornando um objetojava.util.UUID. - A V1 usa
VARCHARparaIPv4eIPv6, mas sempre retorna objetosjava.net.Inet4Addressejava.net.Inet6Address. A V2 corrige isso ao mapearIPv4eIPv6paraOTHERe retornar objetosjava.net.Inet4Addressejava.net.Inet6Address. DynamiceVariantsão novos tipos na V2. Não são compatíveis com a V1.JSONbaseia-se no tipoDynamic. Portanto, tem suporte apenas no V2.- Os valores de IPv4 e IPv6 podem ser lidos como
byte[]usando o métodogetBytes(columnIndex). No entanto, recomenda-se usar classes específicas para esses tipos. - V2 não oferece suporte à leitura de endereços IP como valores numéricos, porque essa conversão é implementada de forma mais adequada nas classes InetAddress.
Alterações de Metadados do Banco de Dados
- O V2 usa apenas o termo
Schemapara nomear bancos de dados. O termoCatalogfica reservado para uso futuro. - A V2 retorna
falseparaDatabaseMetaData.supportsTransactions()eDatabaseMetaData.supportsSavepoints(). Isso será alterado em versões futuras. - Em
DatabaseMetaData.getTypeInfo(), as colunasLITERAL_PREFIXeLITERAL_SUFFIXagora retornamnullpara tipos de dados nos quais não se esperam prefixos e sufixos (por exemplo, tipos numéricos). Na V1, essas colunas retornavam valores não nulos para esses tipos. Essas colunas devem ser usadas ao gerar consultas SQL para delimitar corretamente valores literais de acordo com seu tipo de dado.