Skip to main content

JDBC ドライバー

clickhouse-jdbc は標準の JDBC インターフェースを実装しています。clickhouse-client の上に構築されており、カスタムタイプマッピング、トランザクションサポート、標準的な同期 UPDATE および DELETE 文などの追加機能を提供します。これにより、レガシーアプリケーションやツールでも簡単に使用できます。

Note

最新の JDBC (0.6.5) バージョンは Client-V1 を使用しています

clickhouse-jdbc API は同期的であり、一般により大きなオーバーヘッド(例:SQL パース、タイプマッピング/変換など)があります。パフォーマンスが重要な場合や、ClickHouse へのより直接的なアクセスを好む場合は、clickhouse-client の使用を検討してください。

環境要件

セットアップ

<!-- https://mvnrepository.com/artifact/com.clickhouse/clickhouse-jdbc -->
<dependency>
<groupId>com.clickhouse</groupId>
<artifactId>clickhouse-jdbc</artifactId>
<version>0.6.5</version>
<!-- すべての依存関係を含む uber jar を使用し、より小さい jar のために分類子を http に変更 -->
<classifier>all</classifier>
</dependency>

バージョン 0.5.0 以降、Apache HTTP クライアントを使用して Client がパックされています。パッケージに共有バージョンがないため、依存関係としてロガーを追加する必要があります。

<!-- https://mvnrepository.com/artifact/org.slf4j/slf4j-api -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>2.0.16</version>
</dependency>

設定

ドライバークラス: com.clickhouse.jdbc.ClickHouseDriver

URL 構文: jdbc:(ch|clickhouse)[:<protocol>]://endpoint1[,endpoint2,...][/<database>][?param1=value1&param2=value2][#tag1,tag2,...], 例:

  • jdbc:ch://localhostjdbc:clickhouse:http://localhost:8123 と同じです
  • jdbc:ch:https://localhostjdbc:clickhouse:http://localhost:8443?ssl=true&sslmode=STRICT と同じです
  • jdbc:ch:grpc://localhostjdbc:clickhouse:grpc://localhost:9100 と同じです

接続プロパティ:

プロパティデフォルト説明
continueBatchOnErrorfalseエラーが発生したときにバッチ処理を続行するかどうか
createDatabaseIfNotExistfalseデータベースが存在しない場合に作成するかどうか
custom_http_headersカンマ区切りのカスタム HTTP ヘッダー、例: User-Agent=client1,X-Gateway-Id=123
custom_http_paramsカンマ区切りのカスタム HTTP クエリパラメータ、例: extremes=0,max_result_rows=100
nullAsDefault00 - null 値をそのまま扱い、null 値を非null カラムに挿入する際に例外を投げる; 1 - null 値をそのまま扱い、挿入時のnull チェックを無効にする; 2 - クエリおよび挿入の両方でnull を対応するデータタイプのデフォルト値に置き換える
jdbcCompliancetrue標準の同期UPDATE/DELETE およびフェイクトランザクションをサポートするかどうか
typeMappingsClickHouse データタイプと Java クラスとのマッピングをカスタマイズし、getColumnType() および getObject(Class<?>) の結果に影響します。例: UInt128=java.lang.String,UInt256=java.lang.String
wrapperObjectfalsegetObject() がArray /Tuple に対して java.sql.Array / java.sql.Struct を返すかどうか

注: 詳細は JDBC specific configuration を参照してください。

サポートされているデータタイプ

JDBC ドライバーはクライアントライブラリと同じデータフォーマットをサポートしています。

Note
  • AggregatedFunction - ⚠️ SELECT * FROM table ... はサポートされていません
  • Decimal - 一貫性のために 21.9+ で SET output_format_decimal_trailing_zeros=1
  • Enum - 文字列と整数の両方として扱われることができます
  • UInt64 - long にマップされます (client-v1 では)

接続を作成する

String url = "jdbc:ch://my-server/system"; // デフォルトで http プロトコルとポート 8123 を使用

Properties properties = new Properties();

ClickHouseDataSource dataSource = new ClickHouseDataSource(url, properties);
try (Connection conn = dataSource.getConnection("default", "password");
Statement stmt = conn.createStatement()) {
}

シンプルなステートメント


try (Connection conn = dataSource.getConnection(...);
Statement stmt = conn.createStatement()) {
ResultSet rs = stmt.executeQuery("select * from numbers(50000)");
while(rs.next()) {
// ...
}
}

挿入

Note
  • PreparedStatementStatement の代わりに使用してください

使用は簡単ですが、入力関数(下記参照)と比較してパフォーマンスが遅くなります:

try (PreparedStatement ps = conn.prepareStatement("insert into mytable(* except (description))")) {
ps.setString(1, "test"); // id
ps.setObject(2, LocalDateTime.now()); // タイムスタンプ
ps.addBatch(); // パラメータはバイナリ形式でバッファされたストリームに即座に書き込まれます
...
ps.executeBatch(); // 手元のすべてをストリームして ClickHouse に送信します
}

