Todos los productos
Search
Centro de documentos

ApsaraDB for ClickHouse:Connect using JDBC

Última actualización: Sep 05, 2026

Esta guía explica cómo conectarse a un clúster de ApsaraDB for ClickHouse mediante Java Database Connectivity (JDBC) en un proyecto Maven. Al finalizar, contará con una conexión funcional que utiliza un pool de conexiones HikariCP, una tabla creada y datos insertados de forma concurrente en varios hilos.

Requisitos previos

Antes de empezar, asegúrese de cumplir con lo siguiente:

  • La dirección IP del servidor de su aplicación debe estar agregada a la lista de permitidos del clúster. Consulte Set a whitelist

  • Debe contar con una cuenta de base de datos y su contraseña. Consulte Create an account

  • Tener instalados Maven 3.9.6 y JDK 1.8

Si el servidor de su aplicación y el clúster se encuentran en distintas VPC, resuelva primero el problema de conectividad de red. Consulte How do I resolve network connectivity issues between a destination cluster and a data source? Como alternativa, solicite un endpoint público. Consulte Apply for and release a public endpoint .

Paso 1: Agregar dependencias de Maven

Agregue las siguientes dependencias a su archivo 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>

Paso 2: Comprender el formato de la URL JDBC

La URL JDBC sigue este patrón:

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

Por ejemplo:

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

Puntos clave:

  • El protocolo debe especificarse explícitamente, ya que el controlador no lo infiere a partir del número de puerto.

  • El protocolo predeterminado es HTTP y el puerto predeterminado es 8123. Especifique el puerto 8123 a menos que utilice un puerto personalizado.

  • El formato del endpoint es VPC_ENDPOINT:8123, donde VPC_ENDPOINT corresponde al endpoint de VPC o al endpoint público de su clúster.

Paso 3: Escribir el código de la aplicación

Funcionamiento

El código de ejemplo sigue este flujo:

  1. Construye un objeto HikariDataSource con la configuración del pool de conexiones y las propiedades JDBC específicas de ClickHouse.

  2. Crea una tabla: una única tabla MergeTree para clústeres Enterprise Edition, o bien una tabla local más una tabla Distributed para clústeres Community Edition.

  3. Inserta datos de forma concurrente en 5 hilos; cada uno inserta 10 lotes de 10,000 filas.

  4. Cuenta el total de filas en la tabla para verificar las inserciones.

Parámetros de conexión

Reemplace los valores de marcador de posición en el código con los valores reales de su clúster.

Parámetro

Descripción

Ejemplo

YOUR_INSTANCE_PROTOCOL

Protocolo de conexión. El valor es fijo: "http".

http

YOUR_INSTANCE_ENDPOINT

Endpoint. Formato: VPC_ENDPOINT:8123

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

DATABASE

Base de datos a la que se conectará

default

YOUR_INSTANCE_USER

Cuenta de la base de datos

test

YOUR_INSTANCE_PASSWORD

Contraseña de la cuenta de la base de datos

Password****

ENTERPRISE

Motor de tabla por utilizar. Use true para clústeres Enterprise Edition y false para clústeres Community Edition

true

INSERT_BATCH_SIZE

Número de filas por lote

10000

INSERT_BATCH_NUM

Número de lotes por hilo

10

INSERT_OPTIMIZE_LEVEL

Nivel de optimización de inserción. Valores válidos: 1, 2, 3. Un valor mayor implica mayor velocidad: 3 > 2 > 1

3

Niveles de optimización de inserción

Los tres niveles utilizan sentencias preparadas. Elija el nivel adecuado según sus requisitos de portabilidad:

Nivel

Patrón SQL

Velocidad

Portabilidad

1

INSERT INTO ... VALUES (?, ?)

Referencia

Sí — JDBC estándar

2

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

Más rápido

No — específico de ClickHouse

3

INSERT INTO ... FORMAT RowBinary

El más rápido

No — específico de ClickHouse; requiere serialización manual

Código de ejemplo completo

El punto de entrada es el método main. Antes de ejecutarlo, actualice las constantes al inicio de la clase con los valores de su clúster.

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`");
            }
        }
    }
}

Ejecutar el código

Compile y ejecute desde la raíz del proyecto:

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

Si la conexión es exitosa y las inserciones se completan, la salida terminará con una línea similar a la siguiente:

Table `default`.`test` has 500000 rows

Descargar el proyecto completo

Haga clic en awesome-clickhouse-jdbc-0.2.1.zip para descargar el proyecto de ejemplo.

El proyecto contiene dos subproyectos:

image

Subproyecto

Descripción

native-example

Utiliza HikariCP y JDBC estándar con una única clase Main. Úselo para aprender sobre conectividad JDBC o para ejecutar pruebas básicas de rendimiento.

mybatis-hikari-example

Utiliza HikariCP, MyBatis (ORM) y JDBC estándar, con una estructura completa de capas entidad-mapper-servicio. Úselo si va a integrar ClickHouse en un proyecto basado en MyBatis.

Configure mybatis-hikari-example

La lógica general es la misma que en native-example. Configure los siguientes parámetros antes de ejecutar:

Archivo Parámetro Descripción Ejemplo
src/main/resources/application.yml url URL de conexión JDBC. Formato: jdbc:clickhouse:http://VPC_ENDPOINT:8123 jdbc:clickhouse:http://cc-bp128o64g****ky35-clickhouse.clickhouseserver.rds.aliyuncs.com:8123
username Cuenta de la base de datos test
password Contraseña de la cuenta de la base de datos Password****
src/main/java/com/aliyun/Main.java INSERT_BATCH_SIZE Número de filas por lote 10000
INSERT_BATCH_NUM Número de lotes a insertar 10
ENTERPRISE true para clústeres Enterprise Edition, false para clústeres Community Edition true
INSERT_OPTIMIZE_LEVEL Nivel de optimización de inserción. Valores válidos: 1, 2, 3. Un valor mayor implica mayor velocidad: 3 > 2 > 1 3

native-example

El punto de entrada del código y todas las configuraciones de parámetros de este proyecto se encuentran en src/main/java/com/aliyun/Main.java. Para obtener más información, consulte Step 3: Write the application code.

Solución de problemas

Tiempo de espera de conexión agotado

Revise los siguientes puntos en orden:

  1. Lista de permitidos: Asegúrese de que la dirección IP del servidor de la aplicación esté agregada a la lista de permitidos del clúster. Consulte Set a whitelist.

  2. Red: Compruebe si la aplicación y el clúster se encuentran en la misma VPC.

  3. Endpoint y puerto: Verifique que el endpoint sea correcto y que el puerto sea 8123.

Tiempo de espera de lectura agotado

Este error suele ocurrir durante inserciones grandes con tiempos de ejecución prolongados. Configure los parámetros TCP keepalive del sistema operativo y establezca las siguientes propiedades JDBC, tal como se muestra en el código de ejemplo:

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

Ambas configuraciones ya están incluidas en el código de ejemplo. Para obtener más información, consulte Troubleshooting.

HikariPool — la conexión no está disponible

Cierre la conexión después de usarla. El código de ejemplo utiliza try-with-resources para cerrar las conexiones automáticamente; siga el mismo patrón en su código.

Pasos siguientes

Conéctese a su clúster mediante otras herramientas:

Referencias