PostgreSQLをJavaで動かす

JavaでPostgreSQLを動かしてみる。

##環境

+ macOS Mojave
+ Eclipse 4.7.3
+ PostgreSQL 11.4
+ JDBC Driver 42.2.5（postgresql-42.2.5.jar）


##JDBC Driverの導入
PostgreSQLのRDBにアクセスするためのJavaの標準APIとしてJDBCドライバが必要となる。

まずはそのダウンロードから。

[https://jdbc.postgresql.org/download.html#current](https://jdbc.postgresql.org/download.html#current)


ここでは **postgresql-42.2.5.jar** を使用する。


それを今回のプロジェクト下の任意の場所に配置する（`lib/`の中が分かりやすい？）



![スクリーンショット 2019-07-23 16.28.17.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/426462/75882852-d798-709f-eec7-b982e48c45dc.png)




しかしこのままではどこを参照しているのかが分からないのでパスを設定する必要がある。



macであれば
>`postgresql-42.2.5.jar`をクリックして**プロジェクトタブ** --> **プロパティ** --> **Javaのビルドパス** --> **ライブラリ** --> **外部JARの追加** --> `postgresql-42.2.5.jar`を探してopen --> **適用して閉じる**


で参照ライブラリの中に`postgresql-42.2.5.jar`が参照されていたらOK


![スクリーンショット 2019-07-23 16.41.34.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/426462/4527d59f-4260-c044-b12e-33e7f3926403.png)







##使用するDB

今回は全て以前使用したものを再利用していく。


ユーザーは以下の通り

```sql:terminal
customer=# \l
                                List of databases
   Name    |    Owner    | Encoding | Collate | Ctype |   Access privileges   
-----------+-------------+----------+---------+-------+-----------------------
 customer  | sf213471118 | UTF8     | C       | C     | 

```


テーブルは**customer**テーブルを使用する


```sql:terminal
customer=# \d
          List of relations
 Schema |   Name   | Type  |    Owner   
--------+----------+-------+-------------
 public | customer | table | sf213471118
```

**customerテーブル**の中身は以下の通り

```sql:terminal
customer=# SELECT * FROM customer;
       id        |   name   | gender | age 
-----------------+----------+--------+-----
 ﻿10001           | 田中太郎 | m       |  31
 10002           | 中村花子 | w       |  44
 10003           | 佐藤一郎 | m       |  20
 10004           | 内田彩   | w       |  18
 10005           | 高橋次郎 | m       |  56
 10006           | 中里悟   | m       |  34
(6 rows)
```


##Javaの記述における大事な部分


記述していく上で忘れないようにしたいところを残しておく。

###例外処理

```java:.java
   public static void main(String[] args) throws Exception {
      try {
          System.out.println("DB access start");
          PostgreSQL_test.dbConnect();
      } catch (Exception e) {
    	  System.out.println("error main()");
    	  e.printStackTrace();
      } finally {
    	  System.out.println("DB access finished");
      }
   }
```


例外処理は`try-catch(-finally)`で記述する。

この例だと`Exception e`としているためエラーの場所しか特定できないが、catchの条件を変えて並列記述することでエラー内容を瞬時に把握することも可能。

例えば

```java:.java
try {
    file_reader = new FileReader(customer);
    ~
  } catch (FileNotFoundException f) {          // ファイル探索エラー
    System.out.println("cannot find file");
    f.printStackTrace();
  } catch (IOException i) {                    //ファイル入出力エラー
    System.out.println("in/out error");
    i.printStackTrace();
  } finally {
    try {
      if(file_reader != null) {
        file_reader.close();
      }
    } catch (IOException i) {                  //ファイルclose中エラー
      System.out.println(i);
      i.printStackTrace();
    }
  }
```



というフローがあったとして、

①`try - catch - catch`のほう

+ `new FileReader(customer)`をしたがそのファイル名が見つからないとき
+ **customer**ファイルはあるが出力できなかったとき

の2つを考慮して分岐してある。


②`finally - try - catch`のほう

+ close中のエラー

のみ検出している。



例外処理は`FileNotFoundException`や`IOException`以外にも沢山あるが、`Exception`とすれば全てのエラーを引っ掛けることができる（2つ上のプログラムの通り場所の特定くらいしか思いつかないが。。。）



###DB接続に向けて

####JDBCの初期化

DBに接続する前にJDBCドライバを初期化する必要がある。

PostgreSQLの場合は以下のように記述する


```java:.java
Class.forName("org.postgresql.Driver").newInstance();
```

####DB接続の確立/DB切断

またDBの接続を確立させるために**DriverManager**クラスが標準で持っている**getConnection**メソッドを使用する必要がある。

```java:.java
connection = DriverManager.getConnection( <URL> , <USER>, <PASSWORD>);
```

URLに関しては`jdbc:postgresql://<場所>:<ポート番号>/<DB名>`で表記する。

今回は以下で設定した。

|場所|ポート番号|DB名|
|:---:|:---:|:---:|
|localhost|5432|customer|



切断に関しては、万が一のことも考えて**finally**で行うようにする。

```java:.java
rs.close();    //rsはResultSetインターフェースの変数
```

####SQL文の記述

SQLをDBに送るときには、まず**Connection**インターフェースで定義されている**createStatement**メソッドを使用してステートメントを作成する必要がある（SQLを送るときに必ずセットで記述するもの）

```java:.java
statement = con.createStatement();
```

上記が終わったらSQL文を問い合わせる記述をする

```java:.java
resultset = stm.executeQuery(" <SQL文> ");
```

####データの出力

**ResultSet**インターフェースの**next()**メソッドで1行づつ処理を行うことができる。

```java:.java
while (rs.next()) {
　　　System.out.println(rs.getInt("<column>"));
     System.out.println(rs.getString("<column>"));
}
```





##今回のJava記述


```java:PostgreSQL_test.java
package customer;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;

public class PostgreSQL_test {

   public static void main(String[] args) throws Exception {
      try {
          System.out.println("DB access start");
          PostgreSQL_test.dbConnect();
      } catch (Exception e) {
    	  System.out.println("error main()");
    	  e.printStackTrace();
      } finally {
    	  System.out.println("DB access finished");
      }
   }

   private static void dbConnect() throws Exception {
      Connection con = null;
      Statement stm = null;
      ResultSet rs = null;
      int num = 1;

      String url		= "jdbc:postgresql://localhost:5432/customer";
      String user		= "sf213471118";
      String password	= "";          //今回パスワードは設定していない

      try {
         Class.forName("org.postgresql.Driver").newInstance();
         con = DriverManager.getConnection(url, user, password);
         stm = con.createStatement();
         rs = stm.executeQuery("SELECT * FROM customer");
         while (rs.next()) {
        	 System.out.println("[" + num + " 件目]");
        	 num++;
        	 System.out.println("id     : " + rs.getString("id"));
        	 System.out.println("name   : " + rs.getString("name"));
        	 System.out.println("gender : " + rs.getString("gender"));
        	 System.out.println("age    : " + rs.getInt("age"));
         }
      } catch(SQLException e) {
    	  System.out.println("error dbConnect()");
    	  e.printStackTrace();
      	}finally {
      		try {
      			if ((rs != null)||(stm != null)||(con != null)) {
      				rs.close();
      			}
      		} catch (Exception e) {
      	         e.printStackTrace();
            }
      	}
      }
}
```

これを動かすと、以下が返ってくる。


```text:JavaConsole
DB access start
[1 件目]
id     : ﻿10001         
name   : 田中太郎
gender : m  
age    : 31
[2 件目]
id     : 10002          
name   : 中村花子
gender : w  
age    : 44
[3 件目]
id     : 10003          
name   : 佐藤一郎
gender : m  
age    : 20
[4 件目]
id     : 10004          
name   : 内田彩
gender : w  
age    : 18
[5 件目]
id     : 10005          
name   : 高橋次郎
gender : m  
age    : 56
[6 件目]
id     : 10006          
name   : 中里悟
gender : m  
age    : 34
DB access finished
```

本当であれば`rs.next()`で現在の件数名を出力したかったのだが、よく分からなかったので姑息な手で出力してしまった。。。

~~どなたかやり方を教えていただけるととても嬉しいです~~

##【追記】 2019/7/29

コメントを下さりありがとうございます。以下の通り修正しました。


①ループの部分の試行回数を`rs.getRow()`で受取り




```java:PostgreSQL_test.java
    while (rs.next()) {
      	 System.out.println("[" + rs.getRow() + " 件目]");        /* 修正部分 */
       	 System.out.println("id     : " + rs.getString("id"));
       	 System.out.println("name   : " + rs.getString("name"));
       	 System.out.println("gender : " + rs.getString("gender"));
       	 System.out.println("age    : " + rs.getInt("age"));
    }
```


②close漏れを修正しました

```java:PostgreSQL_test.java
     try {
         if ((rs != null)||(stm != null)||(con != null)) {
             rs.close();
             stm.close();      /* 修正部分 */
             con.close();      /* 修正部分 */
          }
      } catch (Exception e) {
                 e.printStackTrace();
        }
```


改めてありがとうございました。