20210120のJavaに関する記事は9件です。

Javaでディープラーニングを実装してみた

ディープラーニングをスクラッチから実装して理解するアプローチの書籍としては、『ゼロから作るDeeplearning』が有名ですが、同じ趣旨の『ディープラーニングの数学』という書籍がとても分かりやすかったので、PythonのコードJavaで実装してみました。

Javaは機械学習不毛地帯(というかデータサイエンス領域がPythonの独壇場?)ですが、Javaで機械学習のロジックを理解したいという奇特な方がいれば参考にしてください。

オリジナルと同様にできる限りライブラリを使わず、スクラッチで実装しました。
(実行速度の問題で行列やベクトルの演算部分はcommons mathライブラリを利用しています)
また、書籍の内容に合わせたので、Javaっぽくない書き方や冗長になっている部分もあります。

書籍『ディープラーニングの数学』の紹介

まず最初に書籍の内容を簡単に紹介したいと思います。

書籍は4編に分かれていて、導入編が機械学習の基本、理論編が数学、実装編がPythonによる機械学習アルゴリズムの実装、発展編が少し高度な内容の数式もしくは概念レベルの解説(実装はなし)になっています。

この書籍の特徴は以下の3点だと思います。
1. 章同士の依存関係が可視化されていて全体を俯瞰しやすい
2. 数学は公式を羅列するだけでなく、式の導出まできっちりやっている
(厳密ではない部分もありますが、数学の専門書ではないのでちょうどいいバランスだと思います)
3. 実装はシンプルな線形回帰から始まって段階的にディープラーニングに進化していくので、各段階の差分を理解するだけで少しずつ理解を深めていくことができる
特に3は重要なポイントだと思います。

各編の概要は以下のとおりです。

導入編

導入編は1章のみで、機械学習の概要や仕組みについて解説されています。
また、例として身長から体重を予測するという回帰問題を平方完成という式変形で解析的に解いています。

理論編

理論編では主には数学について解説されていますが、ディープラーニングに必要な範囲に絞られています。
2章から6章まであり、2章は微分・積分、3章はベクトル・行列、4章は多変数関数の微分(偏微分)、5章は指数関数・対数関数、6章は確率・統計となっています。
レベルとしては高校数学から大学理系学部の教養科目ぐらいでしょうか。

章構成もよく練られていて、前から読み進めていくと矛盾なく理解できるようになっています。

この記事の中で数学は解説しませんが、ディープラーニングの仕組みを深く理解したい人は、理論編を読んでから後半の実装部分に取り組んだほうがいいと思います。

実装編

実践編ではPythonで機械学習のロジックを実装しています。
7章から10章まであり、7章は線形回帰モデル(回帰問題)、8章はロジスティック回帰モデル(2値分類問題)、9章はロジスティック回帰モデル(多値分類問題)、そして10章でディープラーニングを実装します。

実装編も章構成が工夫されていて、前の章に1つか2つの概念を追加すると次の章のロジックが実現できるようになっているので、段階的に理解することができます。
また、巻頭の見開きに各章の間の差分が表形式でまとめられているので、混乱したら見てみてください。

発展編

発展編は11章のみで、画像に強いCNNモデル、時系列データに強いRNNモデル、数値計算で勾配を求める数値微分の考え方、勾配降下法より効率がいい最適化アルゴリズム、過学習対策、重み行列の初期化方法などについて、数式や概念レベルで解説されています。

線形回帰モデル(回帰問題)のJava実装

それではここからJavaで実装していきます。
線形回帰モデルでは、The Boston Housing Datasetという不動産・地域データを使って不動産の価格を予測します。

線形単回帰モデル

最初は部屋数(RM)のみの1変数を使う線形単回帰モデルを実装します。

コンストラクタでは主に以下の処理を行います。
・ハイパーパラメータ(学習回数と学習率)を設定する
・学習データと正解データを作成する
・重みベクトルを1で初期化する

learnメソッドでは以下の3つの処理を繰り返して学習します。
・予測値を計算する
・誤差を計算する
・勾配に学習率を掛けて重みを更新する

LinearSingleRegression.java
package math.deeplearning.ch07;

import org.apache.commons.math3.linear.*;
import java.io.IOException;
import static math.deeplearning.common.Util.*;

/**
 * 線形単回帰モデル.
 */
public class LinearSingleRegression {
    // 学習率
    private double alpha;
    // 学習回数
    private int iters;
    // 学習データ
    private RealMatrix x;
    // 正解データ
    private RealVector yt;
    // 入力データ行数
    private int M;
    // 入力データ列数
    private int D;
    // 重みベクトル
    private RealVector W;

    /**
     * 初期化処理.
     *
     * @param iters 学習回数
     * @param alpha 学習率
     */
    public LinearSingleRegression(int iters, double alpha) throws IOException {
        this.iters = iters;
        this.alpha = alpha;

        // The Boston Housing Datasetを読み込む
        RealMatrix boston = loadBoston();
        // 学習データとして部屋数(RM)列を抽出し、ダミー変数1を付加する
        x = addBiasCol(extractCol(boston, new int[]{5}));
        // 正解データとして物件価格を抽出する
        yt = boston.getColumnVector(13);
        // 学習データの行数
        M = x.getRowDimension();
        // 学習データの列数
        D = x.getColumnDimension();

        // 重みベクトルを1で初期化する
        W = add(MatrixUtils.createRealVector(new double[D]), 1.0);
    }

    public static void main(String[] args) throws Exception {
        // 学習回数を5000、学習率を0.01に設定する
        LinearSingleRegression lsr = new LinearSingleRegression(50000, 0.01);
        // 学習する
        lsr.learn();
    }

    /**
     * 学習する.
     */
    public void learn() {
        for (int i = 0; i < iters; i++) {
            // 予測値ypを計算
            RealVector yp = dot(x, W);
            // 誤差ydを計算
            RealVector yd = sub(yp, yt);
            // 勾配に学習率を掛けて重みを更新
            W = sub(W, mult(div(dot(t(x), yd), M), alpha));

            // 一定回数学習するごとに誤差を表示する
            if (i % 100 == 0)
                System.out.println(i + " " + mean(pow(yd, 2)) / 2);
        }
    }
}

煩わしい処理をmath.deeplearning.common.Utilクラスに詰め込んだ結果、機械学習のロジック部分はある程度シンプルに実装できました。
実行すると学習が進むにつれて誤差が小さくなっていくことが分かります。

線形単回帰モデルの出力
0 154.2249338409091
100 29.617518011568446
・・・
49800 21.80032626850963
49900 21.800325071320316

線形重回帰モデル

続いて部屋数(RM)と低所得者率(LSTAT)の2変数を使う線形重回帰モデルを実装します。

LinearMultipleRegression.java
package math.deeplearning.ch07;

import org.apache.commons.math3.linear.*;
import java.io.IOException;
import static math.deeplearning.common.Util.*;

/**
 * 線形重回帰モデル.
 */
public class LinearMultipleRegression {
    // 学習率
    private double alpha;
    // 学習回数
    private int iters;
    // 学習データ
    private RealMatrix x;
    // 正解データ
    private RealVector yt;
    // 入力データ行数
    private int M;
    // 入力データ列数
    private int D;
    // 重みベクトル
    private RealVector W;

    /**
     * 初期化処理.
     *
     * @param iters 学習回数
     * @param alpha 学習率
     */
    public LinearMultipleRegression(int iters, double alpha) throws IOException {
        this.iters = iters;
        this.alpha = alpha;

        // The Boston Housing Datasetを読み込む
        RealMatrix boston = loadBoston();
        // 学習データとして部屋数(RM)列と低所得者率(LSTAT)列を抽出し、ダミー変数1を付加する
        x = addBiasCol(extractCol(boston, new int[]{5, 12}));
        // 正解データとして物件価格を抽出する
        yt = boston.getColumnVector(13);
        // 学習データの行数
        M = x.getRowDimension();
        // 学習データの列数
        D = x.getColumnDimension();

        // 重みベクトルを1で初期化する
        W = add(MatrixUtils.createRealVector(new double[D]), 1.0);
    }

    public static void main(String[] args) throws Exception {
        // 学習回数を2000、学習率を0.001に設定する
        LinearMultipleRegression lmr = new LinearMultipleRegression(2000, 0.001);
        // 学習する
        lmr.learn();
    }

    /**
     * 学習する.
     */
    public void learn() {
        for (int i = 0; i < iters; i++) {
            // 予測値ypを計算
            RealVector yp = dot(x, W);
            // 誤差ydを計算
            RealVector yd = sub(yp, yt);
            // 勾配に学習率を掛けて重みを更新
            W = sub(W, mult(div(dot(t(x), yd), M), alpha));

            // 一定回数学習するごとに誤差を表示する
            if (i % 100 == 0)
                System.out.println(i + " " + mean(pow(yd, 2)) / 2);
        }
    }
}

特徴量を1つ追加したことで線形単回帰モデルより誤差が小さくなり、学習も早く収束しています。

線形重回帰モデルの出力
0 112.06398160770748
100 25.358934200838444
・・・
1800 15.280256759397282
1900 15.280228371672587

線形単回帰モデルのコードと比較すると、実質的な差分は下記の学習データを抽出する部分のみで、線形回帰のロジック部分は変更せずに線形重回帰に対応できていることが分かります。

線形単回帰モデル
// 学習データとして、部屋数(RM)列を抽出し、ダミー変数1を付加する
x = addBiasCol(extractCol(boston, new int[]{5}));
線形重回帰モデル
// 学習データとして、部屋数(RM)列と低所得者率(LSTAT)列を抽出し、ダミー変数1を付加する
x = addBiasCol(extractCol(boston, new int[]{5, 12}));

ロジスティック回帰モデル(2値分類問題)のJava実装

次はIris Data Setというアヤメのサイズデータを使ってアヤメの種類を2クラスに分類するロジスティック回帰モデルを実装します。
Iris Data Setは先頭から50件目までがSetosa、51件目から100件目までがVersicolourというアヤメの種類のデータになっているので、先頭から100件のデータを抽出し、並び順をシャッフルします。

// Iris Data SetからSetosaとVersicolourの2種類のアヤメのデータを読み込む
RealMatrix iris = shuffle(loadIris(0, 100));

処理の流れは線形回帰と同じですが、今回はロジスティック回帰モデルで2値分類問題を解くので、活性化関数にsigmoid関数を入れて出力を0から1の確率値に変換します。

2値ロジスティック回帰モデル

BinaryLogisticRegression.java
package math.deeplearning.ch08;

import org.apache.commons.math3.linear.*;
import java.io.IOException;
import static math.deeplearning.common.Util.*;

/**
 * ロジスティック回帰モデル(2値分類).
 */
public class BinaryLogisticRegression {
    // 学習率
    private double alpha;
    // 学習回数
    private int iters;
    // 学習データ
    private RealMatrix x;
    // 評価用学習データ
    private RealMatrix xTest;
    // 正解データ
    private RealVector yt;
    // 評価用正解データ
    private RealVector ytTest;
    // 入力データ行数
    private int M;
    // 入力データ列数
    private int D;
    // 重みベクトル
    private RealVector W;

    /**
     * 初期化処理.
     *
     * @param iters 学習回数
     * @param alpha 学習率
     */
    public BinaryLogisticRegression(int iters, double alpha) throws IOException {
        this.iters = iters;
        this.alpha = alpha;

        // Iris Data SetからSetosaとVersicolourの2種類のアヤメのデータを読み込む
        RealMatrix iris = shuffle(loadIris(0, 100));
        // 学習データとしてがく片の長さの列とがく片の幅の列を抽出し、ダミー変数1を付加する
        x = addBiasCol(extractRowCol(iris, 0, 69, 0, 1));
        // テストデータとしてがく片の長さの列とがく片の幅の列を抽出し、ダミー変数1を付加する
        xTest = addBiasCol(extractRowCol(iris, 70, 99, 0, 1));
        // 学習の正解データとしてアヤメの種類を抽出する
        yt = extractRowCol(iris, 0, 69, 4);
        // テストの正解データとしてアヤメの種類を抽出する
        ytTest = extractRowCol(iris, 70, 99, 4);
        // 正解データの行数
        M = x.getRowDimension();
        // 正解データの列数
        D = x.getColumnDimension();

        // 重みベクトルを1で初期化する
        W = add(MatrixUtils.createRealVector(new double[D]), 1.0);
    }

    public static void main(String[] args) throws Exception {
        // 学習回数を10000、学習率を0.01に設定する
        BinaryLogisticRegression blr = new BinaryLogisticRegression(10000, 0.01);
        // 学習する
        blr.learn();
    }

    /**
     * 学習する.
     */
    public void learn() {
        // 学習する
        for (int i = 0; i < iters; i++) {
            // 予測値ypを計算
            RealVector yp = sigmoid(dot(x, W));
            // 誤差ydを計算
            RealVector yd = sub(yp, yt);
            // 勾配に学習率を掛けて重みを更新
            W = sub(W, mult(div(dot(t(x), yd), M), alpha));

            // 一定回数学習するごとに誤差と精度を表示する
            if (i % 10 == 0) {
                RealVector p = sigmoid(dot(xTest, W));
                System.out.print("iter = " + i + "\tloss = " + crossEntropy(ytTest, p));
                System.out.println("\tscore = " + calcAccuracy(ytTest, p));
            }
        }
    }
}

最初、正解率は50%前後ですが、最終的には100%になりました。

2値ロジスティック回帰モデルの出力
iter = 0    loss = 4.401398657630698    score = 0.5333333333333333
iter = 10   loss = 3.4820950219350593   score = 0.5333333333333333
・・・
iter = 9980 loss = 0.10275578614196225  score = 1.0
iter = 9990 loss = 0.10270185332637241  score = 1.0

線形回帰のコードと比較すると、実質的な差分は下記の予測値ypの計算にsigmoid関数を追加している部分だけであることが分かります。
(あと今回から学習データとテストデータを分けていますが、本質的な違いではありません)

線形回帰モデル
// 予測値ypを計算
RealVector yp = dot(x, W);
ロジスティック回帰モデル
// 予測値ypを計算
RealVector yp = sigmoid(dot(x, W));

ロジスティック回帰モデル(多値分類問題)のJava実装

続いて同じIris Data Setをアヤメのデータをすべて使ってアヤメの種類を3クラスに分類するロジスティック回帰モデルを実装します。

問題設定が2クラス分類から3クラス分類になるので、活性化関数をsigmoid関数からsoftmax関数に変更します。

