Gradle(Spring boot)で作ったWeb APIをHerokuにデプロイしてMySQL接続してダンプをリストアするまで

## 理由
~~制作中のAndroidアプリでWeb APIをたたくときにいちいち起動するのが面倒くさい~~
せっかくなので作ったWeb APIをデプロイしてみたい
初歩的なところでつまずいてしまったので同じ過ちを繰り返さないための備忘録です

## ゴール
* Gradle(Spring boot)で作ったWeb APIをherokuにデプロイする
* MySQL接続をする
* 自作したAndoroidアプリからたたける

## herokuとは
インフラストラクチャ管理が不要で、アプリケーションの開発から実行、運用までのすべてをクラウドで完結できるクラウドベースの PaaS（サービスとしてのプラットフォーム）
（公式より）
https://jp.heroku.com/home

## やったこと
### 環境
* macOS Big Sur 11.2.1
* Spring boot 2.4.3
* MySQL 5.7

### 0. 事前準備
* MySQL接続するWeb APIをGradleで作る
* 作ったアプリケーションをgit管理している
* herokuアカウントを持っている
* インポートしたいダンプを用意している

### 1. herokuとアプリの準備
#### 1.1 heroku CLI(Command Line Interface)をインストールする
https://devcenter.heroku.com/ja/articles/getting-started-with-gradle-on-heroku#-2

↑からインストーラをダウンロードしてもいいですし、Homebrewが入っている人はターミナルで↓のコマンドを叩いてもいいです。

```terminal:terminal
$ brew install heroku/brew/heroku
```

ちょっぴり時間がかかるのでコーヒーでも飲んで待ちましょう。

#### 1.2 ターミナルでherokuにログインする
```terminal:terminal
$ heroku login
heroku: Press any key to open up the browser to login or q to exit
(略)
Logged in as {メールアドレス}
```

最後に`Logged in as {メールアドレス}`で自分のherokuアカウントに登録しているメールアドレスが表示されたら成功です。

### 2. アプリをデプロイする
#### 2.1 ターミナルでアプリの場所に移動する
##### 初心者のつまずき1: gitで1つのリポジトリにGradleプロジェクトとDockerとAndroidアプリをまとめちゃってるんだけどこの場合はどうすれば？
新たにリポジトリを作ってGradleプロジェクト単体だけ移動しました。
適解かはわからないけど、スターターガイドのサンプルプロジェクトは`build.gradle`がroot階層にある状態になっていたのでそれに倣いました。

そして新しく作ったリポジトリに移動します。

```terminal:terminal
$ cd path/to/repository
```

#### 2.2 herokuでアプリを作成する
```terminal:terminal
$ heroku create {アプリ名}
Creating *** ...done
https://***.herokuapp.com/ | https://git.heroku.com/***
```

（`{アプリ名}`は省略可能）

#### 2.3 コードをデプロイする
```terminal:terminal
$ git push heroku master
```

できませんでした。
##### 初心者のつまずき2: heroku側とのJDKバージョンのミスマッチ
```terminal:terminal
remote:        > Task :compileJava FAILED
remote:
remote:        FAILURE: Build failed with an exception.
remote:
remote:        * What went wrong:
remote:        Execution failed for task ':compileJava'.
remote:        > Could not target platform: 'Java SE 11' using tool chain: 'JDK 8 (1.8)'.
```

