You need to enable JavaScript to run this app.
ByteHouse云数仓版

ByteHouse云数仓版

复制全文
JDBC
ClickHouse JDBC Driver
复制全文
ClickHouse JDBC Driver

您可以通过开源的 ClickHouse JDBC 驱动程序连接到 ByteHouse 云数仓版,连接后进行库表操作、数据写入查询等。本文为您介绍 ClickHouse JDBC 驱动连接 ByteHouse 云数仓版的主要操作流程和操作要点。

环境要求

支持 Java 1.8.0_261 或更高版本 (需要 TLS v1.3 支持)。

推荐版本

应用程序代码直连 ByteHouse 时,推荐下载 ClickHouse JDBC v0.4.6,Java 环境建议使用 OpenJDK 11。
如果您通过其他框架(如 DataX)连接 ByteHouse,Driver 版本适配规则如下:

  1. 如果框架内置的开源 ClickHouse JDBC Driver 可成功连接 ByteHouse(通常为较老版本),建议保留框架内的 Driver 版本,无需修改;
  2. 若内置 Driver 版本较新(≥ 0.7.x),需降低 Driver 版本,以确保正常连接 ByteHouse。

使用限制
  • 暂时不支持 ByteHouse 的 JSONB、BitMap64 类型。
  • 如果您在使用过程中遇到其他未知限制,请联系 ByteHouse 团队处理。

下载驱动

下载 ClickHouse JDBC 基础驱动:

驱动

已验证版本/注意事项

ClickHouse JDBC 基础驱动

0.4.6,驱动下载链接

Java

已验证版本:OpenJDK 11

添加为 Maven 依赖

通过 Maven 方式使用 ClickHouse JDBC 驱动的示例如下:

<!-- https://mvnrepository.com/artifact/com.clickhouse/clickhouse-jdbc -->
<dependency>
    <groupId>com.clickhouse</groupId>
    <artifactId>clickhouse-jdbc</artifactId>
    <version>0.4.6</version>
</dependency>

添加为 Gradle 依赖

通过 Gradle 方式使用 ClickHouse JDBC 驱动的示例如下:

// https://mvnrepository.com/artifact/com.clickhouse/clickhouse-jdbc
implementation 'com.clickhouse:clickhouse-jdbc:0.4.6'

获取 ByteHouse 连接信息

ByteHouse 支持通过 IAM 用户或数据库用户连接 ClickHouse JDBC Driver。IAM 用户与数据库用户二者差异说明如下,您可按需选择。

  • IAM 用户为火山引擎访问控制(IAM)中创建的用户,其权限由 IAM 权限策略及您授予的 ByteHouse 资源和数据权限决定。IAM 用户可访问 ByteHouse 控制台,也支持通过 CLI、连接驱动、生态工具、API 等方式访问 ByteHouse。
  • 数据库用户为 ByteHouse 中创建的数据库级别用户,可为其授予环境、资源和数据权限。数据库用户不可访问 ByteHouse 控制台,但支持通过 CLI、连接驱动、生态工具、API 等方式访问 ByteHouse。

更多 IAM 用户和数据库用户的介绍请参见以下文档:

使用 IAM 用户连接

请参考步骤三:获取 ByteHouse 连接串信息,了解如何通过 IAM 用户方式连接到 ByteHouse。
通用参数说明如下:

参数

配置说明

host

配置为 ByteHouse 的公网/私网域名,您可以在 ByteHouse 控制台的租户管理 > 基本信息 > 网络信息中查看并复制域名信息,详情请参见步骤二:配置网络信息

port

固定为 8123。

{DATABASE}

ByteHouse 数据库名称。您可登录 ByteHouse 控制台,在数据库页面查看并复制数据库。

user & password

登录 ByteHouse 数据库的用户名和密码。

  • user:固定为 bytehouse
  • password:为 API Key,配置为 ByteHouse 的租户管理 > 连接信息中获取的API Key,详情请参见获取 API Key

virtual_warehoue_id

可选配置,配置为计算组名。如果您需要指定计算组进行查询和写入,可配置该参数。
您可登录 ByteHouse 控制台,单击顶部计算组,查看并复制计算组 ID。示例:vw-{environment_id}{account_id}-{virtual_warehouse_name}
如果不配置,则默认使用您在 ByteHouse 设置的默认计算组。

ssl

开启安全协议 SSL。

注意

ByteHouse 需要加密认证,从而保护数据安全,因此您需打开 SSL。