多値ロジスティック回帰モデル

MultipleLogisticRegression.java
package math.deeplearning.ch09;

import org.apache.commons.math3.linear.*;
import java.io.IOException;
import static math.deeplearning.common.Util.*;

/**
 * ロジスティック回帰モデル(多値分類).
 */
public class MultipleLogisticRegression {
    // 学習率
    private double alpha;
    // 学習回数
    private int iters;
    // 学習データ
    private RealMatrix x;
    // 評価用学習データ
    private RealMatrix xTest;
    // 正解データ
    private RealMatrix yt;
    // 評価用正解データ
    private RealMatrix ytTest;
    // 入力データ行数
    private int M;
    // 入力データ列数
    private int D;
    // 重み行列
    private RealMatrix W;

    /**
     * 初期化処理.
     *
     * @param iters 学習回数
     * @param alpha 学習率
     */
    public MultipleLogisticRegression(int iters, double alpha) throws IOException {
        this.iters = iters;
        this.alpha = alpha;

        // Iris Data SetからSetosaとVersicolourの2種類のアヤメのデータを読み込む
        RealMatrix iris = shuffle(loadIris());
        // 学習データとしてがく片の長さの列と花弁の長さの列を抽出し、ダミー変数1を付加する
        x = addBiasCol(extractRowCol(iris, 0, 74, new int[]{0, 2}));
        // テストデータとしてがく片の長さの列と花弁の長さの列を抽出し、ダミー変数1を付加する
        xTest = addBiasCol(extractRowCol(iris, 75, 149, new int[]{0, 2}));
        // 4変数すべてを使う場合
        // x = addBiasCol(extractRowCol(iris, 0, 74, 0, 3));
        // xTest = addBiasCol(extractRowCol(iris, 75, 149, 0, 3));
        // 学習の正解データとしてアヤメの種類を抽出し、OneHotVector形式に変換する
        yt = oneHotEncode(extractRowCol(iris, 0, 74, 4), 3);
        // テストの正解データとしてアヤメの種類を抽出し、OneHotVector形式に変換する
        ytTest = oneHotEncode(extractRowCol(iris, 75, 149, 4), 3);
        // 正解データの行数
        M = x.getRowDimension();
        // 正解データの列数
        D = x.getColumnDimension();

        // 重み行列を1で初期化する
        W = add(MatrixUtils.createRealMatrix(D, 3), 1.0);
    }

    public static void main(String[] args) throws Exception {
        // 学習回数を10000、学習率を0.01に設定する
        MultipleLogisticRegression mlr = new MultipleLogisticRegression(10000, 0.01);
        mlr.learn();
    }

    /**
     * 学習する.
     */
    public void learn() {
        // 学習する
        for (int i = 0; i < iters; i++) {
            // 予測値ypを計算
            RealMatrix yp = softmax(dot(x, W));
            // 誤差ydを計算
            RealMatrix yd = sub(yp, yt);
            // 勾配に学習率を掛けて重みを更新
            W = sub(W, mult(div(dot(t(x), yd), M), alpha));

            // 一定回数学習するごとに誤差と精度を表示する
            if (i % 10 == 0) {
                RealMatrix p = softmax(dot(xTest, W));
                System.out.print("iter = " + i + "\tloss = " + crossEntropy(ytTest, p));
                System.out.println("\tscore = " + calcAccuracy(ytTest, p));
            }
        }
    }
}

最初の正解率は1/3前後ですが、最終的には97%になりました。

多値ロジスティック回帰モデルの出力
iter = 0    loss = 1.089863468306522    score = 0.30666666666666664
iter = 10   loss = 1.0505735104517255   score = 0.30666666666666664
・・・
iter = 9980 loss = 0.18412409250145656  score = 0.9733333333333334
iter = 9990 loss = 0.18403868595917505  score = 0.9733333333333334

2値分類のロジスティック回帰モデルのコードと比較すると、主な差分は以下の3点です。
・重みをベクトルから行列に変更(クラスごとに重みを持つため)
・正解データを0/1のバイナリ形式からOneHotVector形式に変更(2値分類から多値分類になったため)
・予測値ypの計算の活性化関数をsigmoid関数からsoftmax関数に変更

ディープラーニングモデルのJava実装

それではいよいよディープラーニングモデルを実装します。

ここではMNISTという手書き数字の画像データを0から9の10クラスに分類する問題を解きます。

3層ディープラーニングモデル

まずは隠れ層が1層のみの3層ディープラーニングモデルを実装します。

DeepLearning.java
package math.deeplearning.ch10;

import org.apache.commons.math3.linear.RealMatrix;
import java.io.IOException;
import java.util.*;
import static math.deeplearning.common.Util.*;

/**
 * ディープラーニング(隠れ層1層).
 */
public class DeepLearning {
    // 学習データの行数
    private int M;
    // 学習データの列数(画像のピクセル数)
    private int D;
    // 分類クラス数
    private int N;
    // 学習回数
    private int iters;
    // バッチデータサイズ
    private int batchSize;
    // 学習率
    private double alpha;

    // MNIST画像データ
    private RealMatrix xAll;
    private RealMatrix xTest;
    private RealMatrix ytAll;
    private RealMatrix ytTest;

    // 重み行列
    private RealMatrix V;
    private RealMatrix W;

    public DeepLearning(int iters, int H, int batchSize, double alpha) throws IOException {
        // MNISTデータセットを読み込む
        xAll = addBiasCol(div(loadMnistImage(MNIST_TRAIN_IMAGE_FILE_NAME), 255));
        xTest = addBiasCol(div(loadMnistImage(MNIST_TEST_IMAGE_FILE_NAME), 255));
        ytAll = oneHotEncode(loadMnistLabel(MNIST_TRAIN_LABEL_FILE_NAME), 10);
        ytTest = oneHotEncode(loadMnistLabel(MNIST_TEST_LABEL_FILE_NAME), 10);

        M = xAll.getRowDimension();
        D = xAll.getColumnDimension();
        N = ytAll.getColumnDimension();

        this.iters = iters;
        this.batchSize = batchSize;
        this.alpha = alpha;

        // 重み行列をHe Normalで初期化
        V = initW(D, H);
        W = initW(H + 1, N);
    }

    public static void main(String... args) throws Exception {
        // 学習回数を10000、隠れ層のニューロン数を128、バッチサイズを512、学習率を0.01に設定する
        DeepLearning dl = new DeepLearning(10000, 128, 512, 0.01);
        // 学習する
        dl.learn();
    }

    public void learn() {
        // ランダムサンプリングのindexを初期化
        List<Integer> indexes = new ArrayList<>();
        for (int i = 0; i < M; i++) indexes.add(i);

        for (int i = 0; i < iters; i++) {
            // 学習データのサンプリング
            List<Integer> index = randIndex(indexes, M, batchSize);
            RealMatrix x = sampling(xAll, index);
            RealMatrix yt = sampling(ytAll, index);

            // 各層の出力値を計算
            RealMatrix a = dot(x, V);
            RealMatrix b = reLU(a);
            RealMatrix b1 = addBiasCol(b);
            RealMatrix u = dot(b1, W);
            RealMatrix yp = softmax(u);
            // 各層の誤差を計算
            RealMatrix yd = sub(yp, yt);
            RealMatrix bd = mult(step(a), dot(yd, t(removeBias(W))));
            // 勾配に学習率を掛けて各層の重みを更新
            W = sub(W, mult(div(dot(t(b1), yd), batchSize), alpha));
            V = sub(V, mult(div(dot(t(x), bd), batchSize), alpha));

            // 一定回数学習するごとに誤差と精度を表示する
            if (i % 100 == 0) {
                RealMatrix p = softmax(dot(addBiasCol(reLU(dot(xTest, V))), W));
                System.out.print(i + " " + crossEntropy(ytTest, p) + " ");
                System.out.println(calcAccuracy(ytTest, p));
            }
        }
    }
}

最初、誤差は2.3前後、正解率は10%前後ですが、最終的に誤差は0.21前後、正解率は94%前後になります。

3層ディープラーニングモデルの出力
0 2.449633365625842 0.0951
100 1.5349024136564533 0.6818
・・・
9800 0.21109711296030495 0.9416
9900 0.21035221505955806 0.9419

重み行列が1つ増えて、入力層に対応する重み行列がV、隠れ層に対応する重み行列がWになります。
また重み行列の初期値は1固定ではなく、He Normalという手法で初期化します。

// 重み行列をHe Normalで初期化
V = initW(D, H);
W = initW(H + 1, N);

隠れ層の活性化関数はReLUを使います。

// 各層の出力値を計算
RealMatrix a = dot(x, V);
RealMatrix b = reLU(a);
RealMatrix b1 = addBiasCol(b);
RealMatrix u = dot(b1, W);
RealMatrix yp = softmax(u);

誤差逆伝播で層ごとに誤差を計算します。
ReLUの微分はstep関数で求められます。

// 各層の誤差を計算
RealMatrix yd = sub(yp, yt);
RealMatrix bd = mult(step(a), dot(yd, t(removeBias(W))));

各層の勾配に学習率を掛けて各層の重みを更新します。

// 勾配に学習率を掛けて各層の重みを更新
W = sub(W, mult(div(dot(t(b1), yd), batchSize), alpha));
V = sub(V, mult(div(dot(t(x), bd), batchSize), alpha));

4層ディープラーニングモデル

最後に隠れ層を1層追加した4層のディープラーニングモデルを実装します。

DeepLearning2.java
package math.deeplearning.ch10;

import org.apache.commons.math3.linear.RealMatrix;
import java.io.IOException;
import java.util.*;
import static math.deeplearning.common.Util.*;

/**
 * ディープラーニング(隠れ層2層).
 */
public class DeepLearning2 {
    // 学習データの行数
    private int M;
    // 学習データの列数(画像のピクセル数)
    private int D;
    // 分類クラス数
    private int N;
    // 学習回数
    private int iters;
    // バッチデータサイズ
    private int batchSize;
    // 学習率
    private double alpha;

    // MNIST画像データ
    private RealMatrix xAll;
    private RealMatrix xTest;
    private RealMatrix ytAll;
    private RealMatrix ytTest;

    // 重み行列
    private RealMatrix U;
    private RealMatrix V;
    private RealMatrix W;

    public DeepLearning2(int iters, int H, int batchSize, double alpha) throws IOException {
        // MNISTデータセットを読み込む
        xAll = addBiasCol(div(loadMnistImage(MNIST_TRAIN_IMAGE_FILE_NAME), 255));
        xTest = addBiasCol(div(loadMnistImage(MNIST_TEST_IMAGE_FILE_NAME), 255));
        ytAll = oneHotEncode(loadMnistLabel(MNIST_TRAIN_LABEL_FILE_NAME), 10);
        ytTest = oneHotEncode(loadMnistLabel(MNIST_TEST_LABEL_FILE_NAME), 10);

        M = xAll.getRowDimension();
        D = xAll.getColumnDimension();
        N = ytAll.getColumnDimension();

        this.iters = iters;
        this.batchSize = batchSize;
        this.alpha = alpha;

        // 重み行列をHe Normalで初期化
        U = initW(D, H);
        V = initW(H + 1, H);
        W = initW(H + 1, N);
    }

    public static void main(String... args) throws Exception {
        // 学習回数を10000、隠れ層のニューロン数を128、バッチサイズを512、学習率を0.01に設定する
        DeepLearning2 dl2 = new DeepLearning2(10000, 128, 512, 0.01);
        // 学習する
        dl2.learn();
    }

    public void learn() {
        // ランダムサンプリングのindexを初期化
        List<Integer> indexes = new ArrayList<>();
        for (int i = 0; i < M; i++) indexes.add(i);

        for (int i = 0; i < iters; i++) {
            // 学習データをサンプリング
            List<Integer> index = randIndex(indexes, M, batchSize);
            RealMatrix x = sampling(xAll, index);
            RealMatrix yt = sampling(ytAll, index);

            // 各層の出力値を計算
            RealMatrix a = dot(x, U);
            RealMatrix b = reLU(a);
            RealMatrix b1 = addBiasCol(b);
            RealMatrix c = dot(b1, V);
            RealMatrix d = reLU(c);
            RealMatrix d1 = addBiasCol(d);
            RealMatrix u = dot(d1, W);
            RealMatrix yp = softmax(u);
            // 各層の誤差を計算
            RealMatrix yd = sub(yp, yt);
            RealMatrix dd = mult(step(c), dot(yd, t(removeBias(W))));
            RealMatrix bd = mult(step(a), dot(dd, t(removeBias(V))));
            // 勾配に学習率を掛けて各層の重みを更新
            W = sub(W, mult(div(dot(t(d1), yd), batchSize), alpha));
            V = sub(V, mult(div(dot(t(b1), dd), batchSize), alpha));
            U = sub(U, mult(div(dot(t(x), bd), batchSize), alpha));

            // 一定回数学習するごとに誤差と精度を表示
            if (i % 100 == 0) {
                RealMatrix p = softmax(dot(addBiasCol(reLU(dot(addBiasCol(reLU(dot(xTest, U))), V))), W));
                System.out.print(i + " " + crossEntropy(ytTest, p) + " ");
                System.out.println(calcAccuracy(ytTest, p));
            }
        }
    }
}

3層ディープラーニングモデルの誤差は0.21前後、正解率は94%前後でしたが、4層ディープラーニングモデルは隠れ層が2層になって表現力が増したことで、誤差は0.15前後、正解率は95%前後に改善します。

4層ディープラーニングモデルの出力
0 2.418195100372308 0.1035
100 1.4860509069098333 0.6518
・・・
9800 0.15087335052084305 0.9552
9900 0.14996068028907877 0.9556

重み行列の初期化処理では、追加した隠れ層に対応する重み行列が1つ増えます。

3層ディープラーニングモデル
// 重み行列をHe Normalで初期化
V = initW(D, H);
W = initW(H + 1, N);
4層ディープラーニングモデル
// 重み行列をHe Normalで初期化
U = initW(D, H);
V = initW(H + 1, H);
W = initW(H + 1, N);

各層の出力値の計算では、追加された隠れ層1層分の処理が増えます。

