1時間でOracleデータベース接続できるかな（１時間チャレンジ第5弾）

#1時間でOracle接続できるかな（備忘録）#

Oracleの公式にあったクイックスタートという記事を参考にOracleデータベースをインストールしたので、今回は実際に接続を行っていきたいと思います。

これがその記事ですね。
[Oracle Database XEクイックスタート](https://www.oracle.com/jp/database/technologies/appdev/xe/quickstart.html)

**環境**
OSはWindows
Oracleは21c
んで、Eclipseを使用する

##Eclipseにソースコードを張り付ける##
まずは、「Oracle Database XEへの接続」という項目の中からJAVAを探して、そのソースコードを全て張り付けちゃいましょう。
SQLExceptionという例外が発生する可能性があるので、try～catch文で囲んでくださいね。
あっ、ソースコードの流れはこんな感じです。
結論として、この処理では「Hello World!」という文をコンソールに表示します。

1. オラクルデータベースのデータソースを作成する。
2. オラクルデータベースのURLを設定する
3. ユーザ名とパスワードを設定する
4. データベースに接続する
5. SQL文を組み立てる
6. 結果を取得する
7. 結果を表示する

ちなみに、HelloWorld!を取得しているdualというテーブルはOracle用のダミーテーブルです。
実際にdualというテーブルは作成しなくてもよいですよ。

##例外が発生しました１##

```
java.sql.SQLRecoverableException: IOエラー: The Network Adapter could not establish the connection 
```

これは「ネットワーク アダプタが接続を確立できませんでした」という意味です。
ネットワークアダプタとは、コンピュータなどの機器を通信ネットワークに接続するための装置のことで、OracleでいうとOracleリスナーのことですね。

##例外１を解決方法##

ということで、解決していきましょう。
まずは、**Oracleデータベース自体が動いているか**を確認します。
なぜ確認するのかというと、Oracleデータベースが動いていないと、当然その中のリスナーも動かないと思ったからです。
さて、Oracleデータベースが動いているかはインスタンスが起動しているかで確認できます。

###インスタンスの起動の確認###
インスタンスとはデータを操作するための装置です。
インスタンスが起動していないとOracleデータベースも動きません。
逆にインスタンスが起動していれば、Oracleデータベースも動いているということです。
以下のSQL文を打つことで確認します
`SELECT INSTANCE_NAME,STATUS FROM V$INSTANCE;`
インスタンスを確認するには、`V$INSTANCE`というビューを使用します。
`INSTANCE_NAME`がインスタンスの名前で、`STATUS`がインスタンスの状態を表しています。

早速確認していきましょう。

```
SQL> SELECT INSTANCE_NAME,STATUS FROM V$INSTANCE;

INSTANCE_NAME                    STATUS
-------------------------------- ------------------------
xe                               OPEN

```

これを見ると`xe`というインスタンスが、`OPEN`実行可能状態になっていますね。
インスタンスが既に起動していることを確認できました。

###リスナーの起動の確認###
次にリスナーが動いていることを確認しましょう。
リスナーとはクライアントとデータベースサーバをつなぐものです。
クライアントの接続要求を待っており、要求が来たら目的のデータベースに接続してくれます。
逆にリスナーがいなくては、いくらサーバが「準備OK。早く要求来ないかな」と思っていても待ちぼうけをくらうだけです。
リスナーのステータスを確認するためには、SQLPLUSから出た後に`lsnrctl status`で確認できます。

```
C:\Users\user>lsnrctl status

LSNRCTL for 64-bit Windows: Version 21.0.0.0.0 - Production on 27-12月-2021 09:30:01

Copyright (c) 1991, 2021, Oracle.  All rights reserved.

(DESCRIPTION=(ADDRESS=(PROTOCOL=TCP)(HOST=192.168.0.15)(PORT=1521)))に接続中
TNS-12541: TNS: リスナーがありません。
 TNS-12560: TNS: プロトコル・アダプタ・エラー
  TNS-00511: リスナーがありません。
   64-bit Windows Error: 61: Unknown error
(DESCRIPTION=(ADDRESS=(PROTOCOL=IPC)(KEY=EXTPROC1521)))に接続中
TNS-12541: TNS: リスナーがありません。
 TNS-12560: TNS: プロトコル・アダプタ・エラー
  TNS-00511: リスナーがありません。
   64-bit Windows Error: 2: No such file or directory

```

####`TNS-00511: リスナーがありません`と表示されました。####

本当にリスナーがないのか確認しましょう。
リスナーの設定ファイル、listener.oraを開いてっと。



![rapture_20211227100343.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/626652/cd679d09-0c60-c053-84ea-a503f8e63eaa.jpeg)

![rapture_20211227100644.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/626652/295d62ef-49b7-4220-2471-668b15f40048.jpeg)

`LISTENER =`って見えますよね。これがリスナーの設定です。
設定もされているし、どうやらリスナーが動いていないだけのようですね。
では、起動しましょう。

####リスナーの起動####

`lsnrctl start`でリスナーを起動できます。
ただし、この処理は**インストールしたときと同じユーザでないとできません**。
管理者権限があるユーザでインストールしたはずなので、そのユーザでログインして、
コマンドプロンプトを管理者として実行してと

```
C:\Windows\system32>lsnrctl start

LSNRCTL for 64-bit Windows: Version 21.0.0.0.0 - Production on 27-12月-2021 09:40:14

Copyright (c) 1991, 2021, Oracle.  All rights reserved.

tnslsnrを起動しています。お待ちください...

TNSLSNR for 64-bit Windows: Version 21.0.0.0.0 - Production
システム・パラメータ・ファイルはC:\app\user\product\21c\homes\OraDB21Home1\network\admin\listener.oraです。
ログ・メッセージをC:\app\user\product\21c\diag\tnslsnr\DESKTOP-9F93K3D\listener\alert\log.xmlに書き込みました。
リスニングしています: (DESCRIPTION=(ADDRESS=(PROTOCOL=tcp)(HOST=192.168.0.15)(PORT=1521)))
リスニングしています: (DESCRIPTION=(ADDRESS=(PROTOCOL=ipc)(PIPENAME=\\.\pipe\EXTPROC1521ipc)))

(DESCRIPTION=(ADDRESS=(PROTOCOL=TCP)(HOST=192.168.0.15)(PORT=1521)))に接続中
リスナーのステータス
------------------------
別名                      LISTENER
バージョン                TNSLSNR for 64-bit Windows: Version 21.0.0.0.0 - Production
開始日                    27-12月-2021 09:40:21
稼働時間                  0 日 0 時間 0 分 12 秒
トレース・レベル          off
セキュリティ              ON: Local OS Authentication
SNMP                      OFF
デフォルト・サービス           XE
パラメータ・ファイル      C:\app\user\product\21c\homes\OraDB21Home1\network\admin\listener.ora
ログ・ファイル            C:\app\user\product\21c\diag\tnslsnr\DESKTOP-9F93K3D\listener\alert\log.xml
リスニング・エンドポイントのサマリー...
  (DESCRIPTION=(ADDRESS=(PROTOCOL=tcp)(HOST=192.168.0.15)(PORT=1521)))
  (DESCRIPTION=(ADDRESS=(PROTOCOL=ipc)(PIPENAME=\\.\pipe\EXTPROC1521ipc)))
サービスのサマリー...
サービス"CLRExtProc"には、1件のインスタンスがあります。
  インスタンス"CLRExtProc"、状態UNKNOWNには、このサービスに対する1件のハンドラがあります...
コマンドは正常に終了しました。
```

無事に起動できました。
もう一度リスナーの確認をしましょう。

```
C:\Windows\system32>lsnrctl status

LSNRCTL for 64-bit Windows: Version 21.0.0.0.0 - Production on 27-12月-2021 09:41:05

Copyright (c) 1991, 2021, Oracle.  All rights reserved.

(DESCRIPTION=(ADDRESS=(PROTOCOL=TCP)(HOST=192.168.0.15)(PORT=1521)))に接続中
リスナーのステータス
------------------------
別名                      LISTENER
バージョン                TNSLSNR for 64-bit Windows: Version 21.0.0.0.0 - Production
開始日                    27-12月-2021 09:40:21
稼働時間                  0 日 0 時間 0 分 48 秒
トレース・レベル          off
セキュリティ              ON: Local OS Authentication
SNMP                      OFF
デフォルト・サービス           XE
パラメータ・ファイル      C:\app\user\product\21c\homes\OraDB21Home1\network\admin\listener.ora
ログ・ファイル            C:\app\user\product\21c\diag\tnslsnr\DESKTOP-9F93K3D\listener\alert\log.xml
リスニング・エンドポイントのサマリー...
  (DESCRIPTION=(ADDRESS=(PROTOCOL=tcp)(HOST=192.168.0.15)(PORT=1521)))
  (DESCRIPTION=(ADDRESS=(PROTOCOL=ipc)(PIPENAME=\\.\pipe\EXTPROC1521ipc)))
  (DESCRIPTION=(ADDRESS=(PROTOCOL=tcps)(HOST=127.0.0.1)(PORT=5500))(Security=(my_wallet_directory=C:\APP\USER\PRODUCT\21C\admin\XE\xdb_wallet))(Presentation=HTTP)(Session=RAW))
サービスのサマリー...
サービス"424cd63be20a4b1e9a48814308378211"には、1件のインスタンスがあります。
  インスタンス"xe"、状態READYには、このサービスに対する2件のハンドラがあります...
サービス"CLRExtProc"には、1件のインスタンスがあります。
  インスタンス"CLRExtProc"、状態UNKNOWNには、このサービスに対する1件のハンドラがあります...
サービス"XE"には、1件のインスタンスがあります。
  インスタンス"xe"、状態READYには、このサービスに対する2件のハンドラがあります...
サービス"XEXDB"には、1件のインスタンスがあります。
  インスタンス"xe"、状態READYには、このサービスに対する1件のハンドラがあります...
サービス"xepdb1"には、1件のインスタンスがあります。
  インスタンス"xe"、状態READYには、このサービスに対する2件のハンドラがあります...
コマンドは正常に終了しました。
```

リスナーの起動を確認できましたね。
ちなみに、リスナーを終了させるには lsnrctl stopでできますよ。

```
C:\Windows\system32>lsnrctl stop

LSNRCTL for 64-bit Windows: Version 21.0.0.0.0 - Production on 27-12月-2021 09:41:34

Copyright (c) 1991, 2021, Oracle.  All rights reserved.

(DESCRIPTION=(ADDRESS=(PROTOCOL=TCP)(HOST=192.168.0.15)(PORT=1521)))に接続中
コマンドは正常に終了しました。

```

##例外が発生しました２##

```
java.sql.SQLRecoverableException: IOエラー: The Network Adapter could not establish the connection (CONNECTION_ID=MK2DMn9nST+K2WaH6ac9PA==)
```

リスナーが起動したのに実行できませんでした。
そんな方も慌てる必要はありません。
まずは接続するURLが間違っていないか確認してください。
というか、まず最初に疑わないといけないのはこれなんですけどね(-_-;)

### URLの確認を行う ###
URLの設定のルールは以下の通りです。
`jdbc:oracle:thin@//[ホスト名]:[ポート番号]/[DBサービス名]`
実際に書いたURLはこれです。
`jdbc:oracle:thin:@//localhost:1521/XEPDB1`
書き方は一致しているようですね。
ちなみに、`/[DBサービス名]`の部分が`:[DBサービス名]`になっているサンプルソースを見たことありますが、
動きませんからね。（私はここでつまずいた。どれだけ悩んだことか！！！）

####ホスト名、ポート番号、サービス名を確認する####
まずは、ホスト名とポート番号を確認しましょう。
ORACLE_HOMEディレクトリではないhomesディレクトリの中にnetwork/adminディレクトリがあると思います。
この中にはリスナー設定ファイルのlistener.oraと一緒に
クライアントが呼べるサービスの設定が書かれているtnsnames.oraがあると思います。

この中にある`XEPDB1`の値の確認をします。
HOSTで設定されている値がホスト名で、PORTはポート番号です。
また、SERVICE_NAMEがサービス名です。

![rapture_20211228092649.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/626652/ed593570-dea5-36bd-d3a3-aae1586ca113.jpeg)

なので、写真の例だとこんな感じですね。
ホスト名：192.168.0.15
ポート番号：1521
サービス名：XEPDB1

おや、localhostではないIPアドレスが設定されていますね。
おそらくこれが原因でしょう。

`jdbc:oracle:thin:@//localhost:1521/XEPDB1`　を
`jdbc:oracle:thin:@//192.168.0.15:1521/XEPDB1`
に直してと。

##例外が発生しました３##

リスナーの起動も確認し、クライアントで実行するURL（接続記述子）も妥当なものにしました。
今度こそ！！！

```
java.sql.SQLException: ORA-01017: ユーザー名/パスワードが無効です。ログオンは拒否されました。
```

……もう堪忍してぇ。
ユーザー名/パスワードが一致していなかったため、ログオンは拒否されたみたいです。
SYSTEMはOracleのシステムユーザだから、パスワードが間違っているのかな。
インストール時に管理用アカウントのパスワードを設定したときのパスワードとは違うものだからだな。
まぁ、要するにパスワード間違えんなってこと。
実際にGetStartedWithXE、ではなくインストール時に設定したパスワードで実行するとログオンできました。
ちなみに、setUserに"sys as sysdba"setPasswordにインストール時に入力したパスワードを入力したら起動できました。
これはOracleのadmin権限を持つユーザがsys as sysdbaだからです。

ようやくつながりました。
完成したソースがこちらです。

```java

package databaseSample;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;

import oracle.jdbc.datasource.impl.OracleDataSource;

public class app {
	public static void main(String args[]) {
		Connection conn = null;
		PreparedStatement stmt = null;
		ResultSet rs = null;
		try {
				OracleDataSource ods = new OracleDataSource();
				ods.setURL("jdbc:oracle:thin:@//192.168.0.15:1521/XEPDB1");
				ods.setUser("system");
				ods.setPassword("pass");
				conn = ods.getConnection();
				stmt = conn.prepareStatement("SELECT 'Hello World!'FROM dual");
				rs = stmt.executeQuery();
				while (rs.next()) {
					System.out.println(rs.getString(1));
				}
		}catch(SQLException ex) {
			ex.printStackTrace();
		}finally{
			try {
				conn.close();
			} catch (SQLException e) {
				// TODO 自動生成された catch ブロック
				e.printStackTrace();
			}
			try {
				stmt.close();
			} catch (SQLException e) {
				// TODO 自動生成された catch ブロック
				e.printStackTrace();
			}
			try {
				rs.close();
			} catch (SQLException e) {
				// TODO 自動生成された catch ブロック
				e.printStackTrace();
			}
		}
	}
}

```

んで、これもできます

```java
package databaseSample;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;

public class app {
	public static void main(String args[]) {
		try {
				String url = "jdbc:oracle:thin:@//192.168.0.15:1521/XEPDB1";
				String user = "system";
				String password = "pass";

				Class.forName("oracle.jdbc.OracleDriver");
				Connection conn = DriverManager.getConnection(url, user, password);
				PreparedStatement stmt = conn.prepareStatement("SELECT 'Hello World!'FROM dual");
				ResultSet rslt = stmt.executeQuery();
				while (rslt.next()) {
					System.out.println(rslt.getString(1));
				}
		}catch(SQLException ex) {
			ex.printStackTrace();
		}catch(ClassNotFoundException ex) {
			ex.printStackTrace();
		}
	}
}

```

実行結果

```
Hello World!
```

はい、というわけで１時間（執筆の時間入れたら１時間超えたけど）でJDBC接続できました。
ユーザ作成はまたの次回に書きたいと思います。
それでは、ばい