使用数据库用户连接

参数

配置说明

host

配置为 ByteHouse 的公网/私网域名,您可以在 ByteHouse 控制台的租户管理 > 基本信息 > 网络信息中查看并复制域名信息,详情请参见步骤二:配置网络信息

port

固定为 8123。

{DATABASE}

ByteHouse 数据库名称。您可登录 ByteHouse控制台,在数据库页面查看并复制数据库。

user & password

登录 ByteHouse 数据库的用户名和密码。

  • user 配置为 {accountID_or_accountName}::{username}[::{envID}],详情请参见步骤三:获取 ByteHouse 连接串信息
    • {accountID_or_accountName} :指火山引擎用户账号 ID 或名称,可登录 ByteHouse 控制台,单击右上角个人中心查看并复制账号 ID 或名称。
    • {username} :指登录 ByteHouse 数据库的用户名。可在 ByteHouse 控制台 > 权限管理 > 用户 > 查看数据库用户名
    • {envID}:可选配置,数据库所在的环境名称。如果使用 default 环境,可不配置;如需使用其他环境,需指定环境名称,配置时无需添加[]。您可登录 ByteHouse 控制台,在租户管理 > 基本信息 > 当前环境中获取。
      使用示例如下:
      • 配置环境 ID:21xxxxxxxx::demouser::demoenv
      • 不配置环境 ID:21xxxxxxxx::demouser
  • password:可联系管理员获取数据库账号的密码。如果密码丢失或遗忘,可通联系管理员重置密码,详情请参考重置密码

virtual_warehoue_id

可选配置,配置为计算组名。如果您需要指定计算组进行查询和写入,可配置该参数。
您可登录 ByteHouse 控制台,单击顶部计算组,查看并复制计算组 ID。示例:vw-{environment_id}{account_id}-{virtual_warehouse_name}
如果不配置,则默认使用您在 ByteHouse 设置的默认计算组。

ssl

开启安全协议 SSL。

注意

ByteHouse 需要加密认证,从而保护数据安全,因此您需打开 SSL。

基本用法

您可以使用以下代码连接至 ByteHouse,并开始使用标准语句开发 ByteHouse,用于查询、写入和读取数据。

  • 超时时间配置:socket_timeout 默认为 30000ms、connect_timeout 默认为 5s。
  • 默认不支持 keepAlive,如需使用该功能,需新建 KeepAliveClickhouseSocketFactory 类,可参考配置 KeepAlive 章节配置,该功能需使用 JDK 11。

连接 ByteHouse

可参考下面代码连接至 ByteHouse,使用时注意替换连接语句中的 {Host}{Password}{User}{Database}{VIRTUAL_WAREHOUSE_ID} 等连接信息字段,获取方式请参见获取 ByteHouse 连接信息

使用 Propertities 配置连接

import com.clickhouse.jdbc.ClickHouseDriver;

import java.sql.*;
import java.util.Properties;
import java.util.UUID;

