How to Connect to ClickHouse from Java
ClickHouse ships an official JDBC driver, so connecting from Java looks like any other relational database: a JDBC URL, a Connection, and PreparedStatement. The one thing that trips people up is the port. The JDBC driver talks over ClickHouse's HTTP interface on port 8123, not the native TCP protocol on 9000. Point it at 9000 and you get a hang or a protocol error that looks like a network problem. This guide covers the driver, the connection code, batch inserts, ClickHouse Cloud, and the errors you will actually hit.
The driver
| Library | Layer | When to use |
|---|---|---|
com.clickhouse:clickhouse-jdbc | Official JDBC driver | Standard JDBC code, existing tooling, ORMs, BI connectors. |
com.clickhouse:client-v2 | Official Java client | New code where you want direct access and the lowest overhead. |
clickhouse-jdbc with :all classifier | Shaded JDBC jar | When you want all dependencies bundled into one jar (the usual choice). |
As of June 2026 the JDBC driver (0.9.x) is a thin layer over the v2 Java client. ClickHouse's own docs recommend the client directly when raw performance matters, but JDBC is the right choice when you need to plug into existing JDBC-based tooling, a connection pool, or an ORM. This guide uses the JDBC driver.
Adding the dependency
The :all classifier pulls in a shaded jar with the driver's dependencies included, which avoids classpath conflicts.
Maven:
<dependency>
<groupId>com.clickhouse</groupId>
<artifactId>clickhouse-jdbc</artifactId>
<version>0.9.8</version>
<classifier>all</classifier>
</dependency>Gradle:
implementation 'com.clickhouse:clickhouse-jdbc:0.9.8:all'The driver requires Java 8 or higher; Java 11 or later is recommended in production for better TLS support (as of June 2026). Modern JDBC drivers register themselves through the service-provider mechanism, so Class.forName("com.clickhouse.jdbc.ClickHouseDriver") is unnecessary.
The connection URL (and the port trap)
jdbc:clickhouse://host:8123/database
The default HTTP port is 8123, not 9000. Port 9000 is ClickHouse's native protocol, which the JDBC driver does not speak. This is the single most common mistake: people copy a port from a clickhouse-client example (which uses 9000) into a JDBC URL and then debug a "connection" problem that is really a wrong-port problem.
For a TLS endpoint (including ClickHouse Cloud), the HTTPS port is 8443:
jdbc:clickhouse://host:8443/database?ssl=true
A bare URL with no database connects to the default database.
A first connection
import java.sql.*;
import java.util.Properties;
public class ClickHouseExample {
public static void main(String[] args) throws SQLException {
String url = "jdbc:clickhouse://localhost:8123/default";
Properties props = new Properties();
props.setProperty("user", "default");
props.setProperty("password", "");
try (Connection conn = DriverManager.getConnection(url, props);
Statement stmt = conn.createStatement();
ResultSet rs = stmt.executeQuery("SELECT version()")) {
if (rs.next()) {
System.out.println("ClickHouse version: " + rs.getString(1));
}
}
}
}try-with-resources closes the ResultSet, Statement, and Connection in the right order even if a query throws.
Parameterized queries
Use PreparedStatement with ? placeholders. This keeps values escaped correctly and is the only safe way to build queries from user input.
String sql = "SELECT event_date, count() AS hits " +
"FROM events WHERE app_id = ? AND event_date >= ? " +
"GROUP BY event_date ORDER BY event_date";
try (PreparedStatement ps = conn.prepareStatement(sql)) {
ps.setString(1, "checkout");
ps.setDate(2, Date.valueOf("2026-01-01"));
try (ResultSet rs = ps.executeQuery()) {
while (rs.next()) {
System.out.println(rs.getDate("event_date") + ": " + rs.getLong("hits"));
}
}
}Batch inserts (and the "too many parts" trap)
ClickHouse is built for large, infrequent inserts, not row-at-a-time writes. Every INSERT creates a new data part on disk, and parts are merged in the background. Insert one row at a time in a tight loop and you generate thousands of tiny parts, which triggers the TOO_MANY_PARTS error and degrades read performance. Batch your writes.
String sql = "INSERT INTO events (event_date, app_id, user_id) VALUES (?, ?, ?)";
try (PreparedStatement ps = conn.prepareStatement(sql)) {
for (Event e : events) {
ps.setDate(1, Date.valueOf(e.date()));
ps.setString(2, e.appId());
ps.setLong(3, e.userId());
ps.addBatch();
}
ps.executeBatch();
}A single executeBatch() with thousands of rows is far healthier than thousands of single inserts. For very high write rates, look at ClickHouse's async_insert setting, which buffers small inserts server-side, rather than fighting it from the client.
Connection pooling
The JDBC driver does not pool connections. For a web application, wrap it in HikariCP:
import com.zaxxer.hikari.HikariConfig;
import com.zaxxer.hikari.HikariDataSource;
HikariConfig config = new HikariConfig();
config.setJdbcUrl("jdbc:clickhouse://localhost:8123/default");
config.setUsername("default");
config.setPassword("");
config.setMaximumPoolSize(10);
try (HikariDataSource ds = new HikariDataSource(config);
Connection conn = ds.getConnection()) {
// use conn
}Because ClickHouse queries are usually heavy analytical scans rather than thousands of cheap OLTP transactions, you typically need a smaller pool than you would for PostgreSQL or MySQL. A handful of connections per application instance is normally enough; oversizing the pool just queues expensive queries against finite server resources.
ClickHouse Cloud
ClickHouse Cloud requires TLS and a password. Use the 8443 HTTPS port and enable SSL:
jdbc:clickhouse://your-instance.clickhouse.cloud:8443/default?ssl=true
Properties props = new Properties();
props.setProperty("user", "default");
props.setProperty("password", System.getenv("CLICKHOUSE_PASSWORD"));
props.setProperty("ssl", "true");Keep the password in an environment variable or secret manager, not in source. Cloud services also idle-suspend; the first query after idle can be slow while the service wakes, so set a generous socket timeout for that path.
Errors you will actually hit
| Symptom | Likely cause | Fix |
|---|---|---|
| Connection hangs or protocol error | URL points at the native port 9000 instead of HTTP 8123 | Use 8123 (or 8443 for TLS) in the JDBC URL |
Code: 516. Authentication failed | Wrong user or password | Verify credentials; check the user is allowed from your host IP |
Code: 252. TOO_MANY_PARTS | Row-by-row inserts creating too many small parts | Batch inserts, or enable async_insert server-side |
No suitable driver found for jdbc:clickhouse:... | Driver not on the classpath | Confirm the clickhouse-jdbc dependency (with :all) resolved |
| TLS handshake failure on Cloud | Missing ssl=true or wrong port | Use port 8443 and ssl=true |
| Query times out after idle (Cloud) | Service was suspended and is waking | Increase socket timeout; retry once |
The No suitable driver found case is almost always a build problem: the dependency is missing, scoped to test, or shaded out of the final jar.
A note on querying after you connect
Once the connection works, the real work is writing the SQL: confirming a table exists, eyeballing a result set, checking that a batch insert landed. You can do that from clickhouse-client, from a GUI (see Best ClickHouse GUI Clients), or with Mako. For loading data, see Import CSV to ClickHouse and Import JSON to ClickHouse. If you are choosing between engines, ClickHouse vs BigQuery and PostgreSQL vs ClickHouse cover the tradeoffs.
Version notes (as of June 2026): clickhouse-jdbc is at 0.9.x, built on the v2 Java client; HikariCP is at 7.x. The driver targets Java 8 and up, with Java 11+ recommended for production.
Mako connects to ClickHouse with AI-powered autocomplete for writing queries. Try it free at mako.ai.
Skip the terminal. Use Mako.
Connect your database, write queries with AI assistance, and import/export data in clicks. Free to start.