3層ディープラーニングモデル
// 各層の出力値を計算
RealMatrix a = dot(x, V);
RealMatrix b = reLU(a);
RealMatrix b1 = addBiasCol(b);
RealMatrix u = dot(b1, W);
RealMatrix yp = softmax(u);
4層ディープラーニングモデル
// 各層の出力値を計算
RealMatrix a = dot(x, U);
RealMatrix b = reLU(a);
RealMatrix b1 = addBiasCol(b);
RealMatrix c = dot(b1, V);
RealMatrix d = reLU(c);
RealMatrix d1 = addBiasCol(d);
RealMatrix u = dot(d1, W);
RealMatrix yp = softmax(u);

誤差逆伝播でも同様に追加された隠れ層の処理が増えます。

3層ディープラーニングモデル
// 各層の誤差を計算
RealMatrix yd = sub(yp, yt);
RealMatrix bd = mult(step(a), dot(yd, t(removeBias(W))));
4層ディープラーニングモデル
// 各層の誤差を計算
RealMatrix yd = sub(yp, yt);
RealMatrix dd = mult(step(c), dot(yd, t(removeBias(W))));
RealMatrix bd = mult(step(a), dot(dd, t(removeBias(V))));

重み行列の更新処理も追加された隠れ層に対応する重み行列の更新処理が増えます。

3層ディープラーニングモデル
// 勾配に学習率を掛けて各層の重みを更新
W = sub(W, mult(div(dot(t(b1), yd), batchSize), alpha));
V = sub(V, mult(div(dot(t(x), bd), batchSize), alpha));
4層ディープラーニングモデル
// 勾配に学習率を掛けて各層の重みを更新
W = sub(W, mult(div(dot(t(d1), yd), batchSize), alpha));
V = sub(V, mult(div(dot(t(b1), dd), batchSize), alpha));
U = sub(U, mult(div(dot(t(x), bd), batchSize), alpha));

隠れ層が1層の3層ディープラーニングモデルのコードと比較すると、隠れ層の重み行列と計算処理が1層分追加されているだけであることが分かります。

まとめ

Javaで線形回帰モデルを実装して回帰問題を解くことからスタートして、最終的にはシンプルなディープラーニングモデルを実装して手書き数字をある程度正しく識別することができました。
著者さんや出版社の回し者ではありませんが、ディープラーニングの仕組みを理解したい方はぜひ書籍も読んでみてください。
そして「なるほどなー!」と思った方は、ご自分の得意な言語で実装し直してみてください。
読むだけの数倍は理解が深まると思います。

ちなみに『ゼロから作るDeeplearning』のPythonのコードJavaで実装してみたのですが、7章のCNNでデータが行列から4次元のテンソルになってJavaできれいに実装できず、6章までで止まってしまっています・・・。
またモチベーションが復活したら7章も実装して記事にまとめたいと思います。

最後まで読んでいただきありがとうございました!

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

SpringBootで日付の相関チェックをする

概要

SpringBootで複数項目を比較してバリデーションを行う方法(相関チェック)をまとめる。

コード

まず、ユーザから日付の入力を受け取るエンティティクラス。

package com.sample.Form;

import com.sample.Annotation.DayCheck;
import lombok.Data;

import javax.validation.constraints.NotNull;
import java.time.LocalDate;

@Data
@DayCheck //(1)
public class SearchLogForm {
  @NotNull(message = "日付入れてね^^") //(2)
  private LocalDate startDate;

  @NotNull(message = "日付入れてね^^")
  private LocalDate endDate;
}

解説
(1)->このあと作成する自作のアノテーションをクラス全体に付与。
(2)->そもそも日付が入力されていなければ意味がないのでNullを許さないように@NotNullアノテーションを付与。

次に、エンティティに付与するアノテーションクラス。

package com.sample.Annotation;

import com.sample.Validation.DayCheckValidation;

import javax.validation.Constraint;
import javax.validation.Payload;
import java.lang.annotation.*;

@Documented
@Constraint(validatedBy = DayCheckValidation.class) //(1)
@Target(ElementType.TYPE) //(2)
@Retention(RetentionPolicy.RUNTIME)
public @interface DayCheck {
  String message() default "終了日は開始日よりあとを入力してね^^";

  Class<?>[] groups() default {};

  Class<? extends Payload>[] payload() default {};
}

解説
(1)->このあと作成するバリデーションチェッククラスを指定。
(2)->複数項目をバリデートする場合クラスそのものに付与するのでTYPEを指定。

最後に、バリデーションチェッククラスを作成。

package com.sample.Validation;

import com.sample.Annotation.DayCheck;
import com.sample.Form.SearchLogForm;

import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;

public class DayCheckValidation implements ConstraintValidator<DayCheck, SearchLogForm> { //(1)

  @Override
  public boolean isValid(SearchLogForm value, ConstraintValidatorContext context) {
    if (value.getStartDate() == null || value.getEndDate() == null) { //(2)
      return false;
    }
    return value.getStartDate().isBefore(value.getEndDate()) ? true : false; //(3)
  }
}

解説
(1)->ConstraintValidatorの<>の中には、カンマ前には上で作成したアノテーションを、後にはアノテーションを付与する(チェックを行う)エンティティクラスを指定。
(2)->日付がnullだとぬるぽが発生するのでnullチェック。
(3)->日付を比較。終了日が開始日より前だった場合falseを、あとだった場合trueを返すように実装。

実行結果

1611130067549.jpg
開始日を終了日より前の日付にすると...

1611130001999.jpg
ちゃんとチェックできてました。

参考文献

https://terasolunaorg.github.io/guideline/public_review/ArchitectureInDetail/Validation.html#validation-correlation-check
https://itneko.com/kotlin-validate-correlation/

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Java Powerpointにフッタを挿入

 

今日はJavaでPowerpointにフッタを挿入する方法を紹介します。Spire.Presentation for Javaというライブラリを使ってPowerpointにフッタを簡単に挿入することができます。

下準備

1.E-iceblueの公式サイトからFree Spire. Presentation for Java無料版をダウンロードしてください。

f:id:lendoris:20210120151857p:plain

2.IDEを起動して新規プロジェクトを作成してから、インストールされたファイルにあった相応しいSpire. Presentation.jarを参照に追加してください。

f:id:lendoris:20210120151914p:plain

コード

import com.spire.presentation.FileFormat;
import com.spire.presentation.Presentation;

public class AddFooter {
    public static void main(String[] args) throws Exception {

 //PowerPointドキュメントをロードします。
        Presentation presentation = new Presentation();
        presentation.loadFromFile("Input.pptx");

        //フッタを入れます。
        presentation.setFooterText("フッタです");
        //フッタを表示にします。
        presentation.setFooterVisible(true);
        //ページ番号を表示にします。
        presentation.setSlideNumberVisible(true);
        //日付を表示にします。
        presentation.setDateTimeVisible(true);

        //保存します。
        presentation.saveToFile("AddFooter.pptx", FileFormat.PPTX_2010);
    }
}

実行結果

f:id:lendoris:20210120155822p:plain

 

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Java  PowerPoint パスワード設定と解除

Spire.Presentation for Javaを使ってPowerPointのファイルにパスワード設定することができます。今回はPowerPointにパスワードを設定・解除する方法を紹介していきます。

下準備

1.E-iceblueの公式サイトからFree Spire. Presentation for Java無料版をダウンロードしてください。

f:id:lendoris:20210120151857p:plain

2.IDEを起動して新規プロジェクトを作成してから、インストールされたファイルにあった相応しいSpire. Presentation.jarを参照に追加してください。

f:id:lendoris:20210120151914p:plain

パスワードの設定

import com.spire.presentation.*;

public class Protect {
public static void main(String[] args) throws Exception {

//ファイルをロードします。
Presentation presentation = new Presentation();
presentation.loadFromFile("C:\\Users\\Administrator\\Desktop\\Sample.pptx");

//パスワードをつけます
presentation.encrypt("e-iceblue");

//保存します。
presentation.saveToFile("output/Encrypted.pptx", FileFormat.PPTX_2010);
}

実行結果 

f:id:lendoris:20210120152033p:plain 

読み取りパスワードの設定

Presentation presentation = new Presentation();
presentation.loadFromFile("C:\\Users\\Administrator\\Desktop\\Sample.pptx");

//ファイルを保護します。
presentation.protect("123456");

//保存します。
presentation.saveToFile("output/Readonly.pptx", FileFormat.PPTX_2010);

実行結果

f:id:lendoris:20210120152159p:plain

 パスワードの解除

Presentation presentation = new Presentation();
presentation.loadFromFile("C:\\Users\\Administrator\\Desktop\\Encrypted.pptx", FileFormat.PPTX_2010,"e-iceblue");

//パスワードを解除します。
presentation.removeEncryption();

//保存します。
presentation.saveToFile("output/Decrypted.pptx", FileFormat.PPTX_2010);

パスワードの変更

Presentation presentation = new Presentation();
presentation.loadFromFile("C:\\Users\\Administrator\\Desktop\\Encrypted.pptx", FileFormat.PPTX_2010,"e-iceblue");

//パスワードを解除します。
presentation.removeEncryption();

//パスワードを再設定します。
presentation.encrypt("Newpass");

//保存します。
presentation.saveToFile("output/Modifypass.pptx", FileFormat.PPTX_2010);

 

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

フィボナッチ数列の初歩メモ



・フィボナッチ数列

フィボナッチ数列は、イタリアの数学者レオナルド・フィボナッチが考えた「ウサギ算」から導かれる数列です。この数列は、自然界の現象に数多く出現し、ヒマワリの種の配列にもフィボナッチ数列の法則が働いているといわれている。

n番目のフィボナッチ数をFnで表すと、Fnは再帰的に

F0 = 0
F1 = 1

Fn+2 = Fn + Fn+1 (n0)

で定義される。



1番目と2番目の値を足すと3番目の値になる。
言い方を変えると、ひとつ前とふたつ前の値を足した数が次の数となる。

・実際に書いたコード

public class TryJava0120 {