public class CKJDBC {
    public static void main(String[] args) {
        String host = "{Host}";
        int port = 8123;
        String password = "{API_KEY}";
        String user = "{User}";
        String database = "{Database}";
        String virtual_warehoue_id = "{VIRTUAL_WAREHOUSE_ID}";

        // 通过 properties 设置一些链接的基本参数
        String url = String.format("jdbc:clickhouse://%s:%d", host, port);
        Properties properties = new Properties();
        // 可以按需设置不同的计算组
          properties.setProperty("custom_http_params", String.format("virtual_warehouse=%s",virtual_warehoue_id));
          properties.setProperty("ssl", "true");
          properties.setProperty("compress", "0");
          properties.setProperty("user", user);
          properties.setProperty("password", password);
          properties.setProperty("database", database);

        // 按照 Java JDBC 标准接口创建 connection
//      try (Connection conn = DriverManager.getConnection(url, properties)) {
        // 按照 Clickhouse Driver 自身接口创建 connection
        Connection conn = null;
        Statement initStatement = null;
        try {
            conn = new ClickHouseDriver().connect(url, properties);
            dropDatabaseWithQueryId(conn);
            dropDatabase(conn);
            createDatabase(conn);
            createTable(conn);
            insertTable(conn);
            insertBatch(conn);
            selectTable(conn);
        } catch (SQLException ex) {
            ex.printStackTrace();
        } finally {
            try {
                if (initStatement != null) initStatement.close();
                if (conn != null) conn.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }

使用 URL 的 param 配置连接

import com.clickhouse.jdbc.ClickHouseDriver;

import java.sql.*;
import java.util.Properties;
import java.util.UUID;

public class CKJDBC {
    public static void main(String[] args) {
        String host = "{Host}";
        int port = 8123;
        String password = "{API_KEY}";
        String user = "{User}";
        String database = "{Database}";
        String virtual_warehoue_id = "{VIRTUAL_WAREHOUSE_ID}";

        String url = String.format("jdbc:clickhouse://%s:%d/?ssl=true&compress=0&user=%s&password=%s&database=%s&custom_http_params=virtual_warehouse=%s",
                host, port, user, password, database, virtual_warehoue_id);


        // 按照 Java JDBC 标准接口创建 connection
//      try (Connection conn = DriverManager.getConnection(url, properties)) {
        // 按照 Clickhouse Driver 自身接口创建 connection
        Connection conn = null;
        Statement initStatement = null;
        try {
            conn = new ClickHouseDriver().connect(url, properties);
            dropDatabaseWithQueryId(conn);
            dropDatabase(conn);
            createDatabase(conn);
            createTable(conn);
            insertTable(conn);
            insertBatch(conn);
            selectTable(conn);
        } catch (SQLException ex) {
            ex.printStackTrace();
        } finally {
            try {
                if (initStatement != null) initStatement.close();
                if (conn != null) conn.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }

配置 KeepAlive

如需使用 keepAlive 功能,请使用 JDK 11,执行以下代码配置并启用。

  1. 新建 KeepAliveClickhouseSocketFactory 类。

    package com.bytehouse.factory;
    
    import com.clickhouse.client.AbstractSocketClient;
    import com.clickhouse.client.ClickHouseConfig;
    import com.clickhouse.client.ClickHouseSocketFactory;
    import com.clickhouse.client.ClickHouseSslContextProvider;
    import com.clickhouse.client.config.ClickHouseSslMode;
    import com.clickhouse.data.ClickHouseUtils;
    import jdk.net.ExtendedSocketOptions;
    import org.apache.hc.client5.http.socket.PlainConnectionSocketFactory;
    import org.apache.hc.client5.http.ssl.DefaultHostnameVerifier;
    import org.apache.hc.client5.http.ssl.SSLConnectionSocketFactory;
    import org.apache.hc.core5.http.protocol.HttpContext;
    import org.apache.hc.core5.ssl.SSLContexts;
    import javax.net.ssl.SSLContext;
    import javax.net.ssl.SSLException;
    import java.io.IOException;
    import java.net.Socket;
    import java.util.logging.Level;
    import java.util.logging.Logger;
    
    public class KeepAliveClickhouseSocketFactory implements ClickHouseSocketFactory {
    
        private static final Logger LOG = Logger.getLogger(KeepAliveClickhouseSocketFactory.class.getName());
    
        static final KeepAliveClickhouseSocketFactory instance = new KeepAliveClickhouseSocketFactory();
    
    
        // TCP Keep-Alive 参数(单位:秒)
        private static final int KEEPALIVE_IDLE = 60;
        private static final int KEEPALIVE_INTERVAL = 30;
        private static final int KEEPALIVE_COUNT = 60;
    
    
        @Override
        public <T> T create(ClickHouseConfig config, Class<T> clazz) throws IOException, UnsupportedOperationException {
            if (config == null || clazz == null) {
                throw new IllegalArgumentException("Non-null configuration and class are required");
            } else if (SSLConnectionSocketFactory.class.equals(clazz)) {
                return clazz.cast(new SSLSocketFactory(config));
            } else if (PlainConnectionSocketFactory.class.equals(clazz)) {
                return clazz.cast(new SocketFactory(config));
            }
    
            throw new UnsupportedOperationException(ClickHouseUtils.format("Class %s is not supported", clazz));
        }
    
        private static void configureKeepAlive(Socket sock) {
            try {
                // 启用 Keep-Alive
                sock.setKeepAlive(true);
    
                // 设置 TCP_KEEPIDLE (例如 60 秒)
                sock.setOption(ExtendedSocketOptions.TCP_KEEPIDLE, KEEPALIVE_IDLE);
    
                // 设置 TCP_KEEPINTVL (例如 30 秒)
                sock.setOption(ExtendedSocketOptions.TCP_KEEPINTERVAL, KEEPALIVE_INTERVAL);
    
                // 设置 TCP_KEEPCNT (例如 5 probes)
                sock.setOption(ExtendedSocketOptions.TCP_KEEPCOUNT, KEEPALIVE_COUNT);
    
                LOG.info("TCP keep-alive options set successfully.");
            } catch (Exception e) {
                LOG.log(Level.WARNING, "can not configure Keep-Alive", e);
            }
        }
    
        @Override
        public boolean supports(Class<?> clazz) {
            return PlainConnectionSocketFactory.class.equals(clazz) || SSLConnectionSocketFactory.class.equals(clazz);
        }
    
        public KeepAliveClickhouseSocketFactory() {
        }
    
        static class SSLSocketFactory extends SSLConnectionSocketFactory {
            private final ClickHouseConfig config;
    
            private SSLSocketFactory(ClickHouseConfig config) throws SSLException {
                super(ClickHouseSslContextProvider.getProvider().getSslContext(SSLContext.class, config)
                                .orElse(SSLContexts.createDefault()),
                        config.getSslMode() == ClickHouseSslMode.STRICT
                                ? new DefaultHostnameVerifier()
                                : (hostname, session) -> true); // NOSONAR
                this.config = config;
            }
    
            @Override
            public Socket createSocket(HttpContext context) throws IOException {
                Socket sock = AbstractSocketClient.setSocketOptions(config, new Socket());
                configureKeepAlive(sock);
                return sock;
            }
    
            public static SSLSocketFactory create(ClickHouseConfig config) throws SSLException {
                return new SSLSocketFactory(config);
            }
        }
    
        static class SocketFactory extends PlainConnectionSocketFactory {
            private final ClickHouseConfig config;
    
            private SocketFactory(ClickHouseConfig config) {
                this.config = config;
            }
    
            @Override
            public Socket createSocket(final HttpContext context) throws IOException {
                Socket sock = AbstractSocketClient.setSocketOptions(config, new Socket());
                configureKeepAlive(sock);
                return sock;
            }
    
            public static SocketFactory create(ClickHouseConfig config) {
                return new SocketFactory(config);
            }
        }
    
    }
    
  2. 在连接 URL 中增加自定义参数 Factory 和 Enable socket_keepalive,设置为 APACHE_HTTP_CLIENT 的 http_connection_provider。

    jdbc:clickhouse://<host>:8123/<database>?custom_socket_factory=com.bytehouse.factory.KeepAliveClickhouseSocketFactory&socket_keepalive=true&http_connection_provider=APACHE_HTTP_CLIENT
    

创建数据库、表

与 ByteHouse 建立连接后,您可以使用以下代码创建数据库、表。

public static void dropDatabase(Connection connection) {
        Statement stmt = null;
        try {
            stmt = connection.createStatement();
            String dropDbQuery = "Drop DATABASE IF EXISTS bhjdbctest";
            stmt.execute(dropDbQuery);
        } catch (SQLException ex) {
            ex.printStackTrace();
        } finally {
            try {
                if (stmt != null) stmt.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }

    public static void createDatabase(Connection connection) {
        Statement stmt = null;
        try {
            stmt = connection.createStatement();
            String createDbQuery = "CREATE DATABASE IF NOT EXISTS bhjdbctest";
            stmt.execute(createDbQuery);
        } catch (SQLException ex) {
            ex.printStackTrace();
        } finally {
            try {
                if (stmt != null) stmt.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }

    public static void createTable(Connection connection) {
        Statement stmt = null;
        try {
            stmt = connection.createStatement();
            String createTableQuery = "CREATE TABLE IF NOT EXISTS bhjdbctest.orders\n (" +
                    "OrderID String, OrderName String, OrderPriority Int8)" +
                    " engine = CnchMergeTree() partition by OrderID order by OrderID";
            stmt.execute(createTableQuery);
        } catch (SQLException ex) {
            ex.printStackTrace();
        } finally {
            try {
                if (stmt != null) stmt.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }

写入数据

您可以使用以下代码写入数据。

public static void insertTable(Connection connection) {
        Statement stmt = null;
        try {
            stmt = connection.createStatement();
            String insertQuery = "INSERT INTO bhjdbctest.orders VALUES ('54895','Apple',12)";
            stmt.executeUpdate(insertQuery);
        } catch (SQLException ex) {
            ex.printStackTrace();
        } finally {
            try {
                if (stmt != null) stmt.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }

批量写入数据

使用 PreparedStatement 批量数据写入。

public static void insertBatch(Connection connection) {
        PreparedStatement pstmt = null;
        String insertQuery = "INSERT INTO bhjdbctest.orders (OrderID, OrderName, OrderPriority) VALUES (?,'Apple',?)";
        try {
            pstmt = connection.prepareStatement(insertQuery);
            int insertBatchSize = 10;
            for (int i = 0; i < insertBatchSize; i++) {
                pstmt.setString(1, "ID" + i);
                pstmt.setInt(2, i);
                pstmt.addBatch();
            }
            pstmt.executeBatch();

        } catch (SQLException ex) {
            ex.printStackTrace();
        } finally {
            try {
                if (pstmt != null) pstmt.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }

查询写入结果

您可以使用以下代码查询数据。

public static void selectTable(Connection connection) {
        Statement stmt = null;
        try {
            stmt = connection.createStatement();
            String selectTableQuery = "SELECT * FROM bhjdbctest.orders";
            ResultSet rs = stmt.executeQuery(selectTableQuery);
            ResultSetMetaData rsmd = rs.getMetaData();
            int columnsNumber = rsmd.getColumnCount();
            while (rs.next()) {
                for (int i = 1; i <= columnsNumber; i++) {
                    if (i > 1) System.out.print(", ");
                    String columnValue = rs.getString(i);
                    System.out.print(columnValue);
                }
                System.out.println();
            }
        } catch (SQLException ex) {
            ex.printStackTrace();
        } finally {
            try {
                if (stmt != null) stmt.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }
}

使用 query ID

设置 query ID 有助于追踪查询执行情况,方便后续调试或管理查询任务。
如果您需要为某条查询语句设置 query ID,可参考以下代码,添加 stmt.unwrap(ClickHouseRequest.class).query(dropDbQuery,String.format("customized_%s", UUID.randomUUID())).execute(); 代码。

public static void dropDatabaseWithQueryId(Connection connection) {
        ClickHouseStatement stmt = null;
        try {
            stmt = (ClickHouseStatement)connection.createStatement();
            String dropDbQuery = "Drop DATABASE IF EXISTS bhjdbctest";
            stmt.unwrap(ClickHouseRequest.class).query(dropDbQuery,String.format("customized_%s", UUID.randomUUID())).execute();
        } catch (SQLException ex) {
            ex.printStackTrace();
        } finally {
            try {
                if (stmt != null) stmt.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }

常见问题

Unsupported or unrecognized SSL message 报错

如果您在使用过程中遇到 Unsupported or unrecognized SSL message 报错,这是 Java 版本过低导致的,Java 1.8.0_261 **** 以下版本会出现此报错。
您可通过以下方式解决:

  • 升级 Java 版本到 Java 11。
  • 如需保留 Java 1.8 环境,可升级至 Java 1.8.0_261 及更高补丁版本解决该问题。

DB::Exception: No local available worker group for vw-xxxx

如果您在使用过程中遇到 DB::Exception: No local available worker group for vw-xxxx 报错,这是计算组尚未启动导致的。
您可通过以下方式解决:
登录 ByteHouse 云数仓版控制台,在计算组页面,查看您使用的计算组是否在 正在运行的状态。

Code: UNAUTHENTICATED

如果您在使用过程中遇到 Exception: unknown error: Code: UNAUTHENTICATED 报错,这可能是 userpassword 配置错误导致的。
您可通过以下方式解决:
再次确认您配置的userpassword是否正确。

DB::Exception: Database xxx.yyy doesn't exist

如果您在使用过程中遇到 DB::Exception: Database xxx.yyy doesn't exist 报错,这可能是数据库名称配置错误导致的。
您可通过以下方式解决:
请确认连接信息中配置的数据库名称是否正确。

版本不兼容报错:only works with ClickHouse servers 20.7 and above

如果您在使用 DBeaver ClickHouse driver 连接 ByteHouse 过程中遇到以下报错:

We apologize, but this driver only works with ClickHouse servers 20.7 and above. Please consider to upgrade your server to a more recent version.

您可按照在连接串或连接参数中设置 server_time_zoneserver_version 参数值解决该问题。

  • server_time_zone:设置为 Asia/Shanghai。如果您处于其他时区,可根据实际情况设置为对应的时区。
  • server_version:设置为 21.8.7.1。
最近更新时间:2025.11.26 14:17:01
这个页面对您有帮助吗?
有用
有用
无用
无用