clickhouse-jdbc は、最新の Java クライアントを用いて標準的な JDBC インターフェイスを実装しています。
パフォーマンスや直接アクセスが重要な場合は、最新の Java クライアントを直接使用することを推奨します。環境要件
- OpenJDK バージョン 8 以上
Setup
- Maven
- Gradle (Kotlin)
- Gradle
- Maven Central から入手し、クラスパスに追加します
- バージョン
0.9.4以降、https://mvnrepository.com/artifact/com.clickhouse/clickhouse-jdbc-all のアーティファクトが利用できます - シェーディング済みの依存関係をすべて含む jar を取得するには、修飾子
allを使用します。
- バージョン
- または公式リポジトリのこちらから
設定
ドライバークラス:com.clickhouse.jdbc.ClickHouseDrivercom.clickhouse.jdbc.ClickHouseDriver は、新旧の JDBC 実装に対するファサードクラスです。デフォルトでは、新しい JDBC 実装が使用されます。
古い JDBC 実装を使用するには、clickhouse.jdbc.v1 システムプロパティを true に設定します。このプロパティは、Driver クラスを呼び出す前に設定する必要があります。バージョンを切り替える別の方法として、各バージョンの Driver クラスを直接使用することもできます。com.clickhouse.jdbc.Driverは新しい JDBC 実装 (V2) です。com.clickhouse.jdbc.DriverV1は古い JDBC 実装 (V1) です。
jdbc:(ch|clickhouse)[:<protocol>]://endpoint[:port][/<database>][?param1=value1¶m2=value2][#tag1,tag2,...]、例:jdbc:clickhouse:http://localhost:8123jdbc:clickhouse:https://localhost:8443?ssl=true
- URL には 1 つのエンドポイントのみ指定できます
- デフォルトの ‘HTTP’ 以外を使用する場合は、プロトコルを指定する必要があります
- ポートは、デフォルトの ‘8123’ 以外を使用する場合に指定する必要があります
- ドライバーはポート番号からプロトコルを推測しないため、明示的に指定する必要があります
- プロトコルが指定されている場合、
sslパラメータは不要です。
接続プロパティ
主な設定パラメーターは Java クライアント で定義されています。これらはそのままドライバーに渡してください。ドライバー固有のプロパティでクライアント設定に含まれないものは、以下に一覧を示します。ドライバーのプロパティ:設定例:
クライアント識別
リクエストを発行したアプリケーションを識別する方法は2つあります。接続プロパティでcom.clickhouse.client.api.ClientConfigProperties#CLIENT_NAME を設定するか、java.sql.Connection#setClientInfo(String name, String value) メソッドを使用します。showLineNumbers
showLineNumbers
http_user_agent の値は次のようになります:client_name プロパティには app_name/version フォーマットの使用を推奨します。クエリログでアプリケーションを識別しやすくなるためです。オペレーションの識別
JDBCドライバーは操作ごとにquery_id を生成します (現在はサーバーの例外に含まれています) 。操作に対して log_comment を設定するには、com.clickhouse.jdbc.StatementImpl#getLocalSettings メソッドを使用します。これには、Statement または PreparedStatement を事前に com.clickhouse.jdbc.StatementImpl にキャストする必要があります。showLineNumbers
localSettings はスレッド間で共有されるため、このアプローチはステートメントのシングルスレッド使用時にのみ有効です。サポートされているデータ型
JDBCドライバーは、基盤となるJava クライアントと同じデータフォーマットをサポートしています。JDBC 型マッピング
以下のマッピングが適用される対象:ResultSet#getObject(columnIndex)- 対応する Java クラスのオブジェクトを返すメソッドです。(Int8->java.lang.Byte,Int16->java.lang.Shortなど)ResultSetMetaData#getColumnType(columnIndex)- メソッドは、対応するJDBC型を返します。(Int8->java.lang.Byte,Int16->java.lang.Short, など)
ResultSet#getObject(columnIndex, class)- このメソッドは値をclass型に変換しようとします。変換にはいくつか制限があります。詳細は各セクションを参照してください。
- 数値型は相互に変換できます。したがって、
Int8はFloat64として取得でき、逆も同様です。:rs.getObject(1, Float64.class)は、Int8カラムの値をFloat64として返します。rs.getLong(1)は、Int8カラムの値をLongとして返します。rs.getByte(1)は、Byteに収まる場合、Int16カラムの値をByteとして返すことができます。
- より広い型からより狭い型への変換は、データが破損するおそれがあるため推奨されません。
Bool型は数値としても扱われます。- すべての数値型は
java.lang.Stringとして読み出すことができます。 - Java の
Float.MAX_VALUEをFloatとして保存すると問題が発生します (https://github.com/ClickHouse/clickhouse-java/issues/809) 。同じ値をDoubleとして保存すれば、この問題は解決します。
Stringは、java.lang.Stringまたはbyte[]としてのみ読み込めます。FixedStringはそのまま読み取られ、カラムの長さに合わせてゼロで埋められます。 (たとえば、'John'に対するFixedString(10)は'John\0\0\0\0\0\0\0\0\0'として読み取られます。)
Enum8とEnum16は、デフォルトではjava.lang.Stringにマッピングされます。- Enum 値は、指定された getter メソッドまたは
getObject(columnIndex, Integer.class)メソッドを使用して、数値として読み取ることができます。 Enum16は内部的に short にマッピングされ、Enum8は byte にマッピングされます。データ破損のリスクがあるため、Enum16を byte として読み取るのは避けてください。- Enum の値は、
PreparedStatementでは文字列または数値として設定できます。
- Date / Time 型は、JDBC との互換性を高めるため、
java.sql型にマッピングされます。ただし、ResultSet#getObject(columnIndex, Class<T>)を使用し、第 2 引数に対応するクラスを指定することで、java.time.LocalDate、java.time.LocalDateTime、java.time.LocalTimeを取得することもできます。rs.getObject(1, java.time.LocalDate.class)は、Dateカラムの値をjava.time.LocalDateとして返します。rs.getObject(1, java.time.LocalDateTime.class)は、DateTimeカラムの値をjava.time.LocalDateTimeとして返します。rs.getObject(1, java.time.LocalTime.class)は、Timeカラムの値をjava.time.LocalTimeとして返します。
Date,Date32,Time,Time64はサーバーのタイムゾーンの影響を受けません。DateTimeとDateTime64は、サーバーまたはセッションのタイムゾーンの影響を受けます。DateTimeとDateTime64は、getObject(colIndex, ZonedDateTime.class)を使用してZonedDateTimeとして取得できます。
- JDBC との互換性を確保するため、
Arrayはデフォルトでjava.sql.Arrayにマッピングされます。これは、返される配列値についてより多くの情報を提供するためでもあります。型推論に役立ちます。 Arrayは、元の配列と同じ内容のjava.sql.ResultSetを返すgetResultSet()メソッドを実装しています。- コレクション型を
java.lang.Stringとして読み取るべきではありません。この形式はデータの有効な表現方法ではないためです (例: 配列内の文字列値はクォートされません) 。 MapはJAVA_OBJECTに対応付けられます。値はgetObject(columnIndex, Class<T>)メソッドでしか読み取れないためです。Mapには名前付きカラムがないため、java.sql.Structではありません。
Tupleは異なる型を含めることができ、Listは使用できないため、Object[]にマップされます。Tupleは、getObject(columnIndex, Array.class)メソッドを使用してArrayとして読み取れます。この場合、Array#baseTypeNameはTupleのカラム定義を返します。
java.sql.Connection#createArrayOf を使用して java.sql.Array オブジェクトをインスタンス化します。このオブジェクトは、異なるデータベース間でのArray処理を統一するために設計されています。
Arrayファクトリメソッドにconfigurationを渡すには、Connectionが必要です。このメソッドは2つの引数を受け取ります:typeName- 配列要素の型名。例:Array(Int32)->"Int32"。elements- 配列の実際の要素。たとえば[[1, 2, 3], [4, 5, 6]]->new Integer[][] {{1, 2, 3}, {4, 5, 6}}。
Object[]またはjava.sql.Structとして表現できます (タプルの記述方法については以下を参照してください) 。例ResultSet#getArray(columnIndex) を使用して Array オブジェクトを読み取ります。このオブジェクトを使用すると、任意のネスト深度のArrayにアクセスできます。
Array#getResultSet() メソッドを使用すると、配列要素を java.sql.ResultSet として統一的な方法で読み取ることができます。配列要素の正確な型が不明な場合に便利です。例com.clickhouse.data.Tupleオブジェクトにマッピングされます。書き込む際は、setObject(columnIndex, tuple)メソッドを呼び出してこのオブジェクトとして渡してください。
移植性を高めるために、java.sql.Structオブジェクトを使用してTupleを書き込むこともできます。例getObject(columnIndex) は Object[] を返します。Tupleは getObject(columnIndex, Array.class) メソッドを使用して java.sql.Array として読み取ることができます。例java.collections.Map オブジェクトとしてのみ書き込み可能です。このデータ型はキー・バリューペアを必要とするためです (java.sql.Struct はキー・バリューペアをサポートしていません) 。例getObject(columnIndex, Map.class)メソッドを使用することで、java.collections.Mapオブジェクトとして読み取ることができます。例java.sql.Connection#createStruct を使用して java.sql.Struct オブジェクトをインスタンス化します。このオブジェクトは、異なるデータベース間でネストされた処理を統一するために設計されています。
Struct ファクトリメソッドに設定を渡すには、コネクションが必要です。このメソッドは2つの引数を受け取ります:typeName- ネストされた要素の型名です。たとえばNested(Tuple(Int32, String))->"Nested(Tuple(Int32, String))"。elements- 実際にネストされた要素。たとえば、[1, 'test']->new Object[] {1, 'test'}。
ResultSet#getStruct(columnIndex, StructDescriptor) を使用して Nested オブジェクトを読み取ります。このオブジェクトを使用すると、任意のネスト深度のネストにアクセスできます。
Struct#getResultSet() メソッドを使用すると、ネストされた要素を java.sql.ResultSet と同様の統一的な方法で読み取ることができます。ネストされた要素の正確な型が不明な場合に便利です。例NullableとLowCardinalityの型
NullableとLowCardinalityは、ほかの型をラップする特殊な型です。Nullableは、ResultSetMetaDataで型名がどのように返されるかに影響します
UUIDは JDBC の標準型ではありません。ただし、JDK には含まれています。デフォルトでは、getObject()メソッドはjava.util.UUIDを返します。getObject(columnIndex, String.class)メソッドを使用すると、UUIDはStringとして読み書きできます。IPv4とIPv6は JDBC の標準型ではありません。ただし、JDK には含まれています。デフォルトでは、getObject()メソッドはjava.net.Inet4Addressとjava.net.Inet6Addressを返します。IPv4とIPv6は、getObject(columnIndex, String.class)メソッドを使うと、Stringとして読み書きできます。
日付、時刻、タイムゾーンの処理
日付/時刻およびタイムスタンプの処理におけるドライバーの動作と注意点については、Date/Time Guideを参照してください。接続の作成
認証情報と設定の指定
showLineNumbers
シンプルなステートメント
showLineNumbers
インサート
showLineNumbers
HikariCP
showLineNumbers
詳細情報
詳細については、GitHub リポジトリおよび Java クライアントのドキュメントを参照してください。トラブルシューティング
ロギング
ドライバーはロギングにslf4jを使用し、classpath上で最初に見つかった実装を使用します。大規模インサート時のJDBCタイムアウトの解消
ClickHouseで実行時間の長い大規模なinsertを実行する際、次のようなJDBCタイムアウトエラーが発生する場合があります:Mac OS
Mac OS では、以下の設定を調整することで問題を解決できます:net.inet.tcp.keepidle: 60000net.inet.tcp.keepintvl: 45000net.inet.tcp.keepinit: 45000net.inet.tcp.keepcnt: 8net.inet.tcp.always_keepalive: 1
Linux
Linuxでは、同等の設定だけでは問題が解決しない場合があります。Linuxのソケットキープアライブ設定の処理方法が異なるため、追加の手順が必要です。以下の手順に従ってください。/etc/sysctl.confまたは関連する設定ファイルで、以下の Linux カーネルパラメーターを調整します。
net.inet.tcp.keepidle: 60000net.inet.tcp.keepintvl: 45000net.inet.tcp.keepinit: 45000net.inet.tcp.keepcnt: 8net.inet.tcp.always_keepalive: 1net.ipv4.tcp_keepalive_intvl: 75net.ipv4.tcp_keepalive_probes: 9net.ipv4.tcp_keepalive_time: 60 (この値は、デフォルトの300秒から引き下げることを検討してもよいでしょう)
- カーネルパラメータを変更した後、次のコマンドを実行して変更を反映してください。
移行ガイド
主な変更点
- JDBC V2 は、より軽量な実装となっており、一部の機能が削除されています。
- ストリーミングデータはJDBC仕様やJavaに含まれていないため、JDBC V2ではサポートされていません。
- JDBC V2 では明示的な設定が必要です。フェイルオーバーのデフォルト設定はありません。
- URLにはプロトコルを指定する必要があります。ポート番号による暗黙的なプロトコル判別は行われません。
設定の変更
enumは2つのみです:com.clickhouse.jdbc.DriverProperties- ドライバー固有の設定プロパティ。com.clickhouse.client.api.ClientConfigProperties- クライアント設定のプロパティです。クライアント設定の 変更点については、Java クライアントのドキュメントで説明しています。
- まず URL からプロパティが解析されます。それらは他のすべてのプロパティを上書きします。
- ドライバーのプロパティはクライアントには渡されません。
- エンドポイント (ホスト、ポート、プロトコル) はURLから抽出されます。
データ型の変更
数値型- 最大の違いは、移植性を高めるために、符号なし型がJavaの型にマッピングされている点です。
FixedStringは、どちらのバージョンでもそのまま読み取られます。たとえば、'John'に対するFixedString(10)は'John\0\0\0\0\0\0\0\0\0'として読み取られます。PreparedStatement#setBytesを使用すると、unhex('<hex_string>')に変換され、その後Stringとして読み込まれます。- StringはUTF-8でエンコードされて保存されます。
TimeとTime64は、V2 でのみ新しい型としてサポートされています。DateTimeとDateTime64は、JDBC との互換性を高めるため、java.sql.Timestampにマッピングされます。
ネストされた型
- V2 では、JDBC との互換性を保つため、
Arrayはデフォルトでjava.sql.Arrayにマッピングされます。これは、返される配列値に関する情報をより多く提供するためでもあります。型推論に役立ちます。 - V2では、
Arrayは元の配列と同じ内容を持つjava.sql.ResultSetを返すgetResultSet()メソッドを実装しています。 - V1 では
MapにSTRUCTを使用しますが、返されるのは常にjava.util.Mapオブジェクトです。V2 では、MapをJAVA_OBJECTにマッピングすることでこの問題を修正しています。 - V1 では
TupleにSTRUCTを使用しますが、返り値は常にList<Object>オブジェクトです。V2 ではTupleはOTHERにマップされ、デフォルトではObject[]が返されます。 - V2 では、タプルを書き込むための
com.clickhouse.data.Tuple#Tupleが導入されました。これにより、値がタプルなのか配列なのかを簡単に見分けられます。 PreparedStatement#setBytesとResultSet#getBytesは、コレクション型では使用できません。これらのメソッドは、バイナリ文字列を扱うように設計されています。- 通常、Array型の書き込みと読み取りには
java.sql.Arrayを使用します。JDBCドライバーはこれを完全にサポートしています。 - V2 では
NestedはArrayにマッピングされ、タプルの配列として扱われます。 - V2 は
java.sql.Structを部分的にサポートしています。これは Array 型と非常によく似ていますが、キー・バリューのペアはサポートしていません。StructはTupleの値を書き込むために使用できます。
NullableとLowCardinalityの型
NullableとLowCardinalityは、他の型を包む特殊な型です。- V2では、これらの型に変更はありません。
- V1 では
UUIDにVARCHARを使用していますが、常にjava.util.UUIDオブジェクトを返します。V2 では、UUIDをOTHERにマッピングすることでこの問題を修正し、java.util.UUIDオブジェクトを返します。 - V1 では
IPv4とIPv6にVARCHARを使用していますが、返されるのは常にjava.net.Inet4Addressおよびjava.net.Inet6Addressオブジェクトです。V2 では、IPv4とIPv6をOTHERにマッピングすることでこの問題を修正し、java.net.Inet4Addressおよびjava.net.Inet6Addressオブジェクトを返すようになりました。 DynamicとVariantは V2 で導入された新しい型です。V1 ではサポートされていません。JSONはDynamic型をベースにしています。そのため、V2 でのみサポートされます。- IPv4 および IPv6 の値は、
getBytes(columnIndex)メソッドを使用してbyte[]として読み取ることもできます。ただし、これらの型については専用のクラスを使用することを推奨します。 - V2 は、変換が InetAddress クラスでより適切に実装されているため、IP アドレスを数値として読み取ることには対応していません。
データベースメタデータの変更
- V2 では、データベースの名称には
Schemaのみを使用します。Catalogは今後のために予約されています。 - V2 では、
DatabaseMetaData.supportsTransactions()とDatabaseMetaData.supportsSavepoints()はfalseを返します。これは今後変更される予定です。