DuckDB JDBC 드라이버 문제 해결
DuckDB JDBC 드라이버 문제 해결 (Troubleshoot)
DuckDB JDBC 드라이버를 쓰다 겪는 흔한 문제와 해결 방법을 모아 두었어요. 여기 없는 문제는 GitHub의 드라이버 이슈 트래커를 검색해 보세요.
출처: 문서
본문
드라이버 클래스를 찾을 수 없음 (Driver Class Not Found)
이 오류는 DuckDB JDBC 드라이버가 애플리케이션의 classpath에 없을 때 발생해요. 보통 빌드 도구가 의존성을 해결하지 못한 경우예요. Java 애플리케이션이 DuckDB 드라이버를 찾지 못하면 다음과 같은 오류를 던질 수 있어요:
Exception in thread "main" java.sql.SQLException: No suitable driver found for jdbc:duckdb:
at java.sql/java.sql.DriverManager.getConnection(DriverManager.java:706)
at java.sql/java.sql.DriverManager.getConnection(DriverManager.java:252)
...
그리고 클래스를 수동으로 로드하려 하면 다음과 같은 오류가 날 수 있어요:
Exception in thread "main" java.lang.ClassNotFoundException: org.duckdb.DuckDBDriver
at java.base/jdk.internal.loader.BuiltinClassLoader.loadClass(BuiltinClassLoader.java:641)
at java.base/jdk.internal.loader.ClassLoaders$AppClassLoader.loadClass(ClassLoaders.java:188)
at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:520)
at java.base/java.lang.Class.forName0(Native Method)
at java.base/java.lang.Class.forName(Class.java:375)
...
이 오류들은 DuckDB Maven/Gradle 의존성이 감지되지 않기 때문에 생겨요. IDE에서 Maven 구성을 강제로 새로고침해 감지되도록 해요.
Parquet 문자열 열이 Blob로 반환됨
일부 레거시 작성자가 만든 Parquet 파일은 문자열 열에 UTF8 플래그를 설정하지 않아서, DuckDB가 이를 BLOB으로 읽어요. 그러면 ResultSet.getObject()가 DuckDBBlobResult를 반환하고, ResultSet.getString()은 기대한 텍스트가 아닌 이스케이프된 문자열로 렌더링된 바이트를 반환해요. binary_as_string 설정을 켜서 이 열들을 VARCHAR로 읽어요:
try (Statement stmt = conn.createStatement()) {
stmt.execute("SET binary_as_string = true;");
}
같은 옵션을 read_parquet에 직접 전달할 수도 있어요. 예: read_parquet('file.parquet', binary_as_string = true).
이 동작은 duckdb-java 이슈 #113에서 추적되고 있어요.
네이티브 이미지: 연결을 열 때 NoSuchMethodError
GraalVM Native Image 실행 파일에서 첫 연결을 열 때 다음과 같이 실패할 수 있어요:
Exception in thread "main" java.lang.ExceptionInInitializerError
...
Caused by: java.lang.NoSuchMethodError: ⟨class and method⟩
at com.oracle.svm.core.jni.functions.JNIFunctions$Support.getMethodID(JNIFunctions.java)
...
드라이버는 네이티브 라이브러리가 초기화될 때 JNI 서페이스 전체를 즉시(eagerly) 해석하므로, reachability 메타데이터에서 클래스·메서드·필드 하나라도 빠지면 초기화 전체가 실패해요. 이는 이미지에 컴파일된 메타데이터가 빠졌거나 드라이버보다 오래된 것임을 의미해요: 자체 메타데이터를 담은 드라이버 버전으로 업그레이드하거나, 사용 중인 정확한 드라이버 버전에 대해 tracing agent로 메타데이터를 재생성해요.
이 오류가 때로는 오해를 불러일으키는 메시지로 감싸져 나오기도 해요:
java.lang.UnsatisfiedLinkError: Unsupported JNI version 0xffffffff, required by ⟨path⟩/libduckdb_java.⟨suffix⟩
이것은 내부 조회가 실패했을 때 라이브러리의 JNI_OnLoad가 보고하는 내용이며, 원인은 JNI 버전 문제가 아니라 같은 누락 메타데이터예요.
네이티브 이미지: UnsatisfiedLinkError: Can't load library
Native Image 실행 파일에서 첫 연결이 다음처럼 실패할 수 있어요:
java.lang.UnsatisfiedLinkError: Can't load library: duckdb_java | java.library.path = [.]
...
Caused by: java.io.FileNotFoundException: DuckDB JNI library not found, path: '⟨path⟩/libduckdb_java.⟨suffix⟩'
공유 라이브러리가 실행 파일에 임베드되지도 않았고 그 옆에서도 찾을 수 없었어요. 플랫폼용 라이브러리 리소스 항목을 추가하거나, 라이브러리 파일을 실행 파일 옆에 두면 돼요. 두 옵션 모두 네이티브 이미지로 배포 페이지에 설명되어 있어요.
java.lang.System의 제한된 메서드에 대한 경고
JDK 24 이상에서 드라이버를 로드하면 다음이 출력돼요:
WARNING: A restricted method in java.lang.System has been called
WARNING: java.lang.System::load has been called by org.duckdb.DuckDBNative ...
WARNING: Use --enable-native-access=ALL-UNNAMED to avoid a warning for callers in this module
이것은 JDK의 네이티브 액세스 무결성 검사로, 무해해요. --enable-native-access=ALL-UNNAMED로 JVM을 실행하면 잠잠해지고, 드라이버를 모듈 경로에 두면 드라이버의 모듈 이름을 쓰면 돼요. 향후 JDK 릴리스에서는 이 경고가 오류가 될 것이므로 플래그를 추가하는 걸 권장해요.
더 읽어보기 (Further Reading)
- 네이티브 이미지로 배포 — 공유 라이브러리를 제공하는 두 가지 방법을 포함한 GraalVM 독립 실행 파일 빌드.
- Java (JDBC) 클라이언트 — Maven Central에서 드라이버 설치. 위 드라이버-not-found 오류의 해결책.
- 연결 정의 (Define Connections) — 드라이버 등록, 설정 옵션, 많은 연결 시점 오류 뒤의 인스턴스 동작.
- Parquet 파일 — Parquet 문자열 열을 올바르게 읽기 위한
binary_as_string설정과 기타 옵션.