clickhouse-jdbc는 최신 Java 클라이언트를 사용해 표준 JDBC 인터페이스를 구현합니다.
성능이나 직접 접근이 중요하다면 최신 Java 클라이언트를 직접 사용할 것을 권장합니다.환경 요구 사항
- OpenJDK 버전 >= 8
Setup
- Maven
- Gradle (Kotlin)
- Gradle
- Maven Central에서 가져와 classpath에 추가합니다
0.9.4버전부터는 https://mvnrepository.com/artifact/com.clickhouse/clickhouse-jdbc-all 아티팩트를 사용할 수 있습니다.- 모든 shaded 종속성이 포함된 jar를 받으려면 한정자
all을 사용하십시오.
- 또는 공식 리포지토리의 여기에서 확인할 수 있습니다
구성
드라이버 클래스:com.clickhouse.jdbc.ClickHouseDrivercom.clickhouse.jdbc.ClickHouseDriver는 신규 및 기존 JDBC 구현을 위한 파사드 클래스입니다. 기본적으로는 신규 JDBC 구현을 사용합니다.
기존 JDBC 구현을 사용하려면 clickhouse.jdbc.v1 system 속성을 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에는 단 하나의 엔드포인트만 허용됩니다
- 프로토콜은 기본값인 ‘HTTP’가 아닌 경우에만 지정해야 합니다
- 포트가 기본값인 ‘8123’이 아닌 경우 포트를 지정해야 합니다
- 드라이버는 포트 번호만 보고 프로토콜을 추측하지 않으므로, 프로토콜을 명시적으로 지정해야 합니다
- protocol이 지정된 경우
ssl매개변수는 필요하지 않습니다.
연결(Connection) 속성
주요 구성 매개변수는 Java 클라이언트에 정의되어 있습니다. 해당 매개변수는 그대로 드라이버에 전달하면 됩니다. 드라이버에는 클라이언트 구성에 포함되지 않는 고유 속성이 있으며, 아래에 나열되어 있습니다.드라이버 속성:예시 구성:
클라이언트 식별
요청을 발생시킨 애플리케이션을 식별하는 방법은 두 가지입니다. 연결(connection) 속성을 통해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를 생성합니다(현재는 서버 예외(Exception)에 포함되어 있습니다).작업에 log_comment를 설정하려면 com.clickhouse.jdbc.StatementImpl#getLocalSettings 메서드를 사용하십시오. 이 메서드를 사용하려면 먼저 Statement 또는 PreparedStatement를 com.clickhouse.jdbc.StatementImpl로 캐스팅해야 합니다.showLineNumbers
localSettings가 스레드 간에 공유되므로, 이 방식은 단일 스레드에서 statement를 사용하는 경우에만 적용됩니다.지원되는 데이터 타입
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은 있는 그대로 읽히며, 컬럼 길이에 맞게 0으로 채워집니다. (예를 들어'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로 읽지 않아야 합니다.PreparedStatement에서는 Enum 값을 문자열이나 숫자 값으로 설정할 수 있습니다.
- Date / Time 타입은 JDBC와의 호환성을 높이기 위해
java.sql타입에 매핑됩니다. 하지만 두 번째 인수로 해당 클래스를 지정해ResultSet#getObject(columnIndex, Class<T>)를 사용하면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으로 가져올 수 있습니다.
- 기본적으로
Array는 JDBC와의 호환성을 위해java.sql.Array에 매핑됩니다. 이는 반환되는 배열 값에 관한 더 많은 정보를 제공하기 위한 것이기도 합니다. 유형 추론에 유용합니다. Array는 원래 배열과 동일한 내용을 담은java.sql.ResultSet을 반환하도록getResultSet()메서드를 구현합니다.- 컬렉션 타입은 데이터를 올바르게 표현하는 방식이 아니므로
java.lang.String으로 읽어서는 안 됩니다(예: 배열의 문자열 값에는 따옴표가 없습니다). Map의 값은getObject(columnIndex, Class<T>)메서드로만 읽을 수 있으므로JAVA_OBJECT에 매핑됩니다.Map은 이름이 있는 컬럼이 없으므로java.sql.Struct가 아닙니다.
Tuple은 서로 다른 타입을 포함할 수 있으며List는 사용할 수 없기 때문에Object[]로 매핑됩니다.Tuple은getObject(columnIndex, Array.class)메서드를 사용해Array로 읽을 수 있습니다. 이 경우Array#baseTypeName은Tuple컬럼의 정의를 반환합니다.
java.sql.Array 객체를 인스턴스화하려면 java.sql.Connection#createArrayOf를 사용하십시오. 이 객체는 서로 다른 데이터베이스 간의 배열 처리를 통합하기 위해 설계되었습니다.
Array 팩토리 메서드에 구성을 전달하려면 Connection이 필요합니다.이 메서드는 두 개의 인수를 받습니다: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#getResultSet() 메서드를 사용하면 배열 요소를 java.sql.ResultSet 형태로 보다 일관된 방식으로 읽을 수 있습니다. 배열 요소의 정확한 유형을 알 수 없을 때 유용합니다.예시com.clickhouse.data.Tuple 객체에 매핑되며, setObject(columnIndex, tuple) 메서드를 호출하여 해당 객체로 작성해야 합니다.
이식성을 높이기 위해 java.sql.Struct 객체를 사용하여 튜플을 작성할 수도 있습니다.예시getObject(columnIndex) 메서드는 Object[]를 반환합니다. 튜플은 getObject(columnIndex, Array.class) 메서드를 사용하여 java.sql.Array로 읽을 수 있습니다.예시java.collections.Map 객체로만 작성할 수 있습니다(java.sql.Struct는 key-value 쌍을 지원하지 않습니다).예시getObject(columnIndex, Map.class) 메서드를 사용하여 java.collections.Map 객체로 읽을 수 있습니다.예시java.sql.Connection#createStruct를 사용하여 java.sql.Struct 객체를 인스턴스화하십시오. 이 객체는 서로 다른 데이터베이스 간의 중첩 처리를 통합하기 위해 설계되었습니다.
Struct 팩토리 메서드에 구성을 전달하려면 Connection이 필요합니다.이 메서드는 두 개의 인수를 받습니다: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를 반환합니다.UUID는getObject(columnIndex, String.class)메서드로String으로 읽고 쓸 수 있습니다.IPv4및IPv6는 JDBC 표준 타입이 아닙니다. 하지만 JDK에는 포함되어 있습니다. 기본적으로getObject()메서드는java.net.Inet4Address와java.net.Inet6Address를 반환합니다.getObject(columnIndex, String.class)메서드를 사용하면IPv4와IPv6를String으로 읽고 쓸 수 있습니다.
날짜, 시간 및 시간대 처리
Date/Time 및 Timestamps 처리 시 흔히 발생하는 문제점과 드라이버의 동작 방식을 설명하는 Date/Time Guide를 참고하십시오.연결(Connection) 생성
자격 증명 및 설정 제공
showLineNumbers
간단한 구문(Statement)
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의 소켓 연결 유지 설정 처리 방식이 다르기 때문에 추가적인 단계가 필요합니다. 다음 단계를 따르십시오.- 다음 Linux 커널 매개변수를
/etc/sysctl.conf또는 관련 설정 파일에서 조정하세요:
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는 명시적인 구성이 필요합니다. failover 기본값은 없습니다.
- URL에 protocol을 명시해야 합니다. 포트 번호를 이용한 암묵적 protocol 감지는 지원하지 않습니다.
구성 변경 사항
enum은 두 가지뿐입니다:com.clickhouse.jdbc.DriverProperties- 드라이버 고유의 구성 속성입니다.com.clickhouse.client.api.ClientConfigProperties- 클라이언트 구성 속성입니다. 클라이언트 구성 변경 사항은 Java 클라이언트 문서에 설명되어 있습니다.
- 먼저 URL에서 속성을 파싱합니다. 이렇게 파싱된 속성은 다른 모든 속성을 재정의합니다.
- 드라이버 속성은 클라이언트로 전달되지 않습니다.
- 엔드포인트(host, port, protocol)는 URL에서 파싱됩니다.
데이터 타입 변경 사항
숫자 타입- 가장 큰 차이점은 이식성을 높이기 위해 부호 없는 타입이 Java 타입으로 매핑된다는 점입니다.
- 두 버전 모두에서
FixedString은 그대로 읽습니다. 예를 들어'John'의FixedString(10)은'John\0\0\0\0\0\0\0\0\0'으로 읽습니다. PreparedStatement#setBytes를 사용하면unhex('<hex_string>')로 변환된 후String으로 읽습니다.- 문자열은 UTF-8 인코딩으로 저장됩니다.
Time및Time64는 V2에서만 새 타입으로 지원됩니다.DateTime및DateTime64는 JDBC와의 호환성을 높이기 위해java.sql.Timestamp로 매핑됩니다.
중첩 타입
- V2에서는 JDBC와의 호환성을 위해 기본적으로
Array를java.sql.Array에 매핑합니다. 이렇게 하면 반환되는 배열 값에 대한 정보를 더 많이 제공할 수 있습니다. 유형 추론에 유용합니다. - V2에서는
Array가getResultSet()메서드를 구현하여 원본 배열과 동일한 내용을 담은java.sql.ResultSet을 반환합니다. - 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가 배열과 매우 유사하고 key-value 쌍을 지원하지 않으므로 부분적으로만 지원합니다.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는 IP 주소를 숫자 값으로 읽는 기능을 지원하지 않습니다. 변환은 InetAddress 클래스에서 처리하는 편이 더 적절하기 때문입니다.
데이터베이스 메타데이터 변경 사항
- V2에서는 데이터베이스를 지칭하는 용어로
Schema만 사용합니다.Catalog는 향후 사용을 위해 예약되어 있습니다. - V2는
DatabaseMetaData.supportsTransactions()및DatabaseMetaData.supportsSavepoints()에 대해false를 반환합니다. 이 동작은 향후 개발 과정에서 변경될 예정입니다.