Todos os produtos
Search
Central de documentação

ApsaraDB for ClickHouse:Connect using JDBC

Última atualização: Jun 28, 2026

Este guia orienta você na conexão com um cluster do ApsaraDB for ClickHouse usando Java Database Connectivity (JDBC) em um projeto Maven. Ao final, você terá uma conexão funcional com pool de conexões HikariCP, uma tabela criada e dados inseridos simultaneamente em múltiplas threads.

Pré-requisitos

Antes de começar, verifique se você possui:

Se o servidor da aplicação e o cluster estiverem em VPCs diferentes, resolva primeiro o problema de conectividade de rede. Consulte Como resolver problemas de conectividade de rede entre um cluster de destino e uma fonte de dados? Como alternativa, solicite um endpoint público. Consulte Solicitar e liberar um endpoint público .

Etapa 1: Adicionar dependências do Maven

Adicione as seguintes dependências ao arquivo pom.xml:

<dependency>
    <groupId>com.zaxxer</groupId>
    <artifactId>HikariCP</artifactId>
    <version>3.4.5</version>
</dependency>
<dependency>
    <groupId>com.clickhouse</groupId>
    <artifactId>clickhouse-jdbc</artifactId>
    <version>0.4.6</version>
</dependency>
<dependency>
    <groupId>org.lz4</groupId>
    <artifactId>lz4-java</artifactId>
    <version>1.8.0</version>
</dependency>
<dependency>
    <groupId>org.apache.httpcomponents.client5</groupId>
    <artifactId>httpclient5</artifactId>
    <version>5.2.1</version>
</dependency>

Etapa 2: Entender o formato da URL JDBC

A URL JDBC segue este padrão:

jdbc:clickhouse:<protocol>://<endpoint>/<database>

Por exemplo:

jdbc:clickhouse:http://cc-bp128o64g****ky35-clickhouse.clickhouseserver.rds.aliyuncs.com:8123/default

Pontos importantes:

  • Especifique o protocolo explicitamente — o driver não o infere pelo número da porta.

  • O protocolo padrão é HTTP e a porta padrão é 8123. Use a porta 8123, a menos que utilize uma porta personalizada.

  • O formato do endpoint é VPC_ENDPOINT:8123, onde VPC_ENDPOINT corresponde ao endpoint de VPC ou ao endpoint público do cluster.

Etapa 3: Escrever o código da aplicação

Funcionamento

O código de exemplo segue este fluxo:

  1. Constrói um HikariDataSource com configurações de pool de conexões e propriedades JDBC específicas do ClickHouse.

  2. Crie uma tabela — uma única tabela MergeTree para clusters Enterprise Edition ou uma tabela local mais uma tabela Distributed para clusters Community Edition.

  3. Insere dados simultaneamente em 5 threads, cada uma inserindo 10 lotes de 10.000 linhas.

  4. Conta o total de linhas na tabela para verificar as inserções.

Parâmetros de conexão

Substitua os valores de espaço reservado no código pelos valores reais do cluster.

Parâmetro

Descrição

Exemplo

YOUR_INSTANCE_PROTOCOL

Protocolo de conexão. Valor fixo: "http".

http

YOUR_INSTANCE_ENDPOINT

Endpoint. Formato: VPC_ENDPOINT:8123

cc-bp128o64g****ky35-clickhouse.clickhouseserver.rds.aliyuncs.com:8123

DATABASE

Banco de dados de destino da conexão

default

YOUR_INSTANCE_USER

Conta do banco de dados

test

YOUR_INSTANCE_PASSWORD

Senha da conta do banco de dados

Password****

ENTERPRISE

Engine de tabela a ser utilizada. true para clusters Enterprise Edition, false para clusters Community Edition

true

INSERT_BATCH_SIZE

Quantidade de linhas por lote

10000

INSERT_BATCH_NUM

Quantidade de lotes por thread

10

INSERT_OPTIMIZE_LEVEL

Nível de otimização de inserção. Valores válidos: 1, 2, 3. Quanto maior, mais rápido: 3 > 2 > 1

3

Níveis de otimização de inserção

Todos os três níveis utilizam prepared statements. Escolha conforme seus requisitos de portabilidade:

Nível

Padrão SQL

Velocidade

Portável

1