    public static void main(String[] args) {

        int f0, f1, fn;

        f0 = 0;
        System.out.println("f0= " + f0);

        f1 = 1;
        System.out.println("f1= " + f1);

        for (int i = 2; i <= 20; i++) {
            fn = f0 + f1;

            System.out.println(fn);
            f0 = f1;
            f1 = fn;

            System.out.println(f0 + " + " + f1 + "は");
        }

    }

}



・実行結果

f0= 0
f1= 1
1
1 + 1
2
1 + 2
3
2 + 3
5
3 + 5
8
5 + 8
13
8 + 13
21
13 + 21
34
21 + 34
55
34 + 55
89
55 + 89
144
89 + 144
233
144 + 233
377
233 + 377
610
377 + 610
987
610 + 987
1597
987 + 1597
2584
1597 + 2584
4181
2584 + 4181
6765
4181 + 6765

前々回の計算結果(f0) + 前回の計算結果(f1)のイコールとして、変数fnに値が代入され出力される。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Java基礎 ファイルの読み書き(1)

FileWriter、FileReaderを使ってファイルを読み書きする

まずファイル名を指定し、Fileオブジェクトを生成します。
それをもとにキャラクタストリームである「FileWriter」「FileReader」を使って、char単位で読み書きを行います。

ちなみにキャラクターストリームを使用すると、文字コードが自動的に変換されるので、ファイルが保存されているOSの文字コードを意識することなく入出力を行えます。

以下のコードは、「text.txt」というファイルを生成し書き込みを行ったのち、
そのファイルの中身を読み込んで出力しているというプログラムです。

Main.java
import java.io.*;

public class Main {
    public static void main(String[] args) {
        try (FileWriter text1 = new FileWriter(new File("text.txt"));
                FileReader text2 = new FileReader(new File("text.txt"))) {
            text1.write("こんにちは。");
            text1.flush();
            int i = 0;
            while ((i = text2.read()) != -1) {
                System.out.print((char) i);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}


「こんにちは」と書き込んだあとにflush()メソッドを使用していますが、これは呼び出した瞬間に強制的にファイルへ書き込みを行うよう命令する処理を意味します。

一つ前に実行しているwrite()メソッドはデータの書き込みを要求しますが、このメソッドを呼び出したすぐにファイルに書き込まれるとは限りません。このプログラムでは、最初に書き込んだ文字を読み込んで出力したいため、必ず読み込み前に書き込みが行われているように強制的な書き込みを命じています。

また、while文の中では、FileReaderクラスのread()メソッドを使用してファイルに書き込まれている文字を一文字ずつ取得しint型で返しています。read()メソッドは1回実行するたびに1文字読み込みを行い、ファイルの終わりに達すると-1を返すため、その条件を判定文に入れています。

read()メソッドで取得したint型の情報を文字として出力するためには、char型への明示的なキャストが必要なため、キャスト式を書いてchar型に変換しています。

このような処理を行い、ファイルの読み書きを行うことができます。

次回はBufferedReader,BefferedWriterクラスや、シリアライズ等について記載できたらと思います。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Java基礎 ファイルの読み書き

FileWriter、FileReaderを使ってファイルを読み書きする

まずファイル名を指定し、Fileオブジェクトを生成します。
それをもとにキャラクタストリームである「FileWriter」「FileReader」を使って、char単位で読み書きを行います。

ちなみにキャラクターストリームを使用すると、文字コードが自動的に変換されるので、ファイルが保存されているOSの文字コードを意識することなく入出力を行えます。

以下のコードは、「text.txt」というファイルを生成し書き込みを行ったのち、
そのファイルの中身を読み込んで出力しているというプログラムです。

Main.java
import java.io.*;

public class Main {
    public static void main(String[] args) {
        try (FileWriter text1 = new FileWriter(new File("text.txt"));
                FileReader text2 = new FileReader(new File("text.txt"))) {
            text1.write("こんにちは。");
            text1.flush();
            int i = 0;
            while ((i = text2.read()) != -1) {
                System.out.print((char) i);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}


「こんにちは」と書き込んだあとにflush()メソッドを使用していますが、これは呼び出した瞬間に強制的にファイルへ書き込みを行うよう命令する処理を意味します。

一つ前に実行しているwrite()メソッドはデータの書き込みを要求しますが、このメソッドを呼び出したすぐにファイルに書き込まれるとは限りません。このプログラムでは、最初に書き込んだ文字を読み込んで出力したいため、必ず読み込み前に書き込みが行われているように強制的な書き込みを命じています。

また、while文の中では、FileReaderクラスのread()メソッドを使用してファイルに書き込まれている文字を一文字ずつ取得しint型で返しています。read()メソッドは1回実行するたびに1文字読み込みを行い、ファイルの終わりに達すると-1を返すため、その条件を判定文に入れています。

read()メソッドで取得したint型の情報を文字として出力するためには、char型への明示的なキャストが必要なため、キャスト式を書いてchar型に変換しています。

このような処理を行い、ファイルの読み書きを行うことができます。

次回はBufferedReader,BefferedWriterクラスや、シリアライズ等について記載できたらと思います。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Oracle Autonomous Database 21c でJDBC Thin接続してみてみた

Autonomous DatabaseではTransport Layer Security (TLSv1.2)を使用するセキュアな接続が要求されます。 JDBC Thinドライバを使用するJavaアプリケーションには、Oracle WalletまたはJava KeyStore (JKS)が必要です。
ということで、JDBCコード・サンプルからDataSourceSample.javaUCPSample.javaをダウンロードしてJDBC Thin接続できることを確認してみてみます。

■ 環境

項目 version
Autonomous Database 21c(21.1.0.0.0)
Oracle Instant Client 12.2.0.1 or 19.8.0.0.0
JDK JDK8(openjdk 1.8.0) or JDK11(openjdk 11.0.9.11)
JDBC ojdbc8 (Oracle Database 12c Release 2 (12.2.0.1) or 19c (19.9) drivers)

Autonomous DatabaseとhrスキーマーとOracle Clientは以下を参考に事前に設定しておきます。
 ・参考: Autonomous Database 21c へsqlplus接続してHRスキーマ作成してみてみた

■ JDK 8 インストール

● Java 8 (JDK) Runtime インストール

[root@oci-inst01 opc]# yum install java-1.8.0-openjdk
    読み込んだプラグイン:langpacks, ulninfo
    依存性の解決をしています
    --> トランザクションの確認を実行しています。
    ---> パッケージ java-1.8.0-openjdk.x86_64 1:1.8.0.275.b01-0.el7_9 を インストール

● Java 8 (JDK) Development Kit インストール

[root@oci-inst01 opc]# yum install java-1.8.0-openjdk-devel
    読み込んだプラグイン:langpacks, ulninfo
    依存性の解決をしています
    --> トランザクションの確認を実行しています。
    ---> パッケージ java-1.8.0-openjdk-devel.x86_64 1:1.8.0.275.b01-0.el7_9 を インストール
    --> 依存性解決を終了しました。

    依存性を解決しました

    ========================================================================================================================================================
    Package                                     アーキテクチャー          バージョン                                   リポジトリー                   容量
    ========================================================================================================================================================
    インストール中:
    java-1.8.0-openjdk-devel                    x86_64                    1:1.8.0.275.b01-0.el7_9                      ol7_latest                    9.7 M

    トランザクションの要約
    ========================================================================================================================================================
    インストール  1 パッケージ

    総ダウンロード容量: 9.7 M
    インストール容量: 40 M
    Is this ok [y/d/N]:  y
    Downloading packages:
    java-1.8.0-openjdk-devel-1.8.0.275.b01-0.el7_9.x86_64.rpm                                                                        | 9.7 MB  00:00:00
    Running transaction check
    Running transaction test
    Transaction test succeeded
    Running transaction
    インストール中          : 1:java-1.8.0-openjdk-devel-1.8.0.275.b01-0.el7_9.x86_64                                                                 1/1
    検証中                  : 1:java-1.8.0-openjdk-devel-1.8.0.275.b01-0.el7_9.x86_64                                                                 1/1

    インストール:
    java-1.8.0-openjdk-devel.x86_64 1:1.8.0.275.b01-0.el7_9

    完了しました!

● Java Version確認

[opc@oci-inst01 ~]$ java -version
    openjdk version "1.8.0_275"
    OpenJDK Runtime Environment (build 1.8.0_275-b01)
    OpenJDK 64-Bit Server VM (build 25.275-b01, mixed mode)

■ JDK 11 インストール

● Java 11 (JDK) Runtime インストール

[opc@oci-inst01 ~]$ sudo yum install -y java-11-openjdk
        読み込んだプラグイン:langpacks, ulninfo
        依存性の解決をしています
        --> トランザクションの確認を実行しています。
        ---> パッケージ java-11-openjdk.x86_64 1:11.0.9.11-2.0.1.el7_9 を インストール
        --> 依存性の処理をしています: java-11-openjdk-headless(x86-64) = 1:11.0.9.11-2.0.1.el7_9 のパッケージ: 1:java-11-openjdk-11.0.9.11-2.0.1.el7_9.x86_64
        --> トランザクションの確認を実行しています。
        ---> パッケージ java-11-openjdk-headless.x86_64 1:11.0.9.11-2.0.1.el7_9 を インストール
        --> 依存性解決を終了しました。

        依存性を解決しました

        ========================================================================================================================================================
        Package                                     アーキテクチャー          バージョン                                   リポジトリー                   容量
        ========================================================================================================================================================
        インストール中:
        java-11-openjdk                             x86_64                    1:11.0.9.11-2.0.1.el7_9                      ol7_latest                    224 k
        依存性関連でのインストールをします:
        java-11-openjdk-headless                    x86_64                    1:11.0.9.11-2.0.1.el7_9                      ol7_latest                     39 M

        トランザクションの要約
        ========================================================================================================================================================
        インストール  1 パッケージ (+1 個の依存関係のパッケージ)

        総ダウンロード容量: 39 M
        インストール容量: 165 M
        Downloading packages:
        (1/2): java-11-openjdk-11.0.9.11-2.0.1.el7_9.x86_64.rpm                                                                          | 224 kB  00:00:00
        (2/2): java-11-openjdk-headless-11.0.9.11-2.0.1.el7_9.x86_64.rpm                                                                 |  39 MB  00:00:01
        --------------------------------------------------------------------------------------------------------------------------------------------------------
        合計                                                                                                                     35 MB/s |  39 MB  00:00:01
        Running transaction check
        Running transaction test
        Transaction test succeeded
        Running transaction
        インストール中          : 1:java-11-openjdk-headless-11.0.9.11-2.0.1.el7_9.x86_64                                                                 1/2
        インストール中          : 1:java-11-openjdk-11.0.9.11-2.0.1.el7_9.x86_64                                                                          2/2
        検証中                  : 1:java-11-openjdk-11.0.9.11-2.0.1.el7_9.x86_64                                                                          1/2
        検証中                  : 1:java-11-openjdk-headless-11.0.9.11-2.0.1.el7_9.x86_64                                                                 2/2

        インストール:
        java-11-openjdk.x86_64 1:11.0.9.11-2.0.1.el7_9

        依存性関連をインストールしました:
        java-11-openjdk-headless.x86_64 1:11.0.9.11-2.0.1.el7_9

        完了しました!

● Java 8 (JDK) Development Kit インストール

[opc@oci-inst01 ~]$ yum install java-11-openjdk-devel
        読み込んだプラグイン:langpacks, ulninfo
        このコマンドを実行するには root である必要があります。
        [opc@oci-inst01 ~]$ sudo yum install java-11-openjdk-devel
        読み込んだプラグイン:langpacks, ulninfo
        依存性の解決をしています
        --> トランザクションの確認を実行しています。
        ---> パッケージ java-11-openjdk-devel.x86_64 1:11.0.9.11-2.0.1.el7_9 を インストール
        --> 依存性解決を終了しました。

        依存性を解決しました

        ========================================================================================================================================================
        Package                                   アーキテクチャー           バージョン                                   リポジトリー                    容量
        ========================================================================================================================================================
        インストール中:
        java-11-openjdk-devel                     x86_64                     1:11.0.9.11-2.0.1.el7_9                      ol7_latest                     3.3 M

        トランザクションの要約
        ========================================================================================================================================================
        インストール  1 パッケージ

        総ダウンロード容量: 3.3 M
        インストール容量: 5.2 M
        Is this ok [y/d/N]: y
        Downloading packages:
        java-11-openjdk-devel-11.0.9.11-2.0.1.el7_9.x86_64.rpm                                                                           | 3.3 MB  00:00:00
        Running transaction check
        Running transaction test
        Transaction test succeeded
        Running transaction
        インストール中          : 1:java-11-openjdk-devel-11.0.9.11-2.0.1.el7_9.x86_64                                                                    1/1
        検証中                  : 1:java-11-openjdk-devel-11.0.9.11-2.0.1.el7_9.x86_64                                                                    1/1

        インストール:
        java-11-openjdk-devel.x86_64 1:11.0.9.11-2.0.1.el7_9

        完了しました!

■ Javaのバージョンの切り替え

複数のJDK 8と11等がインストールされている場合
使用したいバージョンは、切り替えをすることで使いたいバージョンを設定できます

● Java バージョン切り替え

alternativesコマンドで表示される選択番号を選択することで、JAVAバージョンを切り替えることができます

[opc@oci-inst01 ~]$ sudo alternatives --config java

    2 プログラムがあり 'java' を提供します。

    選択       コマンド
    -----------------------------------------------
    *+ 1           java-1.8.0-openjdk.x86_64 (/usr/lib/jvm/java-1.8.0-openjdk-1.8.0.275.b01-0.el7_9.x86_64/jre/bin/java)
    2           java-11-openjdk.x86_64 (/usr/lib/jvm/java-11-openjdk-11.0.9.11-2.0.1.el7_9.x86_64/bin/java)

    Enter を押して現在の選択 [+] を保持するか、選択番号を入力します:2

● javaバージョン確認

[opc@oci-inst01 ~]$ java -version
    openjdk version "11.0.9.1" 2020-11-04 LTS
    OpenJDK Runtime Environment 18.9 (build 11.0.9.1+1-LTS)
    OpenJDK 64-Bit Server VM 18.9 (build 11.0.9.1+1-LTS, mixed mode, sharing)

■ 環境変数の設定

● JDK 8(openjdk 1.8.0)の場合

・JAVA_HOME pathの確認

[opc@oci-inst01 jvm]$ dirname $(readlink $(readlink $(which java)))
    /usr/lib/jvm/java-1.8.0-openjdk-1.8.0.275.b01-0.el7_9.x86_64/jre/bin

・環境変数の設定

export JAVA_HOME=/usr/lib/jvm/java-1.8.0-openjdk-1.8.0.275.b01-0.el7_9.x86_64
export PATH=$PATH:$JAVA_HOME/bin
export CLASSPATH=.:$JAVA_HOME/jre/lib:$JAVA_HOME/lib:$JAVA_HOME/lib/tools.jar 
export JRE_HOME=$JAVA_HOME/jre

● JDK 11 (openjdk 11.0.9.1)の場合

・JAVA_HOME pathの確認

[opc@oci-inst01 ~]$ dirname $(readlink $(readlink $(which java)))
    /usr/lib/jvm/java-11-openjdk-11.0.9.11-2.0.1.el7_9.x86_64/bin

・環境変数の設定

export JAVA_HOME=/usr/lib/jvm/jre-11-openjdk-11.0.9.11-2.0.1.el7_9.x86_64
export PATH=$PATH:$JAVA_HOME/bin
export CLASSPATH=.:$JAVA_HOME/jre/lib:$JAVA_HOME/lib:$JAVA_HOME/lib/tools.jar 
export JRE_HOME=$JAVA_HOME/jre

■ JDK 8 追加設定

JDKバージョンがJDK8u162より小さい場合は、JCE Unlimited Strength Jurisdiction Policy Filesをダウンロードする必要があります。 インストール・ノートについては、READMEファイルを参照してください。 「Java Cryptography Extension (JCE) Unlimited Strength Jurisdiction Policy Files 8ダウンロード」からJCEファイルをダウンロードします。
JDK11, JDK10, or JDK9の場合は不要

●JCE Unlimited Strength Jurisdiction Policy Files設定

1) ダウンロード

・JCE Unlimited Strength Jurisdiction Policy Files
https://www.oracle.com/java/technologies/javase-jce8-downloads.html

[root@oci-inst01 tmp]# ls -l
    合計 16
    -rw-rw-r--. 1 opc opc 12417  1月 18 04:39 jce_policy-8.zip

2) zip解凍

[root@oci-inst01 tmp]# unzip jce_policy-8.zip
    Archive:  jce_policy-8.zip
    creating: UnlimitedJCEPolicyJDK8/
    inflating: UnlimitedJCEPolicyJDK8/US_export_policy.jar
    inflating: UnlimitedJCEPolicyJDK8/local_policy.jar
    inflating: UnlimitedJCEPolicyJDK8/README.txt

3) US_export_policy.jar 設定
JDK8(java-1.8.0)の $JAVA_HOME/jre/lib/security 配下へファイルを配置

[root@oci-inst01 tmp]# ls -l UnlimitedJCEPolicyJDK8/
    合計 24
    -rw-r--r--. 1 root root 7921  7月  1  2020 README.txt
    -rw-r--r--. 1 root root 5373  6月 19  2020 US_export_policy.jar
    -rw-r--r--. 1 root root 5372  6月 19  2020 local_policy.jar

[root@oci-inst01 UnlimitedJCEPolicyJDK8]# mv UnlimitedJCEPolicyJDK8/US_export_policy.jar local_policy.jar /usr/lib/jvm/java-1.8.0-openjdk-1.8.0.275.b01-0.el7_9.x86_64/jre/lib/security

[root@oci-inst01 UnlimitedJCEPolicyJDK8]# ls -l /usr/lib/jvm/java-1.8.0-openjdk-1.8.0.275.b01-0.el7_9.x86_64/jre/lib/security
    合計 76
    -rw-r--r--. 1 root root  5373  6月 19  2020 US_export_policy.jar
    -rw-r--r--. 1 root root  2488 12月 15 18:10 blacklisted.certs
    lrwxrwxrwx. 1 root root    41  1月 18 05:12 cacerts -> ../../../../../../../etc/pki/java/cacerts
    -rw-r--r--. 1 root root  2567 12月 15 18:10 java.policy
    -rw-r--r--. 1 root root 47872  1月  1  2014 java.security
    -rw-r--r--. 1 root root  5372  6月 19  2020 local_policy.jar
    -rw-r--r--. 1 root root   139 12月 15 18:14 nss.cfg
    drwxr-xr-x. 4 root root    38  1月 18 05:12 policy

● ウォレットのロケーションの設定

・$JAVA_HOME/jre/lib/security/java.securityファイル設定

1) java.securityファイル確認

[opc@oci-inst01 ~]$ ls -l /usr/lib/jvm/java-1.8.0-openjdk-1.8.0.275.b01-0.el7_9.x86_64/jre/lib/security/java.security
    -rw-r--r--. 1 root root 47872  1月  1  2014 java.security

2) java.securityファイル設定
・末尾に以下1行追加

