Tous les produits
Search
Centre de documentation

ApsaraDB for ClickHouse:Connect using JDBC

Dernière mise à jour :Aug 11, 2026

Ce guide explique comment se connecter à un cluster ApsaraDB for ClickHouse avec Java Database Connectivity (JDBC) dans un projet Maven. À la fin de ce tutoriel, vous disposerez d'une connexion fonctionnelle dotée d'un pool de connexions HikariCP, d'une table créée et de données insérées simultanément sur plusieurs threads.

Prérequis

Avant de commencer, vérifiez que vous disposez des éléments suivants :

  • L'adresse IP de votre serveur d'applications a été ajoutée à la liste d'autorisation du cluster. Consultez Définir une liste d'autorisation

  • Un compte de base de données et son mot de passe. Consultez Créer un compte

  • Maven 3.9.6 et JDK 1.8 sont installés

Si votre serveur d'applications et le cluster se trouvent dans des VPC différents, résolvez d'abord les problèmes de connectivité réseau. Consultez Comment résoudre les problèmes de connectivité réseau entre un cluster de destination et une source de données ? Vous pouvez également demander un endpoint public. Consultez Demander et libérer un endpoint public .

Étape 1 : Ajouter les dépendances Maven

Ajoutez les dépendances suivantes à votre fichier 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>

Étape 2 : Comprendre le format de l'URL JDBC

L'URL JDBC suit le modèle suivant :

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

Par exemple :

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

Points clés :

  • Le protocole doit être spécifié explicitement ; le pilote ne le déduit pas du numéro de port.

  • Le protocole par défaut est HTTP et le port par défaut est 8123. Spécifiez le port 8123 sauf si vous utilisez un port personnalisé.

  • Le format de l'endpoint est VPC_ENDPOINT:8123, où VPC_ENDPOINT correspond à l'endpoint VPC ou à l'endpoint public de votre cluster.

Étape 3 : Écrire le code de l'application

Fonctionnement

L'exemple de code suit le flux ci-dessous :

  1. Générez un objet HikariDataSource avec les paramètres du pool de connexions et les propriétés JDBC spécifiques à ClickHouse.

  2. Créez une table : une seule table MergeTree pour les clusters Enterprise Edition, ou une table locale plus une table distribuée pour les clusters Community Edition.

  3. Insérez des données simultanément sur 5 threads, chacun insérant 10 lots de 10 000 lignes.

  4. Comptez le nombre total de lignes dans la table pour vérifier les insertions.

Paramètres de connexion

Remplacez les valeurs fictives du code par les valeurs réelles de votre cluster.

Paramètre Description Exemple
YOUR_INSTANCE_PROTOCOL Le protocole de connexion. La valeur est fixée à "http". http
YOUR_INSTANCE_ENDPOINT L'endpoint. Format : VPC_ENDPOINT:8123 cc-bp128o64g****ky35-clickhouse.clickhouseserver.rds.aliyuncs.com:8123
DATABASE La base de données à laquelle se connecter default
YOUR_INSTANCE_USER Le compte de base de données test
YOUR_INSTANCE_PASSWORD Le mot de passe du compte de base de données Password****
ENTERPRISE Le moteur de table à utiliser. true pour les clusters Enterprise Edition, false pour les clusters Community Edition true
INSERT_BATCH_SIZE Nombre de lignes par lot 10000
INSERT_BATCH_NUM Nombre de lots par thread 10
INSERT_OPTIMIZE_LEVEL Niveau d'optimisation de l'insertion. Valeurs valides : 1, 2, 3. Plus le niveau est élevé, plus la vitesse est grande : 3 > 2 > 1 3

Niveaux d'optimisation de l'insertion

Les trois niveaux utilisent des instructions préparées. Choisissez en fonction de vos besoins en matière de portabilité :

Niveau Modèle SQL Vitesse Portable
1 INSERT INTO ... VALUES (?, ?) Référence Oui — JDBC standard
2 INSERT INTO ... SELECT ... FROM input(...) Plus rapide Non — Spécifique à ClickHouse
3 INSERT INTO ... FORMAT RowBinary Le plus rapide Non — Spécifique à ClickHouse, nécessite une sérialisation manuelle

Exemple de code complet

Le point d'entrée est la méthode main. Avant l'exécution, mettez à jour les constantes en haut de la classe avec les valeurs de votre 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`");
            }
        }
    }
}

Exécuter le code

Compilez et exécutez depuis la racine du projet :

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

Si la connexion réussit et que les insertions sont terminées, la sortie se termine par une ligne similaire à :

Table `default`.`test` has 500000 rows

Télécharger le projet complet

Cliquez sur awesome-clickhouse-jdbc-0.2.1.zip pour télécharger l'exemple de projet.

Le projet contient deux sous-projets :

image

Sous-projet Description
native-example Utilise HikariCP et JDBC standard avec une seule classe Main. Utilisez ce projet pour apprendre la connectivité JDBC ou effectuer des tests de performance de base.
mybatis-hikari-example Utilise HikariCP, MyBatis (ORM) et JDBC standard, avec une structure complète en couches entité-mapper-service. Utilisez ce projet si vous intégrez ClickHouse dans un projet basé sur MyBatis.

Configurer mybatis-hikari-example

La logique globale est identique à celle de native-example. Configurez les éléments suivants avant l'exécution :

Fichier Paramètre Description Exemple
src/main/resources/application.yml url URL de connexion JDBC. Format : jdbc:clickhouse:http://VPC_ENDPOINT:8123 jdbc:clickhouse:http://cc-bp128o64g****ky35-clickhouse.clickhouseserver.rds.aliyuncs.com:8123
username Le compte de base de données test
password Le mot de passe du compte de base de données Password****
src/main/java/com/aliyun/Main.java INSERT_BATCH_SIZE Nombre de lignes par lot 10000
INSERT_BATCH_NUM Nombre de lots à insérer 10
ENTERPRISE true pour les clusters Enterprise Edition, false pour les clusters Community Edition true
INSERT_OPTIMIZE_LEVEL Niveau d'optimisation de l'insertion. Valeurs valides : 1, 2, 3. Plus le niveau est élevé, plus la vitesse est grande : 3 > 2 > 1 3

native-example

Le point d'entrée du code et toutes les configurations de paramètres pour ce projet se trouvent dans src/main/java/com/aliyun/Main.java. Pour plus d'informations, consultez Étape 3 : Écrire le code de l'application.

Dépannage

Délai de connexion dépassé

Vérifiez les points suivants dans l'ordre :

  1. Liste d'autorisation : Assurez-vous que l'adresse IP du serveur d'applications a été ajoutée à la liste d'autorisation du cluster. Consultez Définir une liste d'autorisation.

  2. Réseau : Vérifiez si l'application et le cluster se trouvent dans le même VPC.

  3. Endpoint et port : Vérifiez que l'endpoint est correct et que le port est 8123.

Délai de lecture dépassé

Cette erreur survient généralement lors d'insertions volumineuses avec des temps d'exécution longs. Configurez les paramètres TCP keepalive pour le système d'exploitation et définissez les propriétés JDBC suivantes, comme indiqué dans l'exemple de code :

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

Ces deux paramètres sont déjà inclus dans l'exemple de code. Pour plus d'informations, consultez Dépannage.

HikariPool — la connexion n'est pas disponible

Fermez la connexion après utilisation. L'exemple de code utilise l'instruction try-with-resources pour fermer automatiquement les connexions ; appliquez le même modèle dans votre code.

Étapes suivantes

Connectez-vous à votre cluster à l'aide d'autres outils :

Références