INSERT INTO ... VALUES (?, ?)

Base

Sim — JDBC padrão

2

INSERT INTO ... SELECT ... FROM input(...)

Mais rápido

Não — específico do ClickHouse

3

INSERT INTO ... FORMAT RowBinary

Mais rápido de todos

Não — específico do ClickHouse, requer serialização manual

Código de exemplo completo

O ponto de entrada é o método main. Antes de executar, atualize as constantes no início da classe com os valores do cluster.

package com.aliyun;

import com.clickhouse.jdbc.ClickHouseDataSource;
import com.clickhouse.data.ClickHouseOutputStream;
import com.clickhouse.data.ClickHouseWriter;
import com.zaxxer.hikari.HikariConfig;
import com.zaxxer.hikari.HikariDataSource;

import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.Statement;
import java.util.Properties;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class Main {
    private final static String YOUR_INSTANCE_PROTOCOL = "http";
    private final static String YOUR_INSTANCE_ENDPOINT = "VPC_ENDPOINT:8123"; // YOUR CONFIG HERE
    private final static String DATABASE = "default"; // YOUR CONFIG HERE
    private final static String YOUR_INSTANCE_USER = "USER"; // YOUR CONFIG HERE
    private final static String YOUR_INSTANCE_PASSWORD = "PASSWORD"; // YOUR CONFIG HERE
    private final static String JDBC_URL = "jdbc:clickhouse:%s://%s/%s";
    private final static Integer INSERT_BATCH_SIZE = 10000;
    private final static Integer INSERT_BATCH_NUM = 10;
    private final static boolean ENTERPRISE = true; // YOUR CONFIG HERE
    private final static Integer INSERT_OPTIMIZE_LEVEL = 3;

    public static void main(String[] args) {
        try {
            HikariConfig conf = buildHikariDataSource();
            try(HikariDataSource ds = new HikariDataSource(conf)) {
                // Create a table.
                Connection conn = ds.getConnection();
                createTable(conn);
                conn.close();

                // Concurrently insert data.
                int concurrentNum = 5;
                CountDownLatch countDownLatch = new CountDownLatch(concurrentNum);
                ExecutorService executorService = Executors.newFixedThreadPool(concurrentNum);
                for (int i = 0; i < concurrentNum; i++) {
                    executorService.submit(() -> {
                        System.out.printf("[%d] Thread starts inserting\n", Thread.currentThread().getId());
                        try(Connection connection = ds.getConnection()) {
                            batchInsert(connection, INSERT_OPTIMIZE_LEVEL);
                        } catch (Exception e) {
                            e.printStackTrace();
                        } finally {
                            System.out.printf("[%d] Thread stops inserting\n", Thread.currentThread().getId());
                            countDownLatch.countDown();
                        }
                    });
                }
                // Wait for all threads to finish.
                countDownLatch.await();

                // Count the table.
                conn = ds.getConnection();
                count(conn);
                conn.close();
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * Generate the JDBC URL.
     * @param protocol The protocol. Supported protocols include http, https, and grpc.
     * @param endpoint The endpoint.
     * @return The JDBC URL.
     */
    public static String getJdbcUrl(String protocol, String endpoint, String database) {
        return String.format(JDBC_URL, protocol, endpoint, database);
    }

    /**
     * Build HikariDataSource.
     * @return The HikariConfig.
     */
    public static HikariConfig buildHikariDataSource() throws Exception {
        HikariConfig conf = new HikariConfig();

        // Properties
        Properties properties = new Properties();
        /// Socket keepalive
        properties.setProperty("socket_keepalive", "true");
        properties.setProperty("http_connection_provider", "APACHE_HTTP_CLIENT");
        /// Socket timeout
        properties.setProperty("socket_timeout", "120000");
        /// Timezone
        properties.setProperty("use_server_time_zone", "true");

        // Data source configuration
        conf.setDataSource(new ClickHouseDataSource(getJdbcUrl(YOUR_INSTANCE_PROTOCOL, YOUR_INSTANCE_ENDPOINT, DATABASE), properties));
        conf.setUsername(YOUR_INSTANCE_USER);
        conf.setPassword(YOUR_INSTANCE_PASSWORD);

        // Connection pool configuration
        conf.setMaximumPoolSize(10);
        conf.setMinimumIdle(5);
        conf.setIdleTimeout(30000);
        conf.setMaxLifetime(60000);
        conf.setConnectionTimeout(30000);
        conf.setPoolName("HikariPool");

        return conf;
    }

    /**
     * Create a table.
     * @param conn The ClickHouse connection.
     * @throws Exception
     */
    public static void createTable(Connection conn) throws Exception {
        try(Statement statement = conn.createStatement()) {
            if (ENTERPRISE) {
                statement.execute("CREATE TABLE IF NOT EXISTS `default`.`test` ON CLUSTER default (id Int64, name String) ENGINE = MergeTree() ORDER BY id;");
            } else {
                // Create a local table.
                statement.execute("CREATE TABLE IF NOT EXISTS `default`.`test_local` ON CLUSTER default (id Int64, name String) ENGINE = MergeTree() ORDER BY id;");
                // Create a distributed table.
                statement.execute("CREATE TABLE IF NOT EXISTS `default`.`test` ON CLUSTER default (id Int64, name String) ENGINE = Distributed(default, default, test_local, rand());");
            }
        }
    }

    /**
     * Insert data in batches.
     * @param conn The ClickHouse connection.
     * @param optimizeLevel The insert optimization level. 3 is faster than 2, and 2 is faster than 1.<br/>
     *                      1: insert into `default`.`test` (id, name) values(?, ?) -- with an additional query to get the table structure.
     *                         This is portable.<br/>
     *                      2: insert into `default`.`test` select id, name from input('id Int64, name String') -- effectively converts and inserts data sent to the server
     *                         with a given structure into the table with another structure. This is NOT portable because it is limited to ClickHouse.<br/>
     *                      3: insert into `default`.`test` format RowBinary -- fastest (close to the Java client) with streaming mode but requires manual serialization.
     *                         This is NOT portable because it is limited to ClickHouse.
     * @throws Exception
     */
    public static void batchInsert(Connection conn, int optimizeLevel) throws Exception {
        PreparedStatement preparedStatement = null;
        try {
            // Prepared statement
            switch (optimizeLevel) {
                case 1:
                    preparedStatement = conn.prepareStatement("insert into `default`.`test` (id, name) values(?, ?)");
                    break;
                case 2:
                    preparedStatement = conn.prepareStatement("insert into `default`.`test` select id, name from input('id Int64, name String')");
                    break;
                case 3:
                    preparedStatement = conn.prepareStatement("insert into `default`.`test` format RowBinary");
                    break;
                default:
                    throw new IllegalArgumentException("optimizeLevel must be 1, 2 or 3");
            }

            // Insert data.
            long randBase = (long) (Math.random() * 1000000); // A random number to prevent data duplication and loss.
            for (int i = 0; i < INSERT_BATCH_NUM; i++) {
                long insertStartTime = System.currentTimeMillis();
                switch (optimizeLevel) {
                    case 1:
                    case 2:
                        for (int j = 0; j < INSERT_BATCH_SIZE; j++) {
                            long id = (long) i * INSERT_BATCH_SIZE + j + randBase;
                            preparedStatement.setLong(1, id);
                            preparedStatement.setString(2, "name" + id);
                            preparedStatement.addBatch();
                        }
                        preparedStatement.executeBatch();
                        break;
                    case 3:
                        class MyClickHouseWriter implements ClickHouseWriter {
                            int batchIndex = 0;
                            public MyClickHouseWriter(int batchIndex) {
                                this.batchIndex = batchIndex;
                            }
                            @Override
                            public void write(ClickHouseOutputStream clickHouseOutputStream) throws IOException {
                                for (int j = 0; j < INSERT_BATCH_SIZE; j++) {
                                    long id = (long) batchIndex * INSERT_BATCH_SIZE + j + randBase;
                                    // Write id (Int64).
                                    ByteBuffer buffer = ByteBuffer.allocate(Long.BYTES);
                                    buffer.order(ByteOrder.LITTLE_ENDIAN);
                                    buffer.putLong(id);
                                    clickHouseOutputStream.write(buffer.array());
                                    // Write name (String).
                                    clickHouseOutputStream.writeUnicodeString("name" + id);
                                }
                            }
                        }
                        preparedStatement.setObject(1, new MyClickHouseWriter(i));
                        preparedStatement.executeUpdate();
                        break;
                }

                System.out.printf("[%d] optimizeLevel=%d, insert batch [%d/%d] succeeded, cost %d ms\n",
                        Thread.currentThread().getId(), optimizeLevel, i + 1, INSERT_BATCH_NUM, System.currentTimeMillis() - insertStartTime);
            }
        } finally {
            if (preparedStatement != null) {
                preparedStatement.close();
            }
        }

    }

    /**
     * Count the table.
     * @param conn The ClickHouse connection.
     * @throws Exception
     */
    public static void count(Connection conn) throws Exception {
        try(Statement statement = conn.createStatement()) {
            ResultSet resultSet = statement.executeQuery("SELECT count() as cnt FROM `default`.`test`");
            if (resultSet.next()) {
                System.out.printf("Table `default`.`test` has %d rows\n", resultSet.getInt("cnt"));
            } else {
                throw new RuntimeException("Failed to count table `default`.`test`");
            }
        }
    }
}

Execute o código

Compile e execute a partir da raiz do projeto:

mvn compile && mvn exec:java -Dexec.mainClass="com.aliyun.Main"

Se a conexão for bem-sucedida e as inserções forem concluídas, a saída terminará com uma linha semelhante a:

Table `default`.`test` has 500000 rows

Baixe o projeto completo

Clique em awesome-clickhouse-jdbc-0.2.1.zip para baixar o projeto de exemplo.

O projeto contém dois subprojetos:

image

Subprojeto

Descrição

native-example

Utiliza HikariCP e JDBC padrão com uma única classe Main. Ideal para aprender sobre conectividade JDBC ou executar testes básicos de desempenho.

mybatis-hikari-example

Emprega HikariCP, MyBatis (ORM) e JDBC padrão, com uma estrutura completa de camadas entity-mapper-service. Recomendado para integração do ClickHouse em projetos baseados em MyBatis.

Configure mybatis-hikari-example

A lógica geral é a mesma do native-example. Defina os parâmetros abaixo antes de executar:

Arquivo Parâmetro Descrição Exemplo
src/main/resources/application.yml url URL de conexão JDBC. Formato: jdbc:clickhouse:http://VPC_ENDPOINT:8123 jdbc:clickhouse:http://cc-bp128o64g****ky35-clickhouse.clickhouseserver.rds.aliyuncs.com:8123
username Conta do banco de dados test
password Senha da conta do banco de dados Password****
src/main/java/com/aliyun/Main.java INSERT_BATCH_SIZE Quantidade de linhas por lote 10000
INSERT_BATCH_NUM Quantidade de lotes a serem inseridos 10
ENTERPRISE true para clusters Enterprise Edition, false para clusters Community Edition true
INSERT_OPTIMIZE_LEVEL Nível de otimização de inserção. Valores válidos: 1, 2, 3. Quanto maior, mais rápido: 3 > 2 > 1 3

native-example

O ponto de entrada do código e todas as configurações de parâmetros deste projeto estão em src/main/java/com/aliyun/Main.java. Para mais informações, consulte Etapa 3: Escrever o código da aplicação.

Solução de problemas

Tempo limite de conexão

Verifique os itens abaixo nesta ordem:

  1. Lista de permissões: Confirme se o endereço IP do servidor da aplicação foi adicionado à lista de permissões do cluster. Consulte Definir uma lista de permissões.

  2. Rede: Verifique se a aplicação e o cluster estão na mesma VPC.

  3. Endpoint e porta: Valide se o endpoint está correto e se a porta é 8123.

Tempo limite de leitura

Esse erro geralmente ocorre durante inserções grandes com tempos de execução prolongados. Configure os parâmetros de keepalive TCP do sistema operacional e defina as seguintes propriedades JDBC, conforme demonstrado no código de exemplo:

properties.setProperty("socket_keepalive", "true");
properties.setProperty("http_connection_provider", "APACHE_HTTP_CLIENT");

Ambas as configurações já estão incluídas no código de exemplo. Para mais informações, consulte Solução de problemas.

HikariPool — conexão não disponível

Feche a conexão após o uso. O código de exemplo utiliza try-with-resources para fechar conexões automaticamente — adote o mesmo padrão no seu código.

Próximos passos

Conecte-se ao cluster usando outras ferramentas:

Referências