 security.provider.14=oracle.security.pki.OraclePKIProvider

[root@oci-inst01 security]# vi /usr/lib/jvm/java-1.8.0-openjdk-1.8.0.275.b01-0.el7_9.x86_64/jre/lib/security/java.security
[root@oci-inst01 security]# tail -2 java.security
    #Wallet Loction
    security.provider.14=oracle.security.pki.OraclePKIProvider

・ojdbc.propertiesファイル設定
$TNS_ADMIN配下にojdbc.properties作成設定

[opc@oci-inst01 ~]$ cd $TNS_ADMIN
[opc@oci-inst01 ~]$ vi ojdbc.properties
[opc@oci-inst01 ~]$ cat ojdbc.properties
    # Use the following properties to use JKS, comment out the oracle.net.wallet_location property above 
    # and set the correct password for both trustStorePassword and keyStorePassword. 
    # It's the password provided while downloading the wallet credentials from the DB Connection tab 
    oracle.net.ssl_server_dn_match=true 
    javax.net.ssl.trustStore=${TNS_ADMIN}/truststore.jks 
    javax.net.ssl.trustStorePassword=Password
    javax.net.ssl.keyStore=${TNS_ADMIN}/keystore.jks 
    javax.net.ssl.keyStorePassword=Password

■ JDBCインストール

● ojdbc8-full.tar.gzダウンロード

今回あえて、古いOracle Database 12.2.0.1 JDBC Driver & UCP DownloadsからZipped JDBC Driver and Companion JARsのojdbc8-full.tar.gzをダウンロード

推奨は最新の Oracle Database 19c (19.9)のojdbc8-full.tar.gz でもOK,結局手順同じで実行結果も同じになりました。
実際、Application Continuity等の機能が使用できる最新のJDBCを推奨、古いJDBCは使わないようにしましょう。

・JDBC and UCP Downloads page
https://www.oracle.com/ph/database/technologies/appdev/jdbc-downloads.html

● JDBC (ojdbc8)配置

今回は、$HOME/oracleディレクトリへ配置してみます

1) 配置ディレクトリ移動
[opc@oci-inst01 ~]$ cd ~/oracle

2) tar.gz解凍配置

[opc@oci-inst01 oracle]$ tar zxvf /tmp/ojdbc8-full.tar.gz
[opc@oci-inst01 oracle]$ ls -l ojdbc8-full/
    合計 8320
    -rw-r--r--. 1 opc opc    2595  8月 20  2018 README.txt
    -r-xr-xr-x. 1 opc opc   11596  8月  2  2018 ojdbc.policy
    -r--r--r--. 1 opc opc 4161744  8月  2  2018 ojdbc8.jar
    -r--r--r--. 1 opc opc  144428  8月  2  2018 ons.jar
    -r--r--r--. 1 opc opc  307817  8月  2  2018 oraclepki.jar
    -r--r--r--. 1 opc opc 1661545  8月  2  2018 orai18n.jar
    -r--r--r--. 1 opc opc  205152  8月  2  2018 osdt_cert.jar
    -r--r--r--. 1 opc opc  306854  8月  2  2018 osdt_core.jar
    -r--r--r--. 1 opc opc   29103  8月  2  2018 simplefan.jar
    -r--r--r--. 1 opc opc 1398331  8月  2  2018 ucp.jar
    -r--r--r--. 1 opc opc  262415  8月  2  2018 xdb6.jar

■ JDBC Thin接続: DataSourceSample.java

JDBC Thin接続テスト用のDataSourceSample.javaを「JDBCコード・サンプル」からダウンロードしテストしてみます。
・GitHub: JDBCコード・サンプル

● DataSourceSample.java設定

ダウンロードした DataSourceSample.javaの下記3行を環境に合わせた、TNS接続名とhrスキーマパスワードを設定

final static String DB_URL="jdbc:oracle:thin:@atp_high";
final static String DB_USER = "hr";
final static String DB_PASSWORD = "Password";

・DataSourceSample.javaサンプル

[opc@oci-inst01 work]$ cat DataSourceSample.java

/* Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.*/
/*
   DESCRIPTION    
   The code sample shows how to use the DataSource API to establish a connection
   to the Database. You can specify properties with "setConnectionProperties".
   This is the recommended way to create connections to the Database.
   Note that an instance of oracle.jdbc.pool.OracleDataSource doesn't provide
   any connection pooling. It's just a connection factory. A connection pool,
   such as Universal Connection Pool (UCP), can be configured to use an
   instance of oracle.jdbc.pool.OracleDataSource to create connections and 
   then cache them.

    Step 1: Enter the Database details in this file. 
            DB_USER, DB_PASSWORD and DB_URL are required
    Step 2: Run the sample with "ant DataSourceSample"

   NOTES
    Use JDK 1.7 and above
   MODIFIED    (MM/DD/YY)
    nbsundar    02/17/15 - Creation 
 */

import java.io.IOException;
import java.io.InputStream;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Properties;

import oracle.jdbc.pool.OracleDataSource;
import oracle.jdbc.OracleConnection;
import java.sql.DatabaseMetaData;

public class DataSourceSample {  
  // The recommended format of a connection URL is the long format with the
  // connection descriptor.
// final static String DB_URL= "jdbc:oracle:thin:@myhost:1521/myorcldbservicename";
final static String DB_URL="jdbc:oracle:thin:@atp_high";


  // For ATP and ADW - use the TNS Alias name along with the TNS_ADMIN when using 18.3 JDBC driver
  // final static String DB_URL="jdbc:oracle:thin:@wallet_dbname?TNS_ADMIN=/Users/test/wallet_dbname";
  // In case of windows, use the following URL 
  // final static String DB_URL="jdbc:oracle:thin:@wallet_dbname?TNS_ADMIN=C:\\Users\\test\\wallet_dbname";
  final static String DB_USER = "hr";
  final static String DB_PASSWORD = "Password";

 /*
  * The method gets a database connection using 
  * oracle.jdbc.pool.OracleDataSource. It also sets some connection 
  * level properties, such as,
  * OracleConnection.CONNECTION_PROPERTY_DEFAULT_ROW_PREFETCH,
  * OracleConnection.CONNECTION_PROPERTY_THIN_NET_CHECKSUM_TYPES, etc.,
  * There are many other connection related properties. Refer to 
  * the OracleConnection interface to find more. 
  */
  public static void main(String args[]) throws SQLException {
    Properties info = new Properties();     
    info.put(OracleConnection.CONNECTION_PROPERTY_USER_NAME, DB_USER);
    info.put(OracleConnection.CONNECTION_PROPERTY_PASSWORD, DB_PASSWORD);          
    info.put(OracleConnection.CONNECTION_PROPERTY_DEFAULT_ROW_PREFETCH, "20");    


    OracleDataSource ods = new OracleDataSource();
    ods.setURL(DB_URL);    
    ods.setConnectionProperties(info);

    // With AutoCloseable, the connection is closed automatically.
    try (OracleConnection connection = (OracleConnection) ods.getConnection()) {
      // Get the JDBC driver name and version 
      DatabaseMetaData dbmd = connection.getMetaData();       
      System.out.println("Driver Name: " + dbmd.getDriverName());
      System.out.println("Driver Version: " + dbmd.getDriverVersion());
      // Print some connection properties
      System.out.println("Default Row Prefetch Value is: " + 
         connection.getDefaultRowPrefetch());
      System.out.println("Database Username is: " + connection.getUserName());
      System.out.println();
      // Perform a database operation 
      printEmployees(connection);
    }   
  }
 /*
  * Displays first_name and last_name from the employees table.
  */
  public static void printEmployees(Connection connection) throws SQLException {
    // Statement and ResultSet are AutoCloseable and closed automatically. 
    try (Statement statement = connection.createStatement()) {      
      try (ResultSet resultSet = statement
          .executeQuery("select first_name, last_name from employees")) {
        System.out.println("FIRST_NAME" + "  " + "LAST_NAME");
        System.out.println("---------------------");
        while (resultSet.next())
          System.out.println(resultSet.getString(1) + " "
              + resultSet.getString(2) + " ");       
      }
    }   
  } 
}

● DataSourceSample.java コンパイル

1) javac実行
DataSourceSample.javaをclassファイルにコンパイル

・実行コマンド

javac -classpath \
/home/opc/oracle/ojdbc8-full/ojdbc8.jar:\
/home/opc/oracle/ojdbc8-full/ucp.jar:\
/home/opc/oracle/ojdbc8-full/oraclepki.jar:\
/home/opc/oracle/ojdbc8-full/osdt_core.jar:\
/home/opc/oracle/ojdbc8-full/osdt_cert.jar:. \
DataSourceSample.java

・実行結果

[opc@oci-inst01 work]$ javac -classpath \
> /home/opc/oracle/ojdbc8-full/ojdbc8.jar:\
> /home/opc/oracle/ojdbc8-full/ucp.jar:\
> /home/opc/oracle/ojdbc8-full/oraclepki.jar:\
> /home/opc/oracle/ojdbc8-full/osdt_core.jar:\
> /home/opc/oracle/ojdbc8-full/osdt_cert.jar:. \
> DataSourceSample.java

[opc@oci-inst01 work]$ ls -l
    -rw-rw-r--. 1 opc opc 3222  1月 19 05:33 DataSourceSample.class
    -rw-rw-r--. 1 opc opc 4297  1月 19 05:33 DataSourceSample.java

● DataSourceSample実行

・実行コマンド

java -classpath \
/home/opc/oracle/ojdbc8-full/ojdbc8.jar:\
/home/opc/oracle/ojdbc8-full/ucp.jar:\
/home/opc/oracle/ojdbc8-full/oraclepki.jar:\
/home/opc/oracle/ojdbc8-full/osdt_core.jar:\
/home/opc/oracle/ojdbc8-full/osdt_cert.jar:. \
DataSourceSample

・実行結果

[opc@oci-inst01 work]$ java -classpath \
> /home/opc/oracle/ojdbc8-full/ojdbc8.jar:\
> /home/opc/oracle/ojdbc8-full/ucp.jar:\
> /home/opc/oracle/ojdbc8-full/oraclepki.jar:\
> /home/opc/oracle/ojdbc8-full/osdt_core.jar:\
> /home/opc/oracle/ojdbc8-full/osdt_cert.jar:. \
> DataSourceSample

    Driver Name: Oracle JDBC driver
    Driver Version: 18.3.0.0.0
    Default Row Prefetch Value is: 20
    Database Username is: HR

    FIRST_NAME  LAST_NAME
    ---------------------
    Ellen Abel
    Sundar Ande
    Mozhe Atkinson
    ・・・

● CLASSPATH OS環境変数設定による実行

javac, java実行時の -classpath は以下のように OS環境変数CLASSPATHを設定でも可能
1) CLASSPATH OS環境変数設定

[opc@oci-inst01 work]$ export CLASSPATH=.:$JAVA_HOME/jre/lib:$JAVA_HOME/lib:$JAVA_HOME/lib/tools.jar:/home/opc/oracle/ojdbc8-full/ojdbc8.jar:/home/opc/oracle/ojdbc8-full/ucp.jar:/home/opc/oracle/ojdbc8-full/oraclepki.jar:/home/opc/oracle/ojdbc8-full/osdt_core.jar:/home/opc/oracle/ojdbc8-full/osdt_cert.jar:. 

2) コンパイル

[opc@oci-inst01 work]$ javac DataSourceSample.java

3) 実行

[opc@oci-inst01 work]$ java DataSourceSample
    Driver Name: Oracle JDBC driver
    Driver Version: 18.3.0.0.0
    Default Row Prefetch Value is: 20
    Database Username is: HR

    FIRST_NAME  LAST_NAME
    ---------------------
    Ellen Abel
    Sundar Ande
    Mozhe Atkinson
    ・・・
    Eleni Zlotkey

■ JDBC Thin接続テスト: UCPSample.java

JDBC Thin接続テスト用のUCPSample.javaを「JDBCコード・サンプル」からダウンロードしテストしてみます。
・GitHub: JDBCコード・サンプル

● UCPSample.java 設定

ダウンロードした UCPSample.javaの下記3行を環境に合わせた、TNS接続名とhrスキーマパスワードを設定

final static String DB_URL="jdbc:oracle:thin:@atp_high";
final static String DB_USER = "hr";
final static String DB_PASSWORD = "Password";

・DataSourceSample.javaサンプル

[opc@oci-inst01 work]$ vi UCPSample.java
[opc@oci-inst01 work]$ cat UCPSample.java
/* Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.*/

/*
 DESCRIPTION
 The code sample demonstrates Universal Connection Pool (UCP) as a client
 side connection pool and does the following.
 (a)Set the connection factory class name to
 oracle.jdbc.pool.OracleDataSource before getting a connection.
 (b)Set the driver connection properties(e.g.,defaultNChar,includeSynonyms).
 (c)Set the connection pool properties(e.g.,minPoolSize, maxPoolSize).
 (d)Get the connection and perform some database operations.
 Step 1: Enter the Database details in DBConfig.properties file.
 USER, PASSWORD, UCP_CONNFACTORY and URL are required.
 Step 2: Run the sample with "ant UCPSample"
 NOTES
 Use JDK 1.7 and above
 MODIFIED    (MM/DD/YY)
 nbsundar    02/13/15 - Creation (Contributor - tzhou)
 */
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Properties;

import oracle.ucp.jdbc.PoolDataSourceFactory;
import oracle.ucp.jdbc.PoolDataSource;

public class UCPSample {
  // final static String DB_URL="jdbc:oracle:thin:@myhost:1521/orclservicename";
  // Use the TNS Alias name along with the TNS_ADMIN - For ATP and ADW
  // final static String DB_URL="jdbc:oracle:thin:@dbname_alias?TNS_ADMIN=/Users/test/wallet_dbname";
  final static String DB_URL="jdbc:oracle:thin:@atp_high";
  final static String DB_USER = "hr";
  final static String DB_PASSWORD = "Password";
  final static String CONN_FACTORY_CLASS_NAME="oracle.jdbc.pool.OracleDataSource";