入力テーブル関数を使用

優れたパフォーマンス特性を持つオプション:

try (PreparedStatement ps = conn.prepareStatement(
"insert into mytable select col1, col2 from input('col1 String, col2 DateTime64(3), col3 Int32')")) {
// カラム定義が解析され、ドライバーは 3 つのパラメータがあることを知ります: col1, col2 および col3
ps.setString(1, "test"); // col1
ps.setObject(2, LocalDateTime.now()); // col2, setTimestamp は遅いので推奨されません
ps.setInt(3, 123); // col3
ps.addBatch(); // パラメータはバイナリ形式でバッファーされたストリームに即座に書き込まれます
...
ps.executeBatch(); // 手元のすべてをストリームして ClickHouse に送信します
}

プレースホルダを使用した挿入

このオプションは、クライアント側で解析される長い SQL 式が必要になるため、小さな挿入の場合にのみ推奨されます:

try (PreparedStatement ps = conn.prepareStatement("insert into mytable values(trim(?),?,?)")) {
ps.setString(1, "test"); // id
ps.setObject(2, LocalDateTime.now()); // タイムスタンプ
ps.setString(3, null); // description
ps.addBatch(); // クエリにパラメータを追加
...
ps.executeBatch(); // 構成されたクエリを発行: insert into mytable values(...)(...)...(...)
}

DateTime とタイムゾーンの処理

java.sql.Timestamp ではなく java.time.LocalDateTime または java.time.OffsetDateTime、および java.sql.Date ではなく java.time.LocalDate を使用してください。

try (PreparedStatement ps = conn.prepareStatement("select date_time from mytable where date_time > ?")) {
ps.setObject(2, LocalDateTime.now());
ResultSet rs = ps.executeQuery();
while(rs.next()) {
LocalDateTime dateTime = (LocalDateTime) rs.getObject(1);
}
...
}

AggregateFunction の処理

Note

現時点では、groupBitmap のみがサポートされています。

// 入力関数を使用したバッチ挿入
try (ClickHouseConnection conn = newConnection(props);
Statement s = conn.createStatement();
PreparedStatement stmt = conn.prepareStatement(
"insert into test_batch_input select id, name, value from input('id Int32, name Nullable(String), desc Nullable(String), value AggregateFunction(groupBitmap, UInt32)')")) {
s.execute("drop table if exists test_batch_input;"
+ "create table test_batch_input(id Int32, name Nullable(String), value AggregateFunction(groupBitmap, UInt32))engine=Memory");
Object[][] objs = new Object[][] {
new Object[] { 1, "a", "aaaaa", ClickHouseBitmap.wrap(1, 2, 3, 4, 5) },
new Object[] { 2, "b", null, ClickHouseBitmap.wrap(6, 7, 8, 9, 10) },
new Object[] { 3, null, "33333", ClickHouseBitmap.wrap(11, 12, 13) }
};
for (Object[] v : objs) {
stmt.setInt(1, (int) v[0]);
stmt.setString(2, (String) v[1]);
stmt.setString(3, (String) v[2]);
stmt.setObject(4, v[3]);
stmt.addBatch();
}
int[] results = stmt.executeBatch();
...
}

// クエリパラメータとして bitmap を使用
try (PreparedStatement stmt = conn.prepareStatement(
"SELECT bitmapContains(my_bitmap, toUInt32(1)) as v1, bitmapContains(my_bitmap, toUInt32(2)) as v2 from {tt 'ext_table'}")) {
stmt.setObject(1, ClickHouseExternalTable.builder().name("ext_table")
.columns("my_bitmap AggregateFunction(groupBitmap,UInt32)").format(ClickHouseFormat.RowBinary)
.content(new ByteArrayInputStream(ClickHouseBitmap.wrap(1, 3, 5).toBytes()))
.asTempTable()
.build());
ResultSet rs = stmt.executeQuery();
Assert.assertTrue(rs.next());
Assert.assertEquals(rs.getInt(1), 1);
Assert.assertEquals(rs.getInt(2), 0);
Assert.assertFalse(rs.next());
}

HTTP ライブラリの設定

ClickHouse JDBC コネクターは、HttpClientHttpURLConnectionApache HttpClient の3つの HTTP ライブラリをサポートしています。

Note

HttpClient は JDK 11 以上でのみサポートされています。

JDBC ドライバーは、デフォルトで HttpClient を使用します。ClickHouse JDBC コネクターで使用する HTTP ライブラリを変更するには、次のプロパティを設定します。

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

対応する値の完全なリストは次の通りです:

プロパティ値HTTP ライブラリ
HTTP_CLIENTHTTPClient
HTTP_URL_CONNECTIONHttpURLConnection
APACHE_HTTP_CLIENTApache HttpClient