Java 8でコンパイルしようとしている。。。
[公式のサンプルプロジェクト](https://github.com/heroku/gradle-getting-started)に参考になりそうなファイルがあったので真似してみました。

```system.properties
java.runtime.version=11
```
この`system.properties`というファイルを新たに作って親階層に置きます。コミットを忘れずに。
そして再度プッシュ。

**成功！！**

```terminal:terminal
...
remote:
remote:        BUILD SUCCESSFUL in 54s
remote:        4 actionable tasks: 4 executed
remote: -----> Discovering process types
remote:        Procfile declares types     -> (none)
remote:        Default types for buildpack -> web
remote:
remote: -----> Compressing...
remote:        Done: 92.3M
remote: -----> Launching...
remote:        Released v3
remote:        https://***.herokuapp.com/ deployed to Heroku
remote:
remote: Verifying deploy... done.
```

#### 2.4 起動しているか確認してみる
```terminal:terminal
$ heroku ps:scale web=1
Scaling dynos... done, now running web at 1:Free
```

もしくは

```terminal:terminal
$ heroku open
```
でアプリ名で生成されたURLにあるアプリが開きます。が、まだデータベース接続の設定をしていないのでエラー画面が表示されます。

### 3. MySQLと接続する
DB接続をします。herokuはデフォルトではpostgresQLらしいですが今回はMySQLを使います。

`ClearDB MySQL`と`JawsDB MySQL`というアドオンがあり、どちらも5MBまでは無料で使えます。

* ClearDB MySQL: https://elements.heroku.com/addons/cleardb
* JawsDB MySQL: https://elements.heroku.com/addons/jawsdb

ただ、注意が必要なのが`ClearDB MySQL`はバージョンがデフォルトで`5.6`で、かつ無料プランでは変えられないそうなので、それ以外を使っている人は`JawsDB`を使うしかなさそうです。ちなみにJawsDB MySQLのデフォルトは`5.7`とのこと。（2021年3月時点）

私は`5.7`を使っていたので`JawsDB MySQL`を使用します。


#### 3.1 DB作成
```terminal:terminal
$ heroku addons:create jawsdb:kitefin --name=[your DB name] --version=5.7
```

`--name=[your DB name]`オプションでデータベースの名前を任意に変えられるらしい。

**重要**
それよりも大事なのが`--version=5.7`オプション。後述しますが、最初これをつけなかったせいでダンプのインポートに失敗しました。というか知らずにClearDB MySQLを入れていたので全然だめだった。
もしダンプをインポートしようと考えている場合は、エクスポートしたMySQLのバージョンと同じものを設定すべし。

#### 3.2 接続に必要な情報を取得
一旦、configを見てみます。

```terminal:terminal
$ heroku config
JAWSDB_URL: mysql://******
```
いましがた取得した`CLEARDB_DATABASE_URL`をよく見ると以下のようなフォーマットになっています。

```
mysql://[username]:[password]@[host]:[port]/[database name]
```

ただ今回はGradle用に`JDBC_DATABASE_URL`を使用します。が、あとでダンプをインポートするときに使うのでメモはしておきましょう。
公式の説明には

>Java、Scala、Clojure、Gradle 用の公式 Heroku buildpack では、dyno の起動時に JDBC_DATABASE_URL​ 環境変数の作成を試みます。

とあるので、特に何もしなくても変数を作ってくれるみたいです。
試しに以下のコマンドで表示してみます。

```terminal:terminal
$ heroku run echo \$JDBC_DATABASE_URL
jdbc:mysql://*******/*******?password=*******&reconnect=true&user=*******
```

ばっちり！
今回のGradleアプリケーションは`application.yml`ファイルでデータベースを設定していたので、それぞれ値を以下のように修正します。

```yml:application.yml
spring:
  datasource:
    url: ${JDBC_DATABASE_URL}
    username: ${JDBC_DATABASE_USERNAME}
    password: ${JDBC_DATABASE_PASSWORD}
```

#### 3.3 確認
再度`$ heroku open`してみます。
ルート直下に表示用のビューを用意している場合はそれが表示されるはずです。
私は用意していないので404 エラーですが。とはいえ問題なくデータベース接続はできていそうです。

|<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/616261/72f0ab55-e685-a372-36bd-033dd7f63eb2.png" width="600px">|
|:-:|

#### 3.4 ダンプをインポートする
いつものコマンドです。各パラメータには3.2 でメモした値を使います。

```terminal:terminal
$ mysql -u[username] -p[password] [database name] --host=[host] < [path/to/your/dump]
```

##### 初心者のつまずき3: MySQLのバージョンが違うのでダンプインポートに失敗する
（結局、根本原因は2択から絞れませんでした。）

```
You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version
```

最初にClearDBでDBを作るときにバージョンを指定しなかったらこんなエラーがでました。
？？？と思ってバージョンを確認してみることに。
　
　
  
```terminal:terminal
$ mysql -u[username] -p[password] [database name] --host=[host]
Welcome to the MySQL monitor.  Commands end with ; or \g.
Your MySQL connection id is 205064
Server version: 5.6.50-log MySQL Community Server (GPL)

Copyright (c) 2000, 2020, Oracle and/or its affiliates. All rights reserved.

Oracle is a registered trademark of Oracle Corporation and/or its
affiliates. Other names may be trademarks of their respective
owners.

Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.

mysql>
```

バージョンが`5.6`だった。というわけで一旦このDBは削除してバージョン指定して再度作り直しましたが、バージョン指定したはずなのにやっぱり`5.6`。どうやら無料プランでは変えられないらしい？なので`JawsDB`に変えました。

　
##### 初心者のつまずき4: 短時間に接続を繰り返しすぎて上限超える
うまくいかないなあと何度もダンプインポートのコマンドをたたきすぎたせいか、やりすぎって怒られてしまいました。1時間くらい放置してから再度試すことにします。

```
java.sql.SQLSyntaxErrorException: User '********' has exceeded the 'max_user_connections' resource (current value: 10)
```

　
##### 初心者のつまずき5: MySQLのバージョン合わせてもダンプインポートに失敗する

1時間後、無事接続できるようにはなりましたが、なぜかまだ同じエラーが出ます。バージョン合わせたはずなのに。

```
ERROR 1064 (42000) at line 1: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'mysqldump: [Warning] Using a password on the command line interface can be insec' at line 1
```

ダンプファイルを開いて1行目を確認すると、エラー文言にも出ている警告文`mysqldump: ~ be insec`が書いてあるだけでした。
つまりインポートする処理には影響なし。
そして、ちょっとしたバージョンの差でもちょっとした書き方の違いでエラーになることがあるということを見かけたので、思い切ってエラーだと言われている1行目を削除することにしました。

**するとうまくいきました！！！**

結局バージョンの違いのせいだったのか1行目の警告文のせいだったのか、はたまたその両方だったのかは今となってはわかりませんが、似たような事象に陥っている人がいたら試してみてください。

### 4 APIをたたいてDBのデータを取得してみる
ようやくWebAPIとDBの接続が完了したのでAPIをたたいてみて動作確認をします。

|<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/616261/d58b25ce-eaf6-723d-2a82-0e4629595024.png" width="600px">|
|:-:|

**成功！！！**

このあとAndroidアプリでもちゃんとデータが取ってこれていることを確認できました。
めでたしめでたし。

### おまけ

ちなみにお手軽にGET、POST、PUT、DELETEなどなどのAPIを叩くことのできるこのアプリケーション。めっちゃ便利です。
[Advanced REST client - Chrome ウェブストア](https://chrome.google.com/webstore/detail/advanced-rest-client/hgmloofddffdnphfgcellkdfbfbjeloo/details?hl=ja-JP)

## 参考
* [Heroku スターターガイド (Gradle) _ Heroku Dev Center](https://devcenter.heroku.com/ja/articles/getting-started-with-gradle-on-heroku)
* [ClearDB MySQL _ Heroku Dev Center](https://devcenter.heroku.com/articles/cleardb)
* [Importing Your Local MySQL Database Into Heroku](https://fuzzyblog.io/blog/heroku/2019/10/16/importing-your-local-mysql-database-into-heroku.html)
* [herokuでMySQL5.7以降を使うためにClearDBからJawsDBに変更する - Qiita](https://qiita.com/reiya018/items/ba8ceaa732edac07e491)