  /*
   * The sample demonstrates UCP as client side connection pool.
   */
  public static void main(String args[]) throws Exception {
    // Get the PoolDataSource for UCP
    PoolDataSource pds = PoolDataSourceFactory.getPoolDataSource();

    // Set the connection factory first before all other properties
    pds.setConnectionFactoryClassName(CONN_FACTORY_CLASS_NAME);
    pds.setURL(DB_URL);
    pds.setUser(DB_USER);
    pds.setPassword(DB_PASSWORD);
    pds.setConnectionPoolName("JDBC_UCP_POOL");

    // Default is 0. Set the initial number of connections to be created
    // when UCP is started.
    pds.setInitialPoolSize(5);

    // Default is 0. Set the minimum number of connections
    // that is maintained by UCP at runtime.
    pds.setMinPoolSize(5);

    // Default is Integer.MAX_VALUE (2147483647). Set the maximum number of
    // connections allowed on the connection pool.
    pds.setMaxPoolSize(20);

    // Default is 30secs. Set the frequency in seconds to enforce the timeout
    // properties. Applies to inactiveConnectionTimeout(int secs),
    // AbandonedConnectionTimeout(secs)& TimeToLiveConnectionTimeout(int secs).
    // Range of valid values is 0 to Integer.MAX_VALUE. .
    pds.setTimeoutCheckInterval(5);

    // Default is 0. Set the maximum time, in seconds, that a
    // connection remains available in the connection pool.
    pds.setInactiveConnectionTimeout(10);

    // Get the database connection from UCP.
    try (Connection conn = pds.getConnection()) {
      System.out.println("Available connections after checkout: "
          + pds.getAvailableConnectionsCount());
      System.out.println("Borrowed connections after checkout: "
          + pds.getBorrowedConnectionsCount());
      // Perform a database operation
      doSQLWork(conn);
    }
    catch (SQLException e) {
      System.out.println("UCPSample - " + "SQLException occurred : "
          + e.getMessage());
    }
    System.out.println("Available connections after checkin: "
        + pds.getAvailableConnectionsCount());
    System.out.println("Borrowed connections after checkin: "
        + pds.getBorrowedConnectionsCount());
  }

  /*
   * Creates an EMP table and does an insert, update and select operations on
   * the new table created.
   */
  public static void doSQLWork(Connection conn) {
    try {
      conn.setAutoCommit(false);
      // Prepare a statement to execute the SQL Queries.
      Statement statement = conn.createStatement();
      // Create table EMP
      statement.executeUpdate("create table EMP(EMPLOYEEID NUMBER,"
          + "EMPLOYEENAME VARCHAR2 (20))");
      System.out.println("New table EMP is created");
      // Insert some records into the table EMP
      statement.executeUpdate("insert into EMP values(1, 'Jennifer Jones')");
      statement.executeUpdate("insert into EMP values(2, 'Alex Debouir')");
      System.out.println("Two records are inserted.");

      // Update a record on EMP table.
      statement.executeUpdate("update EMP set EMPLOYEENAME='Alex Deborie'"
          + " where EMPLOYEEID=2");
      System.out.println("One record is updated.");

      // Verify the table EMP
      ResultSet resultSet = statement.executeQuery("select * from EMP");
      System.out.println("\nNew table EMP contains:");
      System.out.println("EMPLOYEEID" + " " + "EMPLOYEENAME");
      System.out.println("--------------------------");
      while (resultSet.next()) {
        System.out.println(resultSet.getInt(1) + " " + resultSet.getString(2));
      }
      System.out.println("\nSuccessfully tested a connection from UCP");
    }
    catch (SQLException e) {
      System.out.println("UCPSample - "
          + "doSQLWork()- SQLException occurred : " + e.getMessage());
    }
    finally {
      // Clean-up after everything
      try (Statement statement = conn.createStatement()) {
        statement.execute("drop table EMP");
      }
      catch (SQLException e) {
        System.out.println("UCPSample - "
            + "doSQLWork()- SQLException occurred : " + e.getMessage());
      }
    }
  }
}

1) CLASSPATH OS環境変数設定

[opc@oci-inst01 work]$ export CLASSPATH=.:$JAVA_HOME/jre/lib:$JAVA_HOME/lib:$JAVA_HOME/lib/tools.jar:/home/opc/oracle/ojdbc8-full/ojdbc8.jar:/home/opc/oracle/ojdbc8-full/ucp.jar:/home/opc/oracle/ojdbc8-full/oraclepki.jar:/home/opc/oracle/ojdbc8-full/osdt_core.jar:/home/opc/oracle/ojdbc8-full/osdt_cert.jar:. 

2) コンパイル

[opc@oci-inst01 work]$ javac UCPSample.java

3) 実行

[opc@oci-inst01 work]$ java UCPSample
    Available connections after checkout: 4
    Borrowed connections after checkout: 1
    New table EMP is created
    Two records are inserted.
    One record is updated.

    New table EMP contains:
    EMPLOYEEID EMPLOYEENAME
    --------------------------
    1 Jennifer Jones
    2 Alex Deborie

    Successfully tested a connection from UCP
    Available connections after checkin: 5
    Borrowed connections after checkin: 0

■ 参考

● Download

JDBC and UCP Downloads page
JCE Unlimited Strength Jurisdiction Policy Files
JDBCコード・サンプル

● Oracleマニュアル

JDBC Thin接続とウォレット
Java Connectivity with Autonomous Database (ATP or ADW) using 19c and 18.3 JDBC

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Oracle Autonomous Database 21c へJDBC Thin接続してみてみた

Autonomous DatabaseではTransport Layer Security (TLSv1.2)を使用するセキュアな接続が要求されます。 JDBC Thinドライバを使用するJavaアプリケーションには、Oracle WalletまたはJava KeyStore (JKS)が必要です。
ということで、JDBCコード・サンプルからDataSourceSample.javaUCPSample.javaをダウンロードしてJDBC Thin接続できることを確認してみてみます。

■ 環境

項目 version
Autonomous Database 21c(21.1.0.0.0)
Client OS Oracle Linux Server release 7.9
Oracle Instant Client 12.2.0.1, 19.8.0.0.0, or 21.1.0.0.0
JDK JDK8(openjdk 1.8.0) or JDK11(openjdk 11.0.9.11)
JDBC ojdbc8 (Oracle Database 21c (21.1) drivers)

■ 事前準備

事前に sqlplusでhrスキーマへ接続しSQLできることを確認しておきます。
Autonomous DatabaseとhrスキーマーとOracle Clientは以下を参考に事前に設定しておきます。
 ・参考: Autonomous Database 21c へsqlplus接続してHRスキーマ作成してみてみた

● hrスキーマ接続テスト

[opc@oci-inst01 ~]$ sqlplus hr/Password@atp_high

    SQL*Plus: Release 12.2.0.1.0 Production on Wed Jan 20 01:23:22 2021

    Copyright (c) 1982, 2016, Oracle.  All rights reserved.

    Last Successful login time: Tue Jan 19 2021 15:46:29 +00:00

    Connected to:
    Oracle Database 21c Enterprise Edition Release 21.0.0.0.0 - Production

SQL> select count(*) from employees;

    COUNT(*)
    ----------
        107

■ JDK 8 インストール

● Java 8 (JDK) Runtime インストール

[root@oci-inst01 opc]# yum install java-1.8.0-openjdk
    読み込んだプラグイン:langpacks, ulninfo
    依存性の解決をしています
    --> トランザクションの確認を実行しています。
    ---> パッケージ java-1.8.0-openjdk.x86_64 1:1.8.0.275.b01-0.el7_9 を インストール

● Java 8 (JDK) Development Kit インストール

[root@oci-inst01 opc]# yum install java-1.8.0-openjdk-devel
    読み込んだプラグイン:langpacks, ulninfo
    依存性の解決をしています
    --> トランザクションの確認を実行しています。
    ---> パッケージ java-1.8.0-openjdk-devel.x86_64 1:1.8.0.275.b01-0.el7_9 を インストール
    --> 依存性解決を終了しました。

    依存性を解決しました

    ========================================================================================================================================================
    Package                                     アーキテクチャー          バージョン                                   リポジトリー                   容量
    ========================================================================================================================================================
    インストール中:
    java-1.8.0-openjdk-devel                    x86_64                    1:1.8.0.275.b01-0.el7_9                      ol7_latest                    9.7 M

    トランザクションの要約
    ========================================================================================================================================================
    インストール  1 パッケージ

    総ダウンロード容量: 9.7 M
    インストール容量: 40 M
    Is this ok [y/d/N]:  y
    Downloading packages:
    java-1.8.0-openjdk-devel-1.8.0.275.b01-0.el7_9.x86_64.rpm                                                                        | 9.7 MB  00:00:00
    Running transaction check
    Running transaction test
    Transaction test succeeded
    Running transaction
    インストール中          : 1:java-1.8.0-openjdk-devel-1.8.0.275.b01-0.el7_9.x86_64                                                                 1/1
    検証中                  : 1:java-1.8.0-openjdk-devel-1.8.0.275.b01-0.el7_9.x86_64                                                                 1/1

    インストール:
    java-1.8.0-openjdk-devel.x86_64 1:1.8.0.275.b01-0.el7_9

    完了しました!

● Java Version確認

[opc@oci-inst01 ~]$ java -version
    openjdk version "1.8.0_275"
    OpenJDK Runtime Environment (build 1.8.0_275-b01)
    OpenJDK 64-Bit Server VM (build 25.275-b01, mixed mode)

■ JDK 11 インストール

● Java 11 (JDK) Runtime インストール

[opc@oci-inst01 ~]$ sudo yum install -y java-11-openjdk
        読み込んだプラグイン:langpacks, ulninfo
        依存性の解決をしています
        --> トランザクションの確認を実行しています。
        ---> パッケージ java-11-openjdk.x86_64 1:11.0.9.11-2.0.1.el7_9 を インストール
        --> 依存性の処理をしています: java-11-openjdk-headless(x86-64) = 1:11.0.9.11-2.0.1.el7_9 のパッケージ: 1:java-11-openjdk-11.0.9.11-2.0.1.el7_9.x86_64
        --> トランザクションの確認を実行しています。
        ---> パッケージ java-11-openjdk-headless.x86_64 1:11.0.9.11-2.0.1.el7_9 を インストール
        --> 依存性解決を終了しました。

        依存性を解決しました

        ========================================================================================================================================================
        Package                                     アーキテクチャー          バージョン                                   リポジトリー                   容量
        ========================================================================================================================================================
        インストール中:
        java-11-openjdk                             x86_64                    1:11.0.9.11-2.0.1.el7_9                      ol7_latest                    224 k
        依存性関連でのインストールをします:
        java-11-openjdk-headless                    x86_64                    1:11.0.9.11-2.0.1.el7_9                      ol7_latest                     39 M

        トランザクションの要約
        ========================================================================================================================================================
        インストール  1 パッケージ (+1 個の依存関係のパッケージ)

        総ダウンロード容量: 39 M
        インストール容量: 165 M
        Downloading packages:
        (1/2): java-11-openjdk-11.0.9.11-2.0.1.el7_9.x86_64.rpm                                                                          | 224 kB  00:00:00
        (2/2): java-11-openjdk-headless-11.0.9.11-2.0.1.el7_9.x86_64.rpm                                                                 |  39 MB  00:00:01
        --------------------------------------------------------------------------------------------------------------------------------------------------------
        合計                                                                                                                     35 MB/s |  39 MB  00:00:01
        Running transaction check
        Running transaction test
        Transaction test succeeded
        Running transaction
        インストール中          : 1:java-11-openjdk-headless-11.0.9.11-2.0.1.el7_9.x86_64                                                                 1/2
        インストール中          : 1:java-11-openjdk-11.0.9.11-2.0.1.el7_9.x86_64                                                                          2/2
        検証中                  : 1:java-11-openjdk-11.0.9.11-2.0.1.el7_9.x86_64                                                                          1/2
        検証中                  : 1:java-11-openjdk-headless-11.0.9.11-2.0.1.el7_9.x86_64                                                                 2/2

        インストール:
        java-11-openjdk.x86_64 1:11.0.9.11-2.0.1.el7_9

        依存性関連をインストールしました:
        java-11-openjdk-headless.x86_64 1:11.0.9.11-2.0.1.el7_9

        完了しました!

● Java 8 (JDK) Development Kit インストール

[opc@oci-inst01 ~]$ yum install java-11-openjdk-devel
        読み込んだプラグイン:langpacks, ulninfo
        このコマンドを実行するには root である必要があります。
        [opc@oci-inst01 ~]$ sudo yum install java-11-openjdk-devel
        読み込んだプラグイン:langpacks, ulninfo
        依存性の解決をしています
        --> トランザクションの確認を実行しています。
        ---> パッケージ java-11-openjdk-devel.x86_64 1:11.0.9.11-2.0.1.el7_9 を インストール
        --> 依存性解決を終了しました。

        依存性を解決しました

        ========================================================================================================================================================
        Package                                   アーキテクチャー           バージョン                                   リポジトリー                    容量
        ========================================================================================================================================================
        インストール中:
        java-11-openjdk-devel                     x86_64                     1:11.0.9.11-2.0.1.el7_9                      ol7_latest                     3.3 M

        トランザクションの要約
        ========================================================================================================================================================
        インストール  1 パッケージ

        総ダウンロード容量: 3.3 M
        インストール容量: 5.2 M
        Is this ok [y/d/N]: y
        Downloading packages:
        java-11-openjdk-devel-11.0.9.11-2.0.1.el7_9.x86_64.rpm                                                                           | 3.3 MB  00:00:00
        Running transaction check
        Running transaction test
        Transaction test succeeded
        Running transaction
        インストール中          : 1:java-11-openjdk-devel-11.0.9.11-2.0.1.el7_9.x86_64                                                                    1/1
        検証中                  : 1:java-11-openjdk-devel-11.0.9.11-2.0.1.el7_9.x86_64                                                                    1/1

        インストール:
        java-11-openjdk-devel.x86_64 1:11.0.9.11-2.0.1.el7_9

        完了しました!

■ Javaのバージョンの切り替え

複数のJDK 8と11等がインストールされている場合
使用したいバージョンは、切り替えをすることで使いたいバージョンを設定できます

● Java バージョン切り替え

alternativesコマンドで表示される選択番号を選択することで、JAVAバージョンを切り替えることができます

[opc@oci-inst01 ~]$ sudo alternatives --config java

    2 プログラムがあり 'java' を提供します。

    選択       コマンド
    -----------------------------------------------
    *+ 1           java-1.8.0-openjdk.x86_64 (/usr/lib/jvm/java-1.8.0-openjdk-1.8.0.275.b01-0.el7_9.x86_64/jre/bin/java)
    2           java-11-openjdk.x86_64 (/usr/lib/jvm/java-11-openjdk-11.0.9.11-2.0.1.el7_9.x86_64/bin/java)

    Enter を押して現在の選択 [+] を保持するか、選択番号を入力します:2