SSL を使用して ClickHouse に接続する

SSL を用いて ClickHouse にセキュアに JDBC で接続するには、SSL パラメータを含むように JDBC プロパティを設定する必要があります。これは通常、JDBC URL またはプロパティオブジェクトに sslmodesslrootcert などの SSL プロパティを指定することを含みます。

SSL プロパティ

名称デフォルト値オプション値説明
sslfalsetrue, false接続で SSL/TLS を有効にするかどうか
sslmodestrictstrict, noneSSL/TLS 証明書を確認するかどうか
sslrootcertSSL/TLS ルート証明書のパス
sslcertSSL/TLS 証明書のパス
sslkeyPKCS#8 フォーマットの RSA キー
key_store_typeJKS, PKCS12キーストア/トラストストアファイルのタイプまたはフォーマットを指定
trust_storeトラストストアファイルのパス
key_store_passwordキーストア設定で指定されたファイルにアクセスするためのパスワード

これらのプロパティは、Java アプリケーションが ClickHouse サーバーと暗号化された接続で通信し、データ転送中のセキュリティを強化します。

  String url = "jdbc:ch://your-server:8443/system";

Properties properties = new Properties();
properties.setProperty("ssl", "true");
properties.setProperty("sslmode", "strict"); // NONE はすべてのサーバーを信用し、STRICT は信頼できるもののみ
properties.setProperty("sslrootcert", "/mine.crt");
try (Connection con = DriverManager
.getConnection(url, properties)) {

try (PreparedStatement stmt = con.prepareStatement(

// ここにコードを記述

}
}

大規模挿入時の JDBC タイムアウトの解決

ClickHouse で実行時間の長い大規模挿入を行う場合、以下のような JDBC タイムアウトエラーに遭遇することがあります:

Caused by: java.sql.SQLException: Read timed out, server myHostname [uri=https://hostname.aws.clickhouse.cloud:8443]

これらのエラーはデータ挿入プロセスを妨げ、システムの安定性に影響を与える可能性があります。この問題を解決するには、クライアントの OS のいくつかのタイムアウト設定を調整する必要があります。

Mac OS

Mac OS では、次の設定を調整して問題を解決できます:

  • net.inet.tcp.keepidle: 60000
  • net.inet.tcp.keepintvl: 45000
  • net.inet.tcp.keepinit: 45000
  • net.inet.tcp.keepcnt: 8
  • net.inet.tcp.always_keepalive: 1

Linux

Linux では、同等の設定だけでは問題が解決しない場合があります。ソケットのキープアライブ設定を扱う方法が異なるため、追加の手順が必要です。次の手順に従ってください:

  1. /etc/sysctl.conf や関連する設定ファイルで次の Linux カーネルパラメータを調整します:

    • net.inet.tcp.keepidle: 60000
    • net.inet.tcp.keepintvl: 45000
    • net.inet.tcp.keepinit: 45000
    • net.inet.tcp.keepcnt: 8
    • net.inet.tcp.always_keepalive: 1
    • net.ipv4.tcp_keepalive_intvl: 75
    • net.ipv4.tcp_keepalive_probes: 9
    • net.ipv4.tcp_keepalive_time: 60 (デフォルトの300秒からこの値を下げることを検討してください)
  2. カーネルパラメータを変更した後、次のコマンドを実行して変更を適用します:

    sudo sysctl -p

これらの設定を行った後、クライアントがソケットのキープアライブオプションを有効にしていることを確認する必要があります:

properties.setProperty("socket_keepalive", "true");
Note

現在、clickhouse-java によってサポートされている他の 2 つの HTTP クライアントライブラリでは、ソケットオプションの設定が許可されていないため、ソケットのキープアライブを設定する際は Apache HTTP クライアントライブラリを使用する必要があります。詳しくは HTTP ライブラリの設定 を参照してください。

また、JDBC URL に同等のパラメータを追加することもできます。

JDBC ドライバーのデフォルトのソケットおよび接続タイムアウトは 30 秒です。このタイムアウトを大規模データ挿入操作をサポートするために拡大することができます。ClickHouseClientoptions メソッドを使用し、ClickHouseClientOption によって定義された SOCKET_TIMEOUT および CONNECTION_TIMEOUT オプションと共に使用します:

final int MS_12H = 12 * 60 * 60 * 1000; // 12 時間(ミリ秒)
final String sql = "insert into table_a (c1, c2, c3) select c1, c2, c3 from table_b;";

try (ClickHouseClient client = ClickHouseClient.newInstance(ClickHouseProtocol.HTTP)) {
client.read(servers).write()
.option(ClickHouseClientOption.SOCKET_TIMEOUT, MS_12H)
.option(ClickHouseClientOption.CONNECTION_TIMEOUT, MS_12H)
.query(sql)
.executeAndWait();
}