がんばってSpring BootとJDBCでIBM CloudのDb2に接続する

### どうしてこの記事を書くことになったのか：
知恵袋みたいなアプリケーションに投稿されたデータを、チャットボット（もちろんIBM Cloud上の）から検索かけられたら強そう！　将来的に自然言語解析もできそう！　と思ったので一時のテンションで始めたらエラーだらけだった。


# 前提
・IBM Cloudにアカウントがある
・Db2のリソースが作成済み（ライトアカウント）

# わかるところから

[DB2 via JDBC on Spring - IBM Developer](https://developer.ibm.com/technologies/java/tutorials/spring-with-db2-via-jdbc/)

とりあえず検索して出てきた上記のページを参考に、application.propertiesを書く。
（IBM Cloud>リソース・リスト>作成したDb2>サービス資格情報　からURLなどの情報が取得できる）

```java:application.properties
spring.datasource.url=jdbc:db2://**************:50000/BLUDB
spring.datasource.username=********
spring.datasource.password=********
```
## エラー1個目
```
***************************
APPLICATION FAILED TO START
***************************
Description:
Failed to bind properties under '' to com.zaxxer.hikari.HikariDataSource:
    Property: driverclassname
    Value: com.ibm.db2.jcc.DB2Driver
    Origin: "driverClassName" from property source "source"
    Reason: Failed to load driver class com.ibm.db2.jcc.DB2Driver in either of HikariConfig class loader or Thread context classloader
Action:
Update your application's configuration
```
[Stack Overflowのこの質問](https://stackoverflow.com/questions/50215936/failed-to-bind-properties-under-to-com-zaxxer-hikari-hikaridatasource-spring)を参考にして`build.gradle`を編集した。

対処

```java:build.gradle
dependencies {
    runtime('com.ibm.db2:jcc:11.5.0.0')
}
```

## エラー2個目
```
2020-00-00 15:21:58.916 ERROR 5080 --- [  restartedMain] com.zaxxer.hikari.pool.HikariPool        : HikariPool-1 - Exception during pool initialization.
com.ibm.db2.jcc.am.SqlSyntaxErrorException: DB2 SQL Error: SQLCODE=-104, SQLSTATE=42601, SQLERRMC=END-OF-STATEMENT;select 1;<table_expr>, DRIVER=4.26.14
```
上記は起動時に発生するエラー。内容的にはSQLのシンタックスエラーだが、起動時にSQLを使った覚えがなく、ずっと`import.sql`が悪いのか？起動時にテーブルを生成する設定が悪いのか？などと考えていた。
しかし結局、ここで作動していたのは接続テスト用に（おそらくデフォルトで）設定されていたSQLだった……。
いろいろ調べていくうちに気づいたが、エラー文にあった`SELECT 1`で検索を書けていたらもっと早く気づけたかもしれない。

```java:application.properties
spring.datasource.hikari.connection-test-query = SELECT 1
```
このSQL文がおかしいということで、Db2向けの適当なテスト用SQLがあるんじゃないか？と調べてみたけどヒットせず。
ならこのオプション外せばいいじゃんと思ったが、HikariCPでは代わりに`Connection.isValid()`を実行するようになり、結局エラーが出た。
なら存在する任意のテーブルに対して何か操作すればいいんじゃないということで、適当にtestテーブルにカラムを一つ作成した。

対処

```java:application.properties
spring.datasource.hikari.connection-test-query = SELECT col1 from test
```
## さらに数か所修正

+ `spring.jpa.database-platform`を`org.hibernate.dialect.h2Dialect`から`org.hibernate.dialect.DB2Dialect`にしたり…（最初H2で作っていたので）
+ Javaのソースコード中のSQL文からセミコロンを外したり…（おそらく上記との兼ね合い）
+ タイムアウトの時間を長くしたり…（ライトプランのせいか頻繁にタイムアウトする）
+ pom.xmlを書き換えたり…（下記）

```java:pom.xml
<dependency>
    <groupId>com.ibm.db2</groupId>
    <artifactId>jcc</artifactId>
    <version>11.5.0.0</version>
</dependency>
```

### アクセスできた！

本当にこれでいいのか？　という疑問はありつつも一応アクセスできたのでOKとする。
すごい…ちゃんとブラウザ上にデータが表示されている…。
と思ったのもつかの間、


## エラー3個目

```
2020-00-00 01:22:57.438  WARN 26912 --- [nio-8080-exec-1] o.h.engine.jdbc.spi.SqlExceptionHelper   : [jcc][t4][10217][10310][4.26.14] 接続の確立後は、接続の読み取り専用モードを実行できません。
読み取り専用の接続を実行するには、読み取り専用データ・ソースまたは接続プロパティーを設定してください。 ERRORCODE=4474, SQLSTATE=01000
```
`DriverManager.getConnection()`でアクセスしている分には出ないので、どうもJPARepositoryを介してアクセスしようとするとこのエラーになるらしい。
（混在しているのは自分で書いたコードと人の書いたコードが混ざっているせい……）
JPARepositoryを使わなければひとまず解決しそうだけど、普通に設定変えて使えるような気もする。
第二弾記事に期待。

# まとめ
+ `application.properties`か`build.gradle`あたりが怪しい。
+ エラー文をちゃんと読む

### 余談
Db2のダッシュボードにアクセスしようとすると延々とエラーを吐き続けるという不具合に当たったが、ダッシュボードのアドレスが分かれば、そちらにアクセスして、DBの認証で使っているusernameとpasswordを入力してアクセスすることができる。
リダイレクトするときに、IBMCloudのログイン情報からダッシュボードへの認証がうまくできなかったみたい。