● javaバージョン確認

[opc@oci-inst01 ~]$ java -version
    openjdk version "11.0.9.1" 2020-11-04 LTS
    OpenJDK Runtime Environment 18.9 (build 11.0.9.1+1-LTS)
    OpenJDK 64-Bit Server VM 18.9 (build 11.0.9.1+1-LTS, mixed mode, sharing)

■ 環境変数の設定

● JDK 8(openjdk 1.8.0)の場合

・JAVA_HOME pathの確認

[opc@oci-inst01 jvm]$ dirname $(readlink $(readlink $(which java)))
    /usr/lib/jvm/java-1.8.0-openjdk-1.8.0.275.b01-0.el7_9.x86_64/jre/bin

・環境変数の設定

export JAVA_HOME=/usr/lib/jvm/java-1.8.0-openjdk-1.8.0.275.b01-0.el7_9.x86_64
export PATH=$PATH:$JAVA_HOME/bin
export CLASSPATH=.:$JAVA_HOME/jre/lib:$JAVA_HOME/lib:$JAVA_HOME/lib/tools.jar 
export JRE_HOME=$JAVA_HOME/jre

● JDK 11 (openjdk 11.0.9.1)の場合

・JAVA_HOME pathの確認

[opc@oci-inst01 ~]$ dirname $(readlink $(readlink $(which java)))
    /usr/lib/jvm/java-11-openjdk-11.0.9.11-2.0.1.el7_9.x86_64/bin

・環境変数の設定

export JAVA_HOME=/usr/lib/jvm/jre-11-openjdk-11.0.9.11-2.0.1.el7_9.x86_64
export PATH=$PATH:$JAVA_HOME/bin
export CLASSPATH=.:$JAVA_HOME/jre/lib:$JAVA_HOME/lib:$JAVA_HOME/lib/tools.jar 
export JRE_HOME=$JAVA_HOME/jre

■ JDK 8 追加設定

JDKバージョンがJDK8u162より小さい場合は、JCE Unlimited Strength Jurisdiction Policy Filesをダウンロードする必要があります。 インストール・ノートについては、READMEファイルを参照してください。 「Java Cryptography Extension (JCE) Unlimited Strength Jurisdiction Policy Files 8ダウンロード」からJCEファイルをダウンロードします。
JDK11, JDK10, or JDK9の場合は不要

●JCE Unlimited Strength Jurisdiction Policy Files設定

1) ダウンロード

・JCE Unlimited Strength Jurisdiction Policy Files
https://www.oracle.com/java/technologies/javase-jce8-downloads.html

[root@oci-inst01 tmp]# ls -l
    合計 16
    -rw-rw-r--. 1 opc opc 12417  1月 18 04:39 jce_policy-8.zip

2) zip解凍

[root@oci-inst01 tmp]# unzip jce_policy-8.zip
    Archive:  jce_policy-8.zip
    creating: UnlimitedJCEPolicyJDK8/
    inflating: UnlimitedJCEPolicyJDK8/US_export_policy.jar
    inflating: UnlimitedJCEPolicyJDK8/local_policy.jar
    inflating: UnlimitedJCEPolicyJDK8/README.txt

3) US_export_policy.jar 設定
JDK8(java-1.8.0)の $JAVA_HOME/jre/lib/security 配下へファイルを配置

[root@oci-inst01 tmp]# ls -l UnlimitedJCEPolicyJDK8/
    合計 24
    -rw-r--r--. 1 root root 7921  7月  1  2020 README.txt
    -rw-r--r--. 1 root root 5373  6月 19  2020 US_export_policy.jar
    -rw-r--r--. 1 root root 5372  6月 19  2020 local_policy.jar

[root@oci-inst01 UnlimitedJCEPolicyJDK8]# mv UnlimitedJCEPolicyJDK8/US_export_policy.jar local_policy.jar /usr/lib/jvm/java-1.8.0-openjdk-1.8.0.275.b01-0.el7_9.x86_64/jre/lib/security

[root@oci-inst01 UnlimitedJCEPolicyJDK8]# ls -l /usr/lib/jvm/java-1.8.0-openjdk-1.8.0.275.b01-0.el7_9.x86_64/jre/lib/security
    合計 76
    -rw-r--r--. 1 root root  5373  6月 19  2020 US_export_policy.jar
    -rw-r--r--. 1 root root  2488 12月 15 18:10 blacklisted.certs
    lrwxrwxrwx. 1 root root    41  1月 18 05:12 cacerts -> ../../../../../../../etc/pki/java/cacerts
    -rw-r--r--. 1 root root  2567 12月 15 18:10 java.policy
    -rw-r--r--. 1 root root 47872  1月  1  2014 java.security
    -rw-r--r--. 1 root root  5372  6月 19  2020 local_policy.jar
    -rw-r--r--. 1 root root   139 12月 15 18:14 nss.cfg
    drwxr-xr-x. 4 root root    38  1月 18 05:12 policy

● ウォレットのロケーションの設定

・$JAVA_HOME/jre/lib/security/java.securityファイル設定

1) java.securityファイル確認

[opc@oci-inst01 ~]$ ls -l /usr/lib/jvm/java-1.8.0-openjdk-1.8.0.275.b01-0.el7_9.x86_64/jre/lib/security/java.security
    -rw-r--r--. 1 root root 47872  1月  1  2014 java.security

2) java.securityファイル設定
・末尾に以下1行追加

 security.provider.14=oracle.security.pki.OraclePKIProvider

[root@oci-inst01 security]# vi /usr/lib/jvm/java-1.8.0-openjdk-1.8.0.275.b01-0.el7_9.x86_64/jre/lib/security/java.security
[root@oci-inst01 security]# tail -2 java.security
    #Wallet Loction
    security.provider.14=oracle.security.pki.OraclePKIProvider

■ JDBCインストール

● ojdbc8-full.tar.gzダウンロード

・JDBC and UCP Downloads page
https://www.oracle.com/database/technologies/appdev/jdbc-downloads.html
※ 21.1, 19.9 と 12.2のファイルは同じ名前ojdbc8-full.tar.gzですが、Bytes数が異なり中身は別物なので注意

● JDBC (ojdbc8)配置

今回は、$HOME/oracleディレクトリへ配置してみます

1) 配置ディレクトリ移動

[opc@oci-inst01 ~]$ cd $HOME/oracle

2) tar.gz解凍配置

[opc@oci-inst01 oracle]$ tar zxvf /tmp/ojdbc8-full.tar.gz
[opc@oci-inst01 oracle]$ ls -l ojdbc8-full/
    合計 12144
    drwxr-xr-x. 2 opc opc    4096  1月 14 19:52 .
    drwxrwxr-x. 8 opc opc    4096  1月 20 12:56 ..
    -rw-r--r--. 1 opc opc    5903  1月 14 19:51 BASIC_LICENSE.txt
    -rw-r--r--. 1 opc opc    3324  1月 15 10:54 README.txt
    -rw-r--r--. 1 opc opc   11515  1月 14 19:51 ojdbc.policy
    -rw-r--r--. 1 opc opc 5040465  1月 14 19:51 ojdbc8.jar
    -rw-r--r--. 1 opc opc  198469  1月 14 19:51 ons.jar
    -rw-r--r--. 1 opc opc  306476  1月 14 19:51 oraclepki.jar
    -rw-r--r--. 1 opc opc 1664450  1月 14 19:51 orai18n.jar
    -rw-r--r--. 1 opc opc  210338  1月 14 19:51 osdt_cert.jar
    -rw-r--r--. 1 opc opc  312230  1月 14 19:51 osdt_core.jar
    -rw-r--r--. 1 opc opc  345036  1月 14 19:51 rsi.jar
    -rw-r--r--. 1 opc opc   32169  1月 14 19:51 simplefan.jar
    -rw-r--r--. 1 opc opc 1788363  1月 14 19:51 ucp.jar
    -rw-r--r--. 1 opc opc  265864  1月 14 19:51 xdb.jar
    -r--r--r--. 1 opc opc  262415  8月  2  2018 xdb6.jar
    -rw-r--r--. 1 opc opc 1951430  1月 14 19:51 xmlparserv2.jar

3) Version確認

[opc@oci-inst01 ojdbc8-full]$ java -jar ./ojdbc8-full/ojdbc8.jar -version
        Oracle 21.1.0.0.0 JDBC 4.2 compiled with javac 1.8.0_271 on Fri_Oct_09_09:20:04_PDT_2020
    #Default Connection Properties Resource
    #Wed Jan 20 12:57:33 GMT 2021

    ***** JCE UNLIMITED STRENGTH IS INSTALLED ****

■ Java KeyStore使用設定

Java KeyStore (JKS)および18.3 JDBC Thinドライバを使用してAutonomous Transaction Processingに接続するには、次のステップを実行します。
JKSに関連する接続プロパティを設定: JKSに関連する接続プロパティをojdbc.propertiesファイルに追加します。 keyStoreおよびトラスト・ストアのパスワードは、Autonomous Transaction Processingサービス・コンソールからクライアント資格証明.zipファイルをダウンロードする際に指定されるパスワードです。
Oracle WalletではなくSSL接続を使用するには、次のように、キーストアおよびトラスト・ストアのファイルおよびそのパスワードをojdbc.propertiesファイルで指定します。
※ 12.2または以前のJDBCドライバでは、ojdbc.propertiesファイルがサポートされません。 より古いJDBCドライバ・バージョンでは、ウォレットまたはJKS関連プロパティをシステム・プロパティとして渡すか、接続を確立する接続プロパティとして渡す必要があります。

● ojdbc.propertiesファイル設定

$TNS_ADMIN配下にojdbc.propertiesファイルへパスワード設定

[opc@oci-inst01 ~]$ cd $TNS_ADMIN
[opc@oci-inst01 ~]$ pwd 
    /home/opc/oracle/instantclient_19_8/network/admin
[opc@oci-inst01 admin]$ cat ojdbc.properties
    # Connection property while using Oracle wallets.
    oracle.net.wallet_location=(SOURCE=(METHOD=FILE)(METHOD_DATA=(DIRECTORY=${TNS_ADMIN})))
    # FOLLOW THESE STEPS FOR USING JKS
    # (1) Uncomment the following properties to use JKS.
    # (2) Comment out the oracle.net.wallet_location property above
    # (3) Set the correct password for both trustStorePassword and keyStorePassword.
    # It's the password you specified when downloading the wallet from OCI Console or the Service Console.
    #javax.net.ssl.trustStore=${TNS_ADMIN}/truststore.jks
    #javax.net.ssl.trustStorePassword=<password_from_console>
    #javax.net.ssl.keyStore=${TNS_ADMIN}/keystore.jks
    #javax.net.ssl.keyStorePassword=<password_from_console>
    oracle.net.ssl_server_dn_match=true
    javax.net.ssl.trustStore=${TNS_ADMIN}/truststore.jks
    javax.net.ssl.trustStorePassword=パスワード
    javax.net.ssl.keyStore=${TNS_ADMIN}/keystore.jks
    javax.net.ssl.keyStorePassword=パスワード

■ JDBC Thin接続: DataSourceSample.java

JDBC Thin接続テスト用のDataSourceSample.javaを「JDBCコード・サンプル」からダウンロードしテストしてみます。
・GitHub: JDBCコード・サンプル

● DataSourceSample.java設定

ダウンロードした DataSourceSample.javaの下記3行を環境に合わせた、TNS接続名とhrスキーマパスワードを設定

final static String DB_URL="jdbc:oracle:thin:@atp_high";
final static String DB_USER = "hr";
final static String DB_PASSWORD = "Password";

・DataSourceSample.javaサンプル

[opc@oci-inst01 work]$ cat DataSourceSample.java

/* Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.*/
/*
   DESCRIPTION    
   The code sample shows how to use the DataSource API to establish a connection
   to the Database. You can specify properties with "setConnectionProperties".
   This is the recommended way to create connections to the Database.
   Note that an instance of oracle.jdbc.pool.OracleDataSource doesn't provide
   any connection pooling. It's just a connection factory. A connection pool,
   such as Universal Connection Pool (UCP), can be configured to use an
   instance of oracle.jdbc.pool.OracleDataSource to create connections and 
   then cache them.

    Step 1: Enter the Database details in this file. 
            DB_USER, DB_PASSWORD and DB_URL are required
    Step 2: Run the sample with "ant DataSourceSample"

   NOTES
    Use JDK 1.7 and above
   MODIFIED    (MM/DD/YY)
    nbsundar    02/17/15 - Creation 
 */

import java.io.IOException;
import java.io.InputStream;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Properties;

import oracle.jdbc.pool.OracleDataSource;
import oracle.jdbc.OracleConnection;
import java.sql.DatabaseMetaData;

public class DataSourceSample {  
  // The recommended format of a connection URL is the long format with the
  // connection descriptor.
// final static String DB_URL= "jdbc:oracle:thin:@myhost:1521/myorcldbservicename";
final static String DB_URL="jdbc:oracle:thin:@atp_high";


  // For ATP and ADW - use the TNS Alias name along with the TNS_ADMIN when using 18.3 JDBC driver
  // final static String DB_URL="jdbc:oracle:thin:@wallet_dbname?TNS_ADMIN=/Users/test/wallet_dbname";
  // In case of windows, use the following URL 
  // final static String DB_URL="jdbc:oracle:thin:@wallet_dbname?TNS_ADMIN=C:\\Users\\test\\wallet_dbname";
  final static String DB_USER = "hr";
  final static String DB_PASSWORD = "Password";

