概要
- Oracle Database 12c にてテーブルに主要な SQL データ型のカラムを定義して、Java のプログラムからカラムの値を取得するサンプルコードを示す
- 今回の動作確認環境: Oracle Database 12c Release 2 (12.2.0.1.0) Enterprise Edition (on Docker) + Oracle JDBC Thin driver (ojdbc8.jar) 19.7.0.0 + Java 14 (AdoptOpenJDK 14.0.2) + Gradle 6.6 + macOS Catalina
サンプルコード
ファイル一覧
├── build.gradle
└── src
└── main
└── java
└── JdbcSample.java
build.gradle
plugins {
id 'application'
id 'java'
}
sourceCompatibility = JavaVersion.VERSION_14
repositories {
mavenCentral()
}
dependencies {
// 実行時に Oracle JDBC Driver を使うだけなら runtimeOnly を指定
//runtimeOnly 'com.oracle.database.jdbc:ojdbc8:19.7.0.0'
// 今回は oracle.jdbc.OracleTypes を使うため implementation を指定
implementation 'com.oracle.database.jdbc:ojdbc8:19.7.0.0'
}
tasks.withType(JavaCompile) {
// Java 14 のプレビュー機能を使う
options.compilerArgs += ['--enable-preview']
}
application {
// Java 14 のプレビュー機能を使う
applicationDefaultJvmArgs = ['--enable-preview']
mainClassName = 'JdbcSample'
}
JdbcSample.java
import oracle.jdbc.OracleTypes; // 普通に使うだけなら不要 (今回は Oracle 拡張 JDBC 型の情報を取得するために使用)
import java.lang.reflect.Field;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.Statement;
import java.sql.Types;
import java.util.Arrays;
class JdbcSample {
public static void main(String[] args) throws Exception {
// MySQL に接続
String url = "jdbc:oracle:thin:@//localhost:1521/testdb";
String user = "javarista";
String password = "cafebabe";
Connection con = DriverManager.getConnection(url, user, password);
Statement stmt = con.createStatement();
// テーブルを作成
// いろいろな SQL データ型でカラムを定義
// (Java 14 プレビュー機能で使えるヒアドキュメントっぽく書けるテキストブロック機能を使う)
stmt.execute("""
create table test (
-- 文字列型
my_char CHAR(8 CHAR), -- 固定長文字列 (基本は最大長 2000 bytes)
my_varchar2 VARCHAR2(512 CHAR), -- 可変長文字列 (基本は最大長 4000 bytes)
my_clob CLOB, -- Character Large Object
-- バイナリ型
my_raw RAW(256), -- 可変長バイナリ (基本は最大長 2000 bytes)
my_blob BLOB, -- Binary Large Object
-- 真偽値型
my_number_1 NUMBER(1), -- 0 は false、それ以外の値は true として解釈される
-- 整数型
my_number_38 NUMBER(38), -- 最大38桁
my_smallint SMALLINT, -- Oracle データ型に変換される ANSI データ型
my_integer INTEGER, -- Oracle データ型に変換される ANSI データ型
-- 浮動小数点型
my_binary_float BINARY_FLOAT, -- 単精度浮動小数点数 4 bytes
my_binary_double BINARY_DOUBLE, -- 倍精度浮動小数点数 8 bytes
my_float FLOAT, -- Oracle データ型 FLOAT(126) に変換される ANSI データ型
my_double_precision DOUBLE PRECISION, -- Oracle データ型 FLOAT(126) に変換される ANSI データ型
my_real REAL, -- Oracle データ型 FLOAT(63) に変換される ANSI データ型
-- 固定小数点型
my_fixed_point_number NUMBER(7, 4), -- 固定小数点数
my_numeric NUMERIC(7, 4), -- Oracle データ型に変換される ANSI データ型
my_decimal DECIMAL(7, 4), -- Oracle データ型に変換される ANSI データ型
-- 時間型
my_date DATE, -- 年月日 + 時分秒
my_timestamp TIMESTAMP(9), -- 年月日 + 時分秒 + ナノ秒
my_timestamp_with_time_zone TIMESTAMP(9) WITH TIME ZONE -- 年月日 + 時分秒 + ナノ秒 + タイムゾーン
)""");
// レコードを追加
// (Java 14 プレビュー機能で使えるヒアドキュメントっぽく書けるテキストブロック機能を使う)
stmt.execute("""
insert into test values (
-- 文字列型
'Hello', -- CHAR
'Hello', -- VARCHAR2
'Hello', -- CLOB
-- バイナリ型
HEXTORAW('CAFEBABE'), -- RAW
HEXTORAW('CAFEBABE'), -- BLOB,
-- 真偽値型
1, -- 0 は false、それ以外の値は true として解釈される
-- 整数型
12345678901234567890123456789012345678, -- NUMBER(38)
32767, -- SMALLINT
2147483647, -- INTEGER
-- 浮動小数点型
123.0001, -- BINARY_FLOAT
123.0001, -- BINARY_DOUBLE
123.0001, -- FLOAT
123.0001, -- DOUBLE PRECISION
123.0001, -- REAL
-- 固定小数点型
123.0001, -- NUMBER(7, 4)
123.0001, -- NUMERIC(7, 4)
123.0001, -- DECIMAL(7, 4)
-- 時間型
TO_DATE('2001-02-03 04:05:06', 'YYYY-MM-DD HH24:MI:SS'), -- DATE
TO_TIMESTAMP('2001-02-03 04:05:06.999999999', 'YYYY-MM-DD HH24:MI:SS.FF9'), -- TIMESTAMP(9)
TO_TIMESTAMP_TZ('2001-02-03 04:05:06.999999999 +00:00', 'YYYY-MM-DD HH24:MI:SS.FF9 TZH:TZM') -- TIMESTAMP(9) WITH TIME ZONE
)""");
// レコードを取得
ResultSet rs = stmt.executeQuery("select * from test");
while (rs.next()) {
// カラムの JDBC 型や SQL 型に対する Java オブジェクトの型を取得
System.out.println("カラム名 - JDBC 型 - データベース固有の SQL 型 - Java オブジェクトの型");
ResultSetMetaData rsmd = rs.getMetaData();
for (int i = 1; i <= rsmd.getColumnCount(); i++) {
System.out.println(
rsmd.getColumnName(i) + " - " +
getJdbcTypeName(rsmd.getColumnType(i)) + " - " +
rsmd.getColumnTypeName(i) + " - " +
rsmd.getColumnClassName(i));
}
System.out.println();
// カラムの値を取得していく
System.out.println("カラム名 - カラムの値");
// 文字列型
System.out.println("my_char=" + rs.getString("my_char"));
System.out.println("my_varchar2=" + rs.getString("my_varchar2"));
System.out.println("my_clob=" + rs.getClob("my_clob"));
// バイナリ型
System.out.println("my_raw=" + Arrays.toString(rs.getBytes("my_raw")));
System.out.println("my_blob=" + rs.getBlob("my_blob"));
// 真偽値型
System.out.println("my_number_1=" + rs.getBoolean("my_number_1"));
// 整数型
System.out.println("my_number_38=" + rs.getBigDecimal("my_number_38"));
System.out.println("my_smallint=" + rs.getInt("my_smallint"));
System.out.println("my_integer=" + rs.getInt("my_integer"));
// 浮動小数点型
System.out.println("my_binary_float=" + rs.getFloat("my_binary_float"));
System.out.println("my_binary_double=" + rs.getDouble("my_binary_double"));
System.out.println("my_float=" + rs.getDouble("my_float"));
System.out.println("my_double_precision=" + rs.getDouble("my_double_precision"));
System.out.println("my_real=" + rs.getDouble("my_real"));
// 固定小数点型
System.out.println("my_fixed_point_number=" + rs.getBigDecimal("my_fixed_point_number"));
System.out.println("my_numeric=" + rs.getBigDecimal("my_numeric"));
System.out.println("my_decimal=" + rs.getBigDecimal("my_decimal"));
// 時間型
System.out.println("my_date=" + rs.getTimestamp("my_date"));
System.out.println("my_timestamp=" + rs.getTimestamp("my_timestamp").toInstant());
System.out.println("my_timestamp_with_time_zone=" + rs.getTimestamp("my_timestamp_with_time_zone").toInstant());
}
stmt.close();
con.close();
}
// JDBC 型の名称を取得する
private static String getJdbcTypeName(int type) throws IllegalAccessException {
// Java 標準の JDBC 型から探す
Field[] fs = Types.class.getDeclaredFields();
for (Field f : fs) {
if (type == f.getInt(null)) {
return f.getName();
}
}
// Oracle 拡張機能の JDBC 型から探す
fs = OracleTypes.class.getDeclaredFields();
for (Field f : fs) {
if (type == f.getInt(null)) {
return "OracleTypes." + f.getName();
}
}
// 合致する JDBC 型が無かったので type 値を文字列化して返す
return "" + type;
}
}
実行結果
Gradle の run タスクで実行。
$ gradle run
Starting a Gradle Daemon (subsequent builds will be faster)
> Task :compileJava
注意:/Users/foo/bar/src/main/java/JdbcSample.javaはプレビュー言語機能を使用します。
注意:詳細は、-Xlint:previewオプションを指定して再コンパイルしてください。
> Task :run
カラム名 - JDBC 型 - データベース固有の SQL 型 - Java オブジェクトの型
MY_CHAR - CHAR - CHAR - java.lang.String
MY_VARCHAR2 - VARCHAR - VARCHAR2 - java.lang.String
MY_CLOB - CLOB - CLOB - oracle.jdbc.OracleClob
MY_RAW - VARBINARY - RAW - [B
MY_BLOB - BLOB - BLOB - oracle.jdbc.OracleBlob
MY_NUMBER_1 - NUMERIC - NUMBER - java.math.BigDecimal
MY_NUMBER_38 - NUMERIC - NUMBER - java.math.BigDecimal
MY_SMALLINT - NUMERIC - NUMBER - java.math.BigDecimal
MY_INTEGER - NUMERIC - NUMBER - java.math.BigDecimal
MY_BINARY_FLOAT - OracleTypes.BINARY_FLOAT - BINARY_FLOAT - java.lang.Float
MY_BINARY_DOUBLE - OracleTypes.BINARY_DOUBLE - BINARY_DOUBLE - java.lang.Double
MY_FLOAT - NUMERIC - NUMBER - java.lang.Double
MY_DOUBLE_PRECISION - NUMERIC - NUMBER - java.lang.Double
MY_REAL - NUMERIC - NUMBER - java.lang.Double
MY_FIXED_POINT_NUMBER - NUMERIC - NUMBER - java.math.BigDecimal
MY_NUMERIC - NUMERIC - NUMBER - java.math.BigDecimal
MY_DECIMAL - NUMERIC - NUMBER - java.math.BigDecimal
MY_DATE - TIMESTAMP - DATE - java.sql.Timestamp
MY_TIMESTAMP - TIMESTAMP - TIMESTAMP - oracle.sql.TIMESTAMP
MY_TIMESTAMP_WITH_TIME_ZONE - OracleTypes.TIMESTAMPTZ - TIMESTAMP WITH TIME ZONE - oracle.sql.TIMESTAMPTZ
カラム名 - カラムの値
my_char=Hello
my_varchar2=Hello
my_clob=oracle.sql.CLOB@7c711375
my_raw=[-54, -2, -70, -66]
my_blob=oracle.sql.BLOB@3a44431a
my_number_1=true
my_number_38=12345678901234567890123456789012345678
my_smallint=32767
my_integer=2147483647
my_binary_float=123.0001
my_binary_double=123.0001
my_float=123.0001
my_double_precision=123.0001
my_real=123.0001
my_fixed_point_number=123.0001
my_numeric=123.0001
my_decimal=123.0001
my_date=2001-02-03 04:05:06.0
my_timestamp=2001-02-02T19:05:06.999999999Z
my_timestamp_with_time_zone=2001-02-03T04:05:06.999999999Z
BUILD SUCCESSFUL in 16s
2 actionable tasks: 2 executed
Oracle Database の真偽値型
Oracle Database JDBC開発者ガイド, 12cリリース2 (12.2) - Oracleデータへのアクセスと操作
BOOLEANデータベース型が存在しないため、getBooleanを使用すると必ずデータ型変換が実行されます。getBooleanメソッドは数値用の列に対してのみサポートされます。このような列に対してgetBooleanが適用されると、0(ゼロ)値はfalseとして、それ以外の値はtrueとして解釈されます。別の種類の列に適用された場合は、getBooleanは例外java.lang.NumberFormatExceptionを戻します。
参考資料
- JDBC API 入門 - SQL と Java の型のマッピング
- java.sql (Java SE 14 & JDK 14)
- Oracle Database概要, 12cリリース2 (12.2) - 表と表クラスタ - Oracleデータ型
- Oracle Database SQL言語リファレンス, 12cリリース2 (12.2) - データ型
- Oracle Database SQL言語リファレンス, 12cリリース2 (12.2) - 日時書式モデル
- Oracle Database JDBC開発者ガイド, 12cリリース2 (12.2) - Oracleデータへのアクセスと操作 - SQL型とJava型間のデフォルト・マッピング
- Oracle JDBC Frequently Asked Questions - What are the Oracle JDBC releases Vs JDK versions?
- Maven Central Developers Guide
- Java + H2 Database で主要な JDBC 型の値を取得するサンプルコード - Qiita
- Oracle Database 12c を macOS 上の Docker で構築する - Qiita