ByteHouse JDBC Driver is a driver that enables Java applications to connect to the ByteHouse cloud data warehouse through the JDBC interface. It supports data querying, insertion, and other operations, helping Java developers quickly integrate ByteHouse and achieve efficient data access. This article introduces how to connect to and access the ByteHouse cloud data warehouse using JDBC.
Support Java 1.8.0_261 or later (requires TLS v1.3 support).
It is recommended to use the latest version of ByteHouse JDBC Driver. Go to the ByteHouse Java Driver index page to obtain the latest version of the ByteHouse JDBC Driver jar file.
Some SQL statements and functions are affected by the driver version. For details, refer to the table below.
SQL/Function | Driver version |
|---|---|
replace into | ≥1.1.67 |
Please click to go to the ByteHouse Java Driver index page. There, you can obtain the latest version of the ByteHouse JDBC Driver jar file.
After obtaining the jar file, simply add the compiled jar file to your project, or use your chosen dependency management tool to add this project as a dependency. Then, you can import and use classes in a Java program.
The following is an example of using the ByteHouse JDBC driver with Gradle:
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" }
tip
In the above example, java:1.1.74 is a sample version number. In practice, you need to replace it with the latest version number. Go to the ByteHouse Java Driver index page to obtain the latest version of the driver file.
Examples of using the ByteHouse JDBC driver via Maven are as follows:
// 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>
tip
In the above example, java:1.1.74 is a sample version number. When using this example, replace the sample version number with the latest version number. Go to the ByteHouse Java Driver index page to obtain the latest version of the driver file.
ByteHouse supports connecting to the ByteHouse JDBC Driver using IAM users or database users. The differences between IAM users and database users are described below; you may choose as needed.
For more information about IAM users and database users, refer to the following documents:
Please refer to步骤三:获取 ByteHouse 连接串信息Learn how to connect to ByteHouse using IAM user.
General parameter description as follows
Param | Configuration description |
|---|---|
Host | Configured as ByteHouse's public or private domain name, you can view and copy the domain name information in the ByteHouse Console under Tenant Management > Basic Information > Network Information, for details please see步骤二:配置网络信息。 |
Port | Fixed at 19000. |
User | Fixed to bytehouse. |
Password | To obtain the ByteHouse API Key, you can get it in the ByteHouse Console under Tenant Management > Connection Information. For details, see获取 API Key。 |
Database | Configure the ByteHouse database name for connection. You can log in to the ByteHouse Console and view and copy databases on the Database page. |
virtual_warehouse | This is an optional configuration that is set to the virtual warehouse group name. To specify a virtual warehouse for query and write operations, configure this parameter. |
Please refer to步骤三:获取 ByteHouse 连接串信息Learn how to connect to ByteHouse using a database user account.
General parameter descriptions are as follows:
Param | Configuration description |
|---|---|
Host | The domain name information configured as ByteHouse's public or private domain name can be viewed and copied in the ByteHouse Console under Tenant Management > Basic Information > Network Information. For details, see步骤二:配置网络信息。 |
Port | Fixed at 19000. |
User | Username is configured as
Usage examples are as follows:
|
Password | The database account password is custom-configured by the administrator when creating the account. You may contact the administrator to obtain the password. If your password is lost or forgotten, you can contact the administrator to reset it. For details, please refer to重置密码。 |
Database | Configure the ByteHouse database name for connection. Log in to the ByteHouse Console. On the Database page, you can view and copy databases. |
virtual_warehouse | Optional configuration. Configure as the virtual warehouse name. To specify a virtual warehouse for query and write operations, configure this parameter. |
You can use the following code to connect to ByteHouse and start using standard statements to develop using ByteHouse for querying, writing, and reading data.
keepAlive enables connection reuse and avoids short-lived connections.You can refer to the code below to connect to ByteHouse. When using the code below, be sure to replace the following fields in the connection statement: {host}, {Password}, {User}, {Database}, {VITUAL_WAREHOUSE_ID}, and other connection information fields. For information on how to obtain these fields, see获取 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}"; // Use properties to set some basic parameters of the link String url = String.format("jdbc:bytehouse://%s:%d", host, port); Properties properties = new Properties(); // Different virtual warehouses can be set as needed 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(); } } }
After establishing a connection with ByteHouse, you can use the following code to create a database and a table.
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(); } } } /* * Create database */ 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(); } } } /* * Create table */ 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(); } } }
You can use the following code to write data.
/* * Insert data */ 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(); } } } /* * Batch insert data */ 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(); } } }
You can use the following code to query data.
/* * Query data */ 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(); } } } }
Setting a query ID helps track query execution status and facilitates subsequent debugging or management of query tasks. You can use the native JDBC method or the ByteHouse-specific interface to set the query ID for a query.
To set the query ID for a query statement, refer to the following code and add the stmt.setQueryId(String.format("customized_%s", UUID.randomUUID())); code.
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(); } } }
Starting from version v1.1.83, the ByteHouse JDBC driver supports the feature for assigning a custom Query ID to queries.
Before using the Query ID feature, please ensure that the following conditions are satisfied:
Connection → ByteHouseConnectionStatement → ByteHouseStatementPreparedStatement → ByteHousePreparedStatementIf a Query ID is not explicitly specified during use, the ByteHouse JDBC driver will randomly and automatically generate a Query ID and associate it with the query.
API | Configuration description |
|---|---|
| Set the Query ID only for the next query.
|
| Set the Query ID directly in ByteHouseStatement.
|
| Due to limitations in the interaction between JDBC and ByteHouse, the Query ID must be set before the
|
| Return the last Query ID after connection.
|
You can use the session_query_id_prefix property to set a session-level prefix for all Query IDs.
Properties props = new Properties(); props.setProperty("session_query_id_prefix", "my-spark-application-"); Connection connection = new ByteHouseDriver().connect(jdbcUrl, props);
JDBC automatically adds an incrementing counter to each query's prefix:
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"
The driver program concatenates the prefix with the user-specified Query ID. Does not increment automatically.
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 supports JDBC connections and data development using HikariCP and Druid.
The following example shows how to connect to HikariCP using the ByteHouse JDBC driver.
When using the ByteHouse JDBC driver with Maven, add the following dependencies to the pom.xml file to include HikariCP:
<dependency> <groupId>com.zaxxer</groupId> <artifactId>HikariCP</artifactId> <version>4.0.3</version> </dependency>
When using the ByteHouse JDBC driver with Gradle, add the following dependency to the build.gradle file to include HikariCP:
dependencies { implementation 'com.zaxxer:HikariCP:4.0.3' }
When you use HikariCP, the class you interact with most frequently is HikariDataSource. You can configure and use HikariCP in a Java application as shown in the following example to create a Bytehouse connection.
You can refer to the code below to connect to ByteHouse. When using it, be sure to replace the connection information fields in the connection statement, such as host, port, password, user, database, virtual_warehouse_id, and more. For information on how to obtain these fields, please refer to获取 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); // Standard Hikari configuration config.setJdbcUrl(jdbcURL); config.setMinimumIdle(5); // Min number of free connections in the pool config.setMaximumPoolSize(20); // Max connections in the pool config.setMaxLifetime(4 * 60 * 1000); // The connection will expire (be reclaimed) in 4 minutes, with a maximum allowed lifetime of 5 minutes config.setIdleTimeout(2 * 60 * 1000); // After the connection is idle for 2 minutes, it meets the condition for eviction (recycling) 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(); } } }
You can also connect to ByteHouse through Alibaba's Druid connection pool.
When using the ByteHouse JDBC driver with Maven, add the following dependencies to the pom.xml file to include Druid:
<dependency> <groupId>com.alibaba</groupId> <artifactId>druid</artifactId> <version>1.1.23</version> </dependency>
Please add the following dependencies to the build.gradle file to include Druid:
dependencies { implementation 'com.alibaba:druid:1.1.23' }
When using Druid, the class you interact with most frequently is DruidDataSource. You can configure and use Druid in a Java application to create a Bytehouse connection by following the example below.
Refer to the following to connect to ByteHouse. Caution: When connecting, be sure to replace the fields in the connection statement, such as host, port, password, user, database, virtual_warehouse_id, and more. For information on how to obtain these values, see获取 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); // Max active connections druidDataSource.setInitialSize(2); // Initial connection count druidDataSource.setMinIdle(2); // Min idle connection count druidDataSource.setMaxWait(50000); // Max wait time for obtaining a connection 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(); } } }
If you encounter the Unsupported or unrecognized SSL message error during use, it is caused by a Java version that is too low. This error occurs in Java versions below 1.8.0_261 **** .
You can resolve this in the following ways:
If you encounter the error DB::Exception: No local available worker group for vw-xxxx while using the system, this is caused by the virtual warehouse not started yet.
You can resolve this in the following ways:
Log in to the ByteHouse cloud data warehouse console. On the virtual warehouse page, check whether your virtual warehouse is in the running status.
If you encounter the Exception: unknown error: Code: UNAUTHENTICATED error during use, this may be caused by incorrect configuration of user and password.
You can resolve this in the following ways:
Please ensure that the configured user and password are correct.
xxx.yyy doesn't exist If you encounter the DB::Exception: Database xxx.yyy doesn't exist error while using the system, this may be caused by a database name configuration error.
You can resolve this in the following ways:
Please confirm that the database name configured in the connection information is correct.
If you encounter the Caused by: javax.net.ssl.SSLHandshakeException: No appropriate protocol (protocol is disabled or cipher suites are inappropriate) error while using the product, this may be caused by incompatible TLS versions.
You can resolve this using the following methods:
Please add the following parameter to the end of the connection URL: enabledTLSProtocols=TLSv1.2,TLSv1.3.
The examples are as follows:
String url = String.format("jdbc:bytehouse://%s:%d/%s?enabledTLSProtocols=TLSv1.2,TLSv1.3", host, port, database);