 /*
  * The method gets a database connection using 
  * oracle.jdbc.pool.OracleDataSource. It also sets some connection 
  * level properties, such as,
  * OracleConnection.CONNECTION_PROPERTY_DEFAULT_ROW_PREFETCH,
  * OracleConnection.CONNECTION_PROPERTY_THIN_NET_CHECKSUM_TYPES, etc.,
  * There are many other connection related properties. Refer to 
  * the OracleConnection interface to find more. 
  */
  public static void main(String args[]) throws SQLException {
    Properties info = new Properties();     
    info.put(OracleConnection.CONNECTION_PROPERTY_USER_NAME, DB_USER);
    info.put(OracleConnection.CONNECTION_PROPERTY_PASSWORD, DB_PASSWORD);          
    info.put(OracleConnection.CONNECTION_PROPERTY_DEFAULT_ROW_PREFETCH, "20");    


    OracleDataSource ods = new OracleDataSource();
    ods.setURL(DB_URL);    
    ods.setConnectionProperties(info);

    // With AutoCloseable, the connection is closed automatically.
    try (OracleConnection connection = (OracleConnection) ods.getConnection()) {
      // Get the JDBC driver name and version 
      DatabaseMetaData dbmd = connection.getMetaData();       
      System.out.println("Driver Name: " + dbmd.getDriverName());
      System.out.println("Driver Version: " + dbmd.getDriverVersion());
      // Print some connection properties
      System.out.println("Default Row Prefetch Value is: " + 
         connection.getDefaultRowPrefetch());
      System.out.println("Database Username is: " + connection.getUserName());
      System.out.println();
      // Perform a database operation 
      printEmployees(connection);
    }   
  }
 /*
  * Displays first_name and last_name from the employees table.
  */
  public static void printEmployees(Connection connection) throws SQLException {
    // Statement and ResultSet are AutoCloseable and closed automatically. 
    try (Statement statement = connection.createStatement()) {      
      try (ResultSet resultSet = statement
          .executeQuery("select first_name, last_name from employees")) {
        System.out.println("FIRST_NAME" + "  " + "LAST_NAME");
        System.out.println("---------------------");
        while (resultSet.next())
          System.out.println(resultSet.getString(1) + " "
              + resultSet.getString(2) + " ");       
      }
    }   
  } 
}

● DataSourceSample.java コンパイル

1) javac実行
DataSourceSample.javaをclassファイルにコンパイル

・実行コマンド

javac -classpath \
/home/opc/oracle/ojdbc8-full/ojdbc8.jar:\
/home/opc/oracle/ojdbc8-full/ucp.jar:\
/home/opc/oracle/ojdbc8-full/oraclepki.jar:\
/home/opc/oracle/ojdbc8-full/osdt_core.jar:\
/home/opc/oracle/ojdbc8-full/osdt_cert.jar:. \
-Doracle.net.tns_admin=/home/opc/oracle/instantclient_12_2/network/admin \
DataSourceSample.java

・実行結果
javacコマンドでコンパイルしてclassファイル作成確認

[opc@oci-inst01 work]$ javac -classpath \
> /home/opc/oracle/ojdbc8-full/ojdbc8.jar:\
> /home/opc/oracle/ojdbc8-full/ucp.jar:\
> /home/opc/oracle/ojdbc8-full/oraclepki.jar:\
> /home/opc/oracle/ojdbc8-full/osdt_core.jar:\
> /home/opc/oracle/ojdbc8-full/osdt_cert.jar:. \
> -Doracle.net.tns_admin=/home/opc/oracle/instantclient_12_2/network/admin \
> DataSourceSample.java

[opc@oci-inst01 work]$ ls -l
    -rw-rw-r--. 1 opc opc 3222  1月 19 05:33 DataSourceSample.class
    -rw-rw-r--. 1 opc opc 4297  1月 19 05:33 DataSourceSample.java

● DataSourceSample実行

・実行コマンド

java -classpath \
/home/opc/oracle/ojdbc8-full/ojdbc8.jar:\
/home/opc/oracle/ojdbc8-full/ucp.jar:\
/home/opc/oracle/ojdbc8-full/oraclepki.jar:\
/home/opc/oracle/ojdbc8-full/osdt_core.jar:\
/home/opc/oracle/ojdbc8-full/osdt_cert.jar:. \
-Doracle.net.tns_admin=/home/opc/oracle/instantclient_12_2/network/admin \
DataSourceSample

・実行結果

[opc@oci-inst01 work]$ java -classpath \
> /home/opc/oracle/ojdbc8-full/ojdbc8.jar:\
> /home/opc/oracle/ojdbc8-full/ucp.jar:\
> /home/opc/oracle/ojdbc8-full/oraclepki.jar:\
> /home/opc/oracle/ojdbc8-full/osdt_core.jar:\
> /home/opc/oracle/ojdbc8-full/osdt_cert.jar:. \
> -Doracle.net.tns_admin=/home/opc/oracle/instantclient_12_2/network/admin \
> DataSourceSample

    Driver Name: Oracle JDBC driver
    Driver Version: 21.1.0.0.0
    Default Row Prefetch Value is: 20
    Database Username is: HR

    FIRST_NAME  LAST_NAME
    ---------------------
    Ellen Abel
    Sundar Ande
    Mozhe Atkinson
    ・・・

● OS環境変数設定による実行

javac, java実行時の -classpath は以下のように OS環境変数\$CLASSPATH設定でも可能
-Doracle.net.tns_admin は OS環境変数\$TNS_ADMIN設定でも可能
1) CLASSPATH OS環境変数設定

[opc@oci-inst01 work]$ export CLASSPATH=.:$JAVA_HOME/jre/lib:$JAVA_HOME/lib:$JAVA_HOME/lib/tools.jar:/home/opc/oracle/ojdbc8-full/ojdbc8.jar:/home/opc/oracle/ojdbc8-full/ucp.jar:/home/opc/oracle/ojdbc8-full/oraclepki.jar:/home/opc/oracle/ojdbc8-full/osdt_core.jar:/home/opc/oracle/ojdbc8-full/osdt_cert.jar:. 
[opc@oci-inst01 work]$ export ORACLE_HOME=/home/opc/oracle/instantclient_12_2
[opc@oci-inst01 work]$ export TNS_ADMIN=$ORACLE_HOME/network/admin

2) コンパイル
javacコマンドでコンパイルしてclassファイル作成確認

[opc@oci-inst01 work]$ javac DataSourceSample.java
[opc@oci-inst01 work]$ ls -l
    -rw-rw-r--. 1 opc opc 3222  1月 19 10:55 DataSourceSample.class
    -rw-rw-r--. 1 opc opc 4297  1月 19 05:33 DataSourceSample.java

3) 実行

[opc@oci-inst01 work]$ java DataSourceSample
    Driver Name: Oracle JDBC driver
    Driver Version: 21.1.0.0.0
    Default Row Prefetch Value is: 20
    Database Username is: HR

    FIRST_NAME  LAST_NAME
    ---------------------
    Ellen Abel
    Sundar Ande
    Mozhe Atkinson
    ・・・
    Eleni Zlotkey

■ JDBC Thin接続テスト: UCPSample.java

JDBC Thin接続テスト用のUCPSample.javaを「JDBCコード・サンプル」からダウンロードしテストしてみます。
・GitHub: JDBCコード・サンプル

● UCPSample.java 設定

ダウンロードした UCPSample.javaの下記3行を環境に合わせた、TNS接続名とhrスキーマパスワードを設定

final static String DB_URL="jdbc:oracle:thin:@atp_high";
final static String DB_USER = "hr";
final static String DB_PASSWORD = "Password";

・DataSourceSample.javaサンプル

[opc@oci-inst01 work]$ vi UCPSample.java
[opc@oci-inst01 work]$ cat UCPSample.java
/* Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.*/

/*
 DESCRIPTION
 The code sample demonstrates Universal Connection Pool (UCP) as a client
 side connection pool and does the following.
 (a)Set the connection factory class name to
 oracle.jdbc.pool.OracleDataSource before getting a connection.
 (b)Set the driver connection properties(e.g.,defaultNChar,includeSynonyms).
 (c)Set the connection pool properties(e.g.,minPoolSize, maxPoolSize).
 (d)Get the connection and perform some database operations.
 Step 1: Enter the Database details in DBConfig.properties file.
 USER, PASSWORD, UCP_CONNFACTORY and URL are required.
 Step 2: Run the sample with "ant UCPSample"
 NOTES
 Use JDK 1.7 and above
 MODIFIED    (MM/DD/YY)
 nbsundar    02/13/15 - Creation (Contributor - tzhou)
 */
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Properties;

import oracle.ucp.jdbc.PoolDataSourceFactory;
import oracle.ucp.jdbc.PoolDataSource;

public class UCPSample {
  // final static String DB_URL="jdbc:oracle:thin:@myhost:1521/orclservicename";
  // Use the TNS Alias name along with the TNS_ADMIN - For ATP and ADW
  // final static String DB_URL="jdbc:oracle:thin:@dbname_alias?TNS_ADMIN=/Users/test/wallet_dbname";
  final static String DB_URL="jdbc:oracle:thin:@atp_high";
  final static String DB_USER = "hr";
  final static String DB_PASSWORD = "Password";
  final static String CONN_FACTORY_CLASS_NAME="oracle.jdbc.pool.OracleDataSource";

  /*
   * The sample demonstrates UCP as client side connection pool.
   */
  public static void main(String args[]) throws Exception {
    // Get the PoolDataSource for UCP
    PoolDataSource pds = PoolDataSourceFactory.getPoolDataSource();

    // Set the connection factory first before all other properties
    pds.setConnectionFactoryClassName(CONN_FACTORY_CLASS_NAME);
    pds.setURL(DB_URL);
    pds.setUser(DB_USER);
    pds.setPassword(DB_PASSWORD);
    pds.setConnectionPoolName("JDBC_UCP_POOL");

    // Default is 0. Set the initial number of connections to be created
    // when UCP is started.
    pds.setInitialPoolSize(5);

    // Default is 0. Set the minimum number of connections
    // that is maintained by UCP at runtime.
    pds.setMinPoolSize(5);

    // Default is Integer.MAX_VALUE (2147483647). Set the maximum number of
    // connections allowed on the connection pool.
    pds.setMaxPoolSize(20);

    // Default is 30secs. Set the frequency in seconds to enforce the timeout
    // properties. Applies to inactiveConnectionTimeout(int secs),
    // AbandonedConnectionTimeout(secs)& TimeToLiveConnectionTimeout(int secs).
    // Range of valid values is 0 to Integer.MAX_VALUE. .
    pds.setTimeoutCheckInterval(5);

    // Default is 0. Set the maximum time, in seconds, that a
    // connection remains available in the connection pool.
    pds.setInactiveConnectionTimeout(10);

    // Get the database connection from UCP.
    try (Connection conn = pds.getConnection()) {
      System.out.println("Available connections after checkout: "
          + pds.getAvailableConnectionsCount());
      System.out.println("Borrowed connections after checkout: "
          + pds.getBorrowedConnectionsCount());
      // Perform a database operation
      doSQLWork(conn);
    }
    catch (SQLException e) {
      System.out.println("UCPSample - " + "SQLException occurred : "
          + e.getMessage());
    }
    System.out.println("Available connections after checkin: "
        + pds.getAvailableConnectionsCount());
    System.out.println("Borrowed connections after checkin: "
        + pds.getBorrowedConnectionsCount());
  }

  /*
   * Creates an EMP table and does an insert, update and select operations on
   * the new table created.
   */
  public static void doSQLWork(Connection conn) {
    try {
      conn.setAutoCommit(false);
      // Prepare a statement to execute the SQL Queries.
      Statement statement = conn.createStatement();
      // Create table EMP
      statement.executeUpdate("create table EMP(EMPLOYEEID NUMBER,"
          + "EMPLOYEENAME VARCHAR2 (20))");
      System.out.println("New table EMP is created");
      // Insert some records into the table EMP
      statement.executeUpdate("insert into EMP values(1, 'Jennifer Jones')");
      statement.executeUpdate("insert into EMP values(2, 'Alex Debouir')");
      System.out.println("Two records are inserted.");

      // Update a record on EMP table.
      statement.executeUpdate("update EMP set EMPLOYEENAME='Alex Deborie'"
          + " where EMPLOYEEID=2");
      System.out.println("One record is updated.");

      // Verify the table EMP
      ResultSet resultSet = statement.executeQuery("select * from EMP");
      System.out.println("\nNew table EMP contains:");
      System.out.println("EMPLOYEEID" + " " + "EMPLOYEENAME");
      System.out.println("--------------------------");
      while (resultSet.next()) {
        System.out.println(resultSet.getInt(1) + " " + resultSet.getString(2));
      }
      System.out.println("\nSuccessfully tested a connection from UCP");
    }
    catch (SQLException e) {
      System.out.println("UCPSample - "
          + "doSQLWork()- SQLException occurred : " + e.getMessage());
    }
    finally {
      // Clean-up after everything
      try (Statement statement = conn.createStatement()) {
        statement.execute("drop table EMP");
      }
      catch (SQLException e) {
        System.out.println("UCPSample - "
            + "doSQLWork()- SQLException occurred : " + e.getMessage());
      }
    }
  }
}

1) CLASSPATH OS環境変数設定

[opc@oci-inst01 work]$ export CLASSPATH=.:$JAVA_HOME/jre/lib:$JAVA_HOME/lib:$JAVA_HOME/lib/tools.jar:/home/opc/oracle/ojdbc8-full/ojdbc8.jar:/home/opc/oracle/ojdbc8-full/ucp.jar:/home/opc/oracle/ojdbc8-full/oraclepki.jar:/home/opc/oracle/ojdbc8-full/osdt_core.jar:/home/opc/oracle/ojdbc8-full/osdt_cert.jar:. 
[opc@oci-inst01 work]$ export ORACLE_HOME=/home/opc/oracle/instantclient_12_2
[opc@oci-inst01 work]$ export TNS_ADMIN=$ORACLE_HOME/network/admin

2) コンパイル
javacコマンドでコンパイルしてclassファイル作成確認

[opc@oci-inst01 work]$ javac UCPSample.java
[opc@oci-inst01 work]$ ls -l
    -rw-rw-r--. 1 opc opc 4866  1月 19 15:46 UCPSample.class
    -rw-rw-r--. 1 opc opc 5675  1月 19 15:46 UCPSample.java

3) 実行

[opc@oci-inst01 work]$ java UCPSample
    Available connections after checkout: 4
    Borrowed connections after checkout: 1
    New table EMP is created
    Two records are inserted.
    One record is updated.

    New table EMP contains:
    EMPLOYEEID EMPLOYEENAME
    --------------------------
    1 Jennifer Jones
    2 Alex Deborie

    Successfully tested a connection from UCP
    Available connections after checkin: 5
    Borrowed connections after checkin: 0

■ 参考

● Download

JDBC and UCP Downloads page
JCE Unlimited Strength Jurisdiction Policy Files
JDBCコード・サンプル

● Oracleマニュアル

JDBC Thin接続とウォレット
Java Connectivity with Autonomous Database (ATP or ADW) using 19c and 18.3 JDBC

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む