ByteHouse JDBC Driver 是用于 Java 应用通过 JDBC 接口连接 ByteHouse 云数仓的驱动程序,支持数据查询、写入等操作,可帮助 Java 开发者快速集成 ByteHouse,实现高效的数据访问。本文介绍如何通过 JDBC 方式连接并访问 ByteHouse 云数仓。
支持 Java 1.8.0_261 或更高版本 (需要 TLS v1.3 支持)。
推荐使用 ByteHouse JDBC Driver 最新版本。您可前往 ByteHouse Java Driver index 页面,获取 ByteHouse JDBC Driver 最新版本的 jar 文件。
部分 SQL/ Function 受驱动版本影响,详情参阅下表。
SQL/Function | 驱动版本 |
|---|---|
replace into | ≥1.1.67 |
请单击前往 ByteHouse Java Driver index 页面,获取 ByteHouse JDBC Driver 最新版本的 jar 文件。
获取 jar 文件后,您只需将编译后的 jar 文件添加到您的项目中,或者使用您选择的依赖项管理工具将此项目添加为依赖项。然后,您可以导入并使用 Java 程序中的类。
通过 Gradle 方式使用 ByteHouse JDBC 驱动的示例如下:
repositories { // This is public bytedance repository for downloading artifacts maven { url "https://artifact.bytedance.com/repository/releases" } } dependencies { implementation "com.bytedance.bytehouse:driver-java:1.1.74:all" }
说明
以上示例中,java:1.1.74为一个示例的版本号,实际使用时,您需替换为最新的版本号。前往 ByteHouse Java Driver index 页面获取最新版本的驱动文件。
通过 Maven 方式使用 ByteHouse JDBC 驱动的示例如下:
// This is public bytedance repository for downloading artifacts <repository> <id>bytedance</id> <name>ByteDance Public Repository</name> <url>https://artifact.bytedance.com/repository/releases</url> </repository> <dependency> <groupId>com.bytedance.bytehouse</groupId> <artifactId>driver-java</artifactId> <version>1.1.74</version> <classifier>all</classifier> </dependency>
说明
以上示例中,java:1.1.74为一个示例的版本号,实际使用时,您需替换为最新的版本号。前往 ByteHouse Java Driver index 页面获取最新版本的驱动文件。
ByteHouse 支持通过 IAM 用户或数据库用户连接 ByteHouse JDBC Driver。IAM 用户与数据库用户二者差异说明如下,您可按需选择。
更多 IAM 用户和数据库用户的介绍请参见以下文档:
请参考步骤三:获取 ByteHouse 连接串信息,了解如何通过 IAM 用户方式连接到 ByteHouse。
通用参数说明如下:
参数 | 配置说明 |
|---|---|
Host | 配置为 ByteHouse 的公网/私网域名,您可以在 ByteHouse 控制台的租户管理 > 基本信息 > 网络信息中查看并复制域名信息,详情请参见步骤二:配置网络信息。 |
Port | 固定为 19000。 |
User | 固定为 bytehouse。 |
Password | 为 ByteHouse 的 API Key,您可以在 ByteHouse 控制台的租户管理 > 连接信息中获取 API Key,详情请参见获取 API Key。 |
Database | 配置为连接 ByteHouse 数据库名称。您可登录 ByteHouse控制台,在数据库页面查看并复制数据库。 |
virtual_warehouse | 可选配置,配置为计算组名。如果您需要指定计算组进行查询和写入,可配置该参数。 |
请参考步骤三:获取 ByteHouse 连接串信息,了解如何通过数据库用户的方式连接到 ByteHouse。
通用参数说明如下:
参数 | 配置说明 |
|---|---|
Host | 配置为 ByteHouse 的公网/私网域名,您可以在 ByteHouse 控制台的租户管理 > 基本信息 > 网络信息中查看并复制域名信息,详情请参见步骤二:配置网络信息。 |
Port | 固定为 19000。 |
User | username 配置为
|
Password | 数据库账号的密码由管理员创建数据库账号时自定义配置,您可联系管理员获取密码。如果密码丢失或遗忘,可通联系管理员重置密码,详情请参考重置密码。 |
Database | 配置为连接 ByteHouse 数据库名称。您可登录 ByteHouse控制台,在数据库页面查看并复制数据库。 |
virtual_warehouse | 可选配置,配置为计算组名。如果您需要指定计算组进行查询和写入,可配置该参数。 |
您可以使用以下代码连接至 ByteHouse,并开始使用标准语句开发 ByteHouse,用于查询、写入和读取数据。
keepAlive,可以复用连接和避免短链接。可参考下面代码连接至 ByteHouse,使用时注意替换连接语句中的 {host} 、{Password}、{User}、{Database}、{VITUAL_WAREHOUSE_ID} 等连接信息字段,获取方式请参见获取 ByteHouse 连接信息。
import com.bytedance.bytehouse.jdbc.statement.ByteHouseStatement; import java.sql.*; import java.util.Properties; import java.util.UUID; public class BHJDBC { public static void main(String[] args) { String host = "{Host}"; int port = 19000; String password = "{Password}"; String user = "{User}"; String database = "{Database}"; String virtual_warehouse_id = "{VIRTUAL_WAREHOUSE_ID}"; // 通过 properties 设置一些链接的基本参数 String url = String.format("jdbc:bytehouse://%s:%d", host, port); Properties properties = new Properties(); // 可以按需设置不同的计算组 properties.setProperty("virtual_warehouse", virtual_warehouse_id); properties.setProperty("secure", "true"); properties.setProperty("user", user); properties.setProperty("password", password); properties.setProperty("database", database); Connection conn = null; try { conn = DriverManager.getConnection(url, properties); } catch (SQLException ex) { ex.printStackTrace(); } finally { try { if (conn != null) conn.close(); } catch (SQLException e) { e.printStackTrace(); } } }
import com.bytedance.bytehouse.jdbc.statement.ByteHouseStatement; import java.sql.*; import java.util.Properties; import java.util.UUID; public class BHJDBC { public static void main(String[] args) { String host = "{Host}"; int port = 19000; String password = "{Password}"; String user = "{User}"; String database = "{Database}"; String virtual_warehouse_id = "{VIRTUAL_WAREHOUSE_ID}"; String url = String.format("jdbc:bytehouse://%s:%d/?secure=true&user=%s&password=%s&database=%s&virtual_warehouse=%s", host, port, user, password, database, virtual_warehouse_id); Connection conn = null; try { conn = DriverManager.getConnection(url); } catch (SQLException ex) { ex.printStackTrace(); } finally { try { if (conn != null) conn.close(); } catch (SQLException e) { e.printStackTrace(); } } }
与 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(); } } } /* * 批量插入数据 */ 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 有助于追踪查询执行情况,方便后续调试或管理查询任务。您可以使用 JDBC 原生方式,也可以使用 ByteHouse 专用接口,为查询语句设置 query ID。
如果您需要为某条查询语句设置 query ID,可参考以下代码,添加 stmt.setQueryId(String.format("customized_%s", UUID.randomUUID())); 代码。
public static void dropDatabaseWithQueryId(Connection connection) { ByteHouseStatement stmt = null; try { stmt = (ByteHouseStatement)connection.createStatement(); stmt.setQueryId(String.format("customized_%s", UUID.randomUUID())); String createDbQuery = "Drop DATABASE IF EXISTS bhjdbctest"; stmt.execute(createDbQuery); } catch (SQLException ex) { ex.printStackTrace(); } finally { try { if (stmt != null) stmt.close(); } catch (SQLException e) { e.printStackTrace(); } } }
ByteHouse JDBC 驱动从 v1.1.83 版本起支持了为查询分配自定义 Query ID 功能。
使用 Query ID 功能前,请确保满足以下条件:
Connection → ByteHouseConnectionStatement → ByteHouseStatementPreparedStatement → ByteHousePreparedStatement如果使用时未明确指定 Query ID,ByteHouse JDBC 驱动将随机自动生成 Query ID,并将其与查询关联。
API 接口 | 配置说明 |
|---|---|
| 仅为下一条查询设置 Query ID。
|
| 将 Query ID 直接设置到 ByteHouseStatement 中。
|
| JDBC 与 ByteHouse 交互的方式所限,Query ID 必须在
|
| 返回连接后的最后一个 Query ID。
|
您可以使用 session_query_id_prefix 属性为所有 Query ID 设置会话级的前缀。
Properties props = new Properties(); props.setProperty("session_query_id_prefix", "my-spark-application-"); Connection connection = new ByteHouseDriver().connect(jdbcUrl, props);
JDBC会自动为每个查询的前缀添加一个递增的计数器:
Properties props = new Properties(); props.setProperty("session_query_id_prefix", "my-spark-application-"); Connection connection = new ByteHouseDriver().connect(jdbcUrl, props); connection.createStatement().executeQuery("DROP DATABASE IF EXISTS demo"); // Query ID: "my-spark-application-1" connection.createStatement().executeQuery("CREATE DATABASE IF NOT EXISTS demo"); // Query ID: "my-spark-application-2" connection.createStatement().executeQuery("CREATE TABLE demo.tbl(id INT) ENGINE=CnchMergeTree ORDER BY id"); // Query ID: "my-spark-application-3"
驱动程序将前缀与用户指定的查询ID连接起来。不自动递增。
Properties props = new Properties(); props.setProperty("session_query_id_prefix", "my-spark-application-"); Connection connection = new ByteHouseDriver().connect(jdbcUrl, props); ((ByteHouseConnection) connection).setQueryId("drop-db"); connection.createStatement().executeQuery("DROP DATABASE IF EXISTS demo"); // Query ID: "my-spark-application-drop-db" ((ByteHouseConnection) connection).setQueryId("create-db"); connection.createStatement().executeQuery("CREATE DATABASE IF NOT EXISTS demo"); // Query ID: "my-spark-application-create-db"
ByteHouse 支持通过 HikariCP 和 Druid 进行 JDBC 连接和数据开发。
下面举例介绍如何通过 ByteHouse JDBC 驱动程序与 HikariCP 连接。
通过 Maven 方式使用 ByteHouse JDBC 驱动时,请在 pom.xml 文件中添加以下依赖项以包含 HikariCP:
<dependency> <groupId>com.zaxxer</groupId> <artifactId>HikariCP</artifactId> <version>4.0.3</version> </dependency>
通过 Gradle 方式使用 ByteHouse JDBC 驱动时,请在 build.gradle 文件中添加以下依赖项以包含 HikariCP:
dependencies { implementation 'com.zaxxer:HikariCP:4.0.3' }
使用 HikariCP 时,您最常与之交互的类是 HikariDataSource。您可以按照以下示例在 Java 应用程序中配置和使用 HikariCP 来创建 Bytehouse 连接。
可参考下面代码连接至 ByteHouse,使用时注意替换连接语句中的host 、port、password、user、database、virtual_warehouse_id 等连接信息字段,获取方式请参见获取 ByteHouse 连接信息。
import com.zaxxer.hikari.HikariConfig; import com.zaxxer.hikari.HikariDataSource; import java.sql.*; public class HikariConnection { public static Connection getConnectionWithHikariConfig(String host, String port, String apiKey) throws Exception { HikariConfig config = new HikariConfig(); String jdbcURL = String.format("jdbc:bytehouse://%s:%s/?api_key=%s", host, port, apiKey); // 标准 Hikari 配置 config.setJdbcUrl(jdbcURL); config.setMinimumIdle(5); // 池中的最小空闲连接数 config.setMaximumPoolSize(20); // 池中的最大连接数 config.setMaxLifetime(4 * 60 * 1000); // 连接将在 4 分钟后失效(被回收),最长允许(的存活时间)为 5 分钟 config.setIdleTimeout(2 * 60 * 1000); // 连接闲置 2 分钟后,即符合被驱逐(回收)的条件 config.setPoolName("BH-Pool"); config.addDataSourceProperty("secure", "true"); // config.addDataSourceProperty("virtual_warehouse", {VIRTUAL_WAREHOUSE_ID}); HikariDataSource hds = new HikariDataSource(config); Connection conn = hds.getConnection(); return conn; } public static void main(String[] args) { String host = "{HOST}"; String apiKey = "{API_KEY}"; String port = "19000"; try { Connection connection = getConnectionWithHikariConfig(host, port, apiKey); createDatabase(connection); createTable(connection); insertTable(connection); insertBatch(connection); selectTable(connection); } catch (Exception e) { e.printStackTrace(); } } public static void createDatabase(Connection connection) { try (Statement stmt = connection.createStatement()) { String createDbQuery = "CREATE DATABASE IF NOT EXISTS inventory"; stmt.execute(createDbQuery); } catch (SQLException ex) { ex.printStackTrace(); } } public static void createTable(Connection connection) { try (Statement stmt = connection.createStatement()) { String createTableQuery = "CREATE TABLE IF NOT EXISTS inventory.orders\n (" + "OrderID String, OrderName String, OrderPriority Int8)" + " engine = CnchMergeTree() partition by OrderID order by OrderID"; stmt.execute( createTableQuery ); } catch (SQLException ex) { ex.printStackTrace(); } } public static void insertTable(Connection connection) { try (Statement stmt = connection.createStatement()) { String insertQuery = "INSERT INTO inventory.orders VALUES ('54895','Apple',12)"; stmt.executeUpdate( insertQuery ); } catch (SQLException ex) { ex.printStackTrace(); } } public static void insertBatch(Connection connection) { String insertQuery = "INSERT INTO inventory.orders (OrderID, OrderName, OrderPriority) VALUES (?,'Apple',?)"; try (PreparedStatement 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(); } } public static void selectTable(Connection connection) { try (Statement stmt = connection.createStatement()) { String selectTableQuery = "SELECT * FROM inventory.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(); } } }
您也可以通过阿里巴巴的 Druid 连接池来连接 ByteHouse。
通过 Maven 方式使用 ByteHouse JDBC 驱动时,请在 pom.xml 文件中添加以下依赖项以包含 Druid:
<dependency> <groupId>com.alibaba</groupId> <artifactId>druid</artifactId> <version>1.1.23</version> </dependency>
通过 Gradle 方式使用 ByteHouse JDBC 驱动时,请在 build.gradle 文件中添加以下依赖项以包含 Druid:
dependencies { implementation 'com.alibaba:druid:1.1.23' }
在使用 Druid 时,您最常与之交互的类是 DruidDataSource。您可以按照以下示例在Java 应用程序中配置和使用 Druid 来创建 Bytehouse 连接。
可参考下面连接至 ByteHouse,使用时注意替换连接语句中的host 、port、password、user、database、virtual_warehouse_id 等连接信息字段,获取方式请参见获取 ByteHouse 连接信息。
import com.alibaba.druid.pool.DruidDataSource; import java.sql.*; import java.util.Properties; public class DruidConnection { public static Connection getConnectionWithDruidConfig(String host, String port, String apiKey) throws Exception { DruidDataSource druidDataSource = new DruidDataSource(); String jdbcURL = String.format("jdbc:bytehouse://%s:%s/?api_key=%s", host, port, apiKey); druidDataSource.setUrl(jdbcURL); druidDataSource.setDriverClassName("com.bytedance.bytehouse.jdbc.ByteHouseDriver"); druidDataSource.setMaxActive(10); // 最大活跃连接数 druidDataSource.setInitialSize(2); // 初始连接数 druidDataSource.setMinIdle(2); // 最小空闲连接数 druidDataSource.setMaxWait(50000); // 获取连接的最大等待时间 Properties properties = druidDataSource.getConnectProperties(); properties.setProperty("secure", "true"); // properties.setProperty("virtual_warehouse", {VIRTUAL_WAREHOUSE_ID}); druidDataSource.setConnectProperties(properties); Connection conn = druidDataSource.getConnection(); return conn; } public static void main(String[] args) { String host = "{HOST}"; String apiKey = "{API_KEY}"; String port = "19000"; try { Connection connection = getConnectionWithDruidConfig(host, port, apiKey); createDatabase(connection); createTable(connection); insertTable(connection); insertBatch(connection); selectTable(connection); } catch (Exception e) { e.printStackTrace(); } } public static void createDatabase(Connection connection) { try (Statement stmt = connection.createStatement()) { String createDbQuery = "CREATE DATABASE IF NOT EXISTS inventory"; stmt.execute(createDbQuery); } catch (SQLException ex) { ex.printStackTrace(); } } public static void createTable(Connection connection) { try (Statement stmt = connection.createStatement()) { String createTableQuery = "CREATE TABLE IF NOT EXISTS inventory.orders\n (" + "OrderID String, OrderName String, OrderPriority Int8)" + " engine = CnchMergeTree() partition by OrderID order by OrderID"; stmt.execute( createTableQuery ); } catch (SQLException ex) { ex.printStackTrace(); } } public static void insertTable(Connection connection) { try (Statement stmt = connection.createStatement()) { String insertQuery = "INSERT INTO inventory.orders VALUES ('54895','Apple',12)"; stmt.executeUpdate( insertQuery ); } catch (SQLException ex) { ex.printStackTrace(); } } public static void insertBatch(Connection connection) { String insertQuery = "INSERT INTO inventory.orders (OrderID, OrderName, OrderPriority) VALUES (?,'Apple',?)"; try (PreparedStatement 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(); } } public static void selectTable(Connection connection) { try (Statement stmt = connection.createStatement()) { String selectTableQuery = "SELECT * FROM inventory.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(); } } }
如果您在使用过程中遇到 Unsupported or unrecognized SSL message 报错,这是 Java 版本过低导致的,Java 1.8.0_261 **** 以下版本会出现此报错。
您可通过以下方式解决:
如果您在使用过程中遇到 DB::Exception: No local available worker group for vw-xxxx 报错,这是计算组尚未启动导致的。
您可通过以下方式解决:
登录 ByteHouse 云数仓版控制台,在计算组页面,查看您使用的计算组是否在 正在运行的状态。
如果您在使用过程中遇到 Exception: unknown error: Code: UNAUTHENTICATED 报错,这可能是 user与password 配置错误导致的。
您可通过以下方式解决:
再次确认您配置的user与password是否正确。
xxx.yyy doesn't exist如果您在使用过程中遇到 DB::Exception: Database xxx.yyy doesn't exist 报错,这可能是数据库名称配置错误导致的。
您可通过以下方式解决:
请确认连接信息中配置的数据库名称是否正确。
如果您在使用过程中遇到 Caused by: javax.net.ssl.SSLHandshakeException: No appropriate protocol (protocol is disabled or cipher suites are inappropriate) 报错,这可能是TLS版本不兼容导致的。
您可通过以下方式解决:
请在连接 URL 后面增加一个参数:enabledTLSProtocols=TLSv1.2,TLSv1.3。
举例如下:
String url = String.format("jdbc:bytehouse://%s:%d/%s?enabledTLSProtocols=TLSv1.2,TLSv1.3", host, port, database);