Spring Boot + Heroku Postgres

細かいところで苦戦したので、自分用のメモとして書き残します。

Herokuのアプリに[Heroku Postgres](https://elements.heroku.com/addons/heroku-postgresql)を追加した状態を前提とします。
![2018-08-05_11h48_15.png](https://qiita-image-store.s3.amazonaws.com/0/47737/eba157db-24d6-8297-b473-0d42243d8e05.png)

Spring Bootは2.0.4です。

# ローカルマシンにpsqlコマンドをインストール

Heroku PotgresのSettings(上図)をみると「**Heroku CLI**」とあって、次のコマンドが書いてあります。

```shell
heroku pg:psql データストア名 --app アプリ名
```

自分の環境で試すとエラーになりました。ローカルマシンに**psql**を入れる必要があるとのこと。当たり前ですねｗ

```shell
--> Connecting to データストア名
 !    The local psql command could not be located. For help installing psql, see
 !    https://devcenter.heroku.com/articles/heroku-postgresql#local-setup
```

[上記URL](https://devcenter.heroku.com/articles/heroku-postgresql#local-setup)を参考に、自分のWindows 10環境へ[Windows Setup](https://devcenter.heroku.com/articles/heroku-postgresql#set-up-postgres-on-windows)でインストールしました。そして、環境変数に「C:\Program Files\PostgreSQL\<VERSION>\bin」を設定しました。

```shell
heroku pg:psql データストア名 --app アプリ名
--> Connecting to データストア名
psql (10.4)
SSL 接続 (プロトコル: TLSv1.2、暗号化方式: ECDHE-RSA-AES256-GCM-SHA384、ビット長: 256、圧縮: オフ)
"help" でヘルプを表示します。

アプリ名::DATABASE=>
```

これで無事に接続できました。

# コマンド

ここはSpring BootもHerokuも関係ない部分ですが、自分がPostgresのコマンドを知らなすぎたのでメモ。

## データベース一覧表示

```shell
\l
```
これを実行するとデータベースがたくさん出てきたのですが、他の所有者のDBもみえている…？マルチテナントなのだろうか…。

## データベース選択

```shell
\c データベース名
```

```
SSL 接続 (プロトコル: TLSv1.2、暗号化方式: ECDHE-RSA-AES256-GCM-SHA384、ビット長: 256、圧縮: オフ)
データベース "xxxx" にユーザ "xxxx" として接続しました。
```

## テーブル一覧表示

create tableでテーブルを作成した後、テーブル表示する。

```shell
\d
```

``` 
               リレーション一覧
 スキーマ |   名前   |    型    |     所有者
----------+----------+----------+----------------
 public   | nogizaka | テーブル | xxxxxxxxxxxxx
```

## テーブルの詳細表示

カラムなどの詳細をみる。

```shell
\d nogizaka
```

スキーマは超手抜きです。

```
                      テーブル "public.nogizaka"
  列  |          型           | 照合順序 | Null 値を許容 | デフォルト
------+-----------------------+----------+---------------+------------
 id   | integer               |          |               |
 name | character varying(64) |          |               |
```

# Spring Boot

## Spring Initializr

[Spring Initializr](https://start.spring.io/)でプロジェクトの雛形を作ります。dependencyは次の3つを選びました。Webはなくてもいいのですが、実行時のエンドポイントをREST APIにするために入れました。

- Web
- PostgreSQL
- JPA

![2018-08-06_18h58_47.png](https://qiita-image-store.s3.amazonaws.com/0/47737/11c48eef-d2de-19a6-6749-2cc99d4f01e9.png)

## application.properties

データベースへの接続内容はHeroku PostgresのSettingsにあるDatabase Credentialsをもとに次のように定義して動かしました。

```
spring.datasource.driver-class-name=org.postgresql.Driver
spring.datasource.url=jdbc:postgresql://ホスト名:ポート番号/データベース名?sslmode=require
spring.datasource.username=ユーザ名
spring.datasource.password=パスワード
```

最初ハマったときは次の2点が漏れてました。

- URLの「postgresql」を「postgres」としていた（Heroku CLIに書いてあるURLをコピペしてた)
- sslmodeのパラメータを付けていなかった


## コード

あとはひたすら書くのみ。

**Entity**

```java:Nogizaka.java
package tech.kikutaro.herokupostgres;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;

@Entity
@Table(name="nogizaka")
public class Nogizaka {
    @Id
    @GeneratedValue(strategy=GenerationType.IDENTITY)
    private long id;

    @Column(name="name")
    private String name;

    public long getId() {
        return id;
    }

    public void setId(long id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}
```

setter,getterはLombok使ったほうがよいかも。

**Repository**

```java:NogizakaRepository.java
package tech.kikutaro.herokupostgres;

import org.springframework.data.jpa.repository.JpaRepository;

public interface NogizakaRepository extends JpaRepository<Nogizaka, Long> {

}
```

**Service**

```java:NogizakaService.java
package tech.kikutaro.herokupostgres;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Service
public class NogizakaService {
    @Autowired
    NogizakaRepository nogirepo;

    public long count() {
        return nogirepo.count();
    }

    public Nogizaka getMember(long id) {
        return nogirepo.getOne(id);
    }
}
```

とりあえず雑にcountとgetだけ。

**Main**

```java:HerokupostgresApplication.java
package tech.kikutaro.herokupostgres;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;

@SpringBootApplication
@RestController
public class HerokupostgresApplication {

	@Autowired
	private NogizakaService nogiService;

	public static void main(String[] args) {
		SpringApplication.run(HerokupostgresApplication.class, args);
	}

	@GetMapping("/count")
	public String count() {
		System.out.println(nogiService.count());
		return Long.toString(nogiService.count());
	}

	@GetMapping("/select/{id}")
	public String getName(@PathVariable("id") long id) {
		return nogiService.getMember(id).getName();
	}
}

```

# 実行結果

無事に動きましたー。

http://localhost:8080/nogizaka/1

![2018-08-09_23h07_44.png](https://qiita-image-store.s3.amazonaws.com/0/47737/80c59b7b-64af-e138-8832-613c19e35afd.png)

http://localhost:8080/select/1

![2018-08-10_00h00_18.png](https://qiita-image-store.s3.amazonaws.com/0/47737/819ed3f5-fcd0-c32d-368e-4ff06cc5a94b.png)