【SAP BTP】CAPのAPI(Java)をPostgreSQLで使ってみる

# はじめに
[CAP(SAP Cloud Application Programming Model)](https://cap.cloud.sap/docs/)を利用してHANA DBと連携するODataAPIを作成しました。（Java）
HANA DBはTrial環境だと気軽に利用できますが、購入して利用する場合はお高いこともあります。
HANA DBほどのスペックが必要とされない場合、BTP環境のなかでほかの選択肢としてはPostgreSQLがあります。
ということでJavaで作ったCAP APIをPostgreSQLでも使えるようにする方法を試してみました。

# 前提
CAP Service SDK for Javaで作成したOData APIがあることを前提としています。
【参考】
[【SAP BTP】CAP Service SDK for JavaでOData APIを作ってみる(2)](https://qiita.com/yonekura-h/items/9ce04ddbb37dc9001bb4)


# PostgreSQLのインスタンスを作る
まずはTrial環境でPostgreSQLのインスタンスを立てます。
Service MarketplaceからPostgreSQLのサービスを選択して「Create」を押下します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/159467/baddb8b3-b085-92ea-47f6-79d339f64d47.png)

instance Nameに任意の名称を入力し、インスタンスを作成します。
今回は「demoPostgreSQL」としました。
それ以外の値は初期値のままです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/159467/bba34f10-4ea6-e2cc-7816-7f0ee43c8f1b.png)

サービスインスタンスが起動したらInstances and Subscriptionsから確認できます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/159467/a797a364-70f1-f045-fc89-68c958647737.png)


PostgreSQLインスタンスからBTPへデプロイ済みのJavaアプリケーション（capCustom_api）とのService Bindingを作成します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/159467/91f70783-b64a-a518-3a7c-523a942ceb26.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/159467/a9249309-a88c-2675-b085-5645d139679c.png)

Bindingが作成されるとDBのCredentialsが確認できます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/159467/43fafe3f-e7b9-159c-21d1-cb4ec4b3d7bd.png)


# PostgreSQLへアクセスする

PostgreSQLへアクセスする方法はよくわからないので
（VSCodeの拡張機能などからそのままアクセスできると思っていたらそうでもなかったみたいです。）
[公式ドキュメント](https://help.sap.com/docs/PostgreSQL/b3fe3621fa4a4ed28d7bbe3d6d88f036/69036a6c0b3b4377a1ae6a1edd084988.html?locale=en-US)の記載に基づいて進めていきます。

ローカルからコマンドラインで接続していきます。（記事の環境はwslのUbuntuです。）
cf cliで環境へログインし、まずはスペースがSSHアクセスを許容していることを確認します。
```
$ cf login
$ cf allow-space-ssh dev
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/159467/d3079153-01b3-3634-ac89-f3fd19f7d2e4.png)


デプロイ済みのJavaアプリケーション（capCustom_api）に対してSSH接続を有効にします。
（有効化したら一度Restartしたほうがいいみたいです。）
```
$ cf enable-ssh capCustom_api
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/159467/5db7f27d-7422-4f04-5dbb-88467d827dba.png)


Javaアプリケーション（capCustom_api）をSSH接続のトンネルにしてポートフォワードするみたいなので、空いているローカルのポート（63306）を使ってSSH接続をします。
（PostgreSQLのホスト、ポートはService Binding作ったときに確認できるCredentialsの内容から設定します。）
```
$ cf ssh -L 63306:<PostgreSQLのホスト>:<PostgreSQLのポート> capCustom_api
```
こんな感じでつながります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/159467/d147f206-40f1-81ed-171d-8b5e153238fe.png)


ポートフォワードできたので、localhost:63306にアクセスすればPostgreSQLにつながるはずなので、
VSCodeに拡張機能「PostgreSQL」を入れて接続してみます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/159467/f2275290-ce28-78ef-7fe2-347b0e117e65.png)

接続設定は以下の通りです。
| 項目 | 値 |
|:-:|:-:|
| DBのHOST  | localhost  |
| DBのUser  | ※Credentialsに記載  |
| パスワード  | ※Credentialsに記載  |
| DBのPort  | 63306(cf sshの指定値)  |

接続できるようになりました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/159467/5e9ac791-be3c-a7f0-2be7-fe9bac0b0450.png)

テーブルとテストデータを作成します。
```
CREATE TABLE demo_Users (
  ID INTEGER NOT NULL,
  FirstName VARCHAR(100),
  LastName VARCHAR(100),
  Age INTEGER,
  createdAt TIMESTAMP,
  createdBy VARCHAR(255),
  modifiedAt TIMESTAMP,
  modifiedBy VARCHAR(255),
  PRIMARY KEY(ID)
);

CREATE VIEW UserService_Users AS SELECT
  Users_0.createdAt,
  Users_0.createdBy,
  Users_0.modifiedAt,
  Users_0.modifiedBy,
  Users_0.ID,
  Users_0.FirstName,
  Users_0.LastName,
  Users_0.Age
FROM demo_Users AS Users_0;

INSERT INTO demo_Users VALUES(
	1/*ID <INTEGER>*/,
	'Postgre'/*FIRSTNAME <NVARCHAR(100)>*/,
	'太郎'/*LASTNAME <NVARCHAR(100)>*/,
	49/*AGE <INTEGER>*/,
	DEFAULT/*CREATEDAT <TIMESTAMP>*/,
	'PostgreSQL DB'/*CREATEDBY <NVARCHAR(255)>*/,
	DEFAULT/*MODIFIEDAT <TIMESTAMP>*/,
	'PostgreSQL DB'/*MODIFIEDBY <NVARCHAR(255)>*/
);
INSERT INTO demo_Users VALUES(
	2/*ID <INTEGER>*/,
	'Postgre'/*FIRSTNAME <NVARCHAR(100)>*/,
	'次郎'/*LASTNAME <NVARCHAR(100)>*/,
	120/*AGE <INTEGER>*/,
	DEFAULT/*CREATEDAT <TIMESTAMP>*/,
	'PostgreSQL DB'/*CREATEDBY <NVARCHAR(255)>*/,
	DEFAULT/*MODIFIEDAT <TIMESTAMP>*/,
	'PostgreSQL DB'/*MODIFIEDBY <NVARCHAR(255)>*/
);
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/159467/65f008e5-5ece-0eb6-43b8-c3e531f494ec.png)

上記でDB側の準備ができました。

# CAP APIの接続先DBを変更する
今のままではCAP APIをデプロイするたびにService Bindingを変える必要があるので、
MTAのmta.yamlを編集してアプリケーションの接続先を変更します。

resourcesに記載していたHANA DBの代わりにPostgreSQLサービスを追加して、Javaモジュールのrequiresに記載します。
```yaml:mta.yaml
_schema-version: "3.2"
ID: mtademo
version: 0.0.1
modules:

  # customserviceのJavaModule
  - name: capCustom_api
    type: java
    path: customservice
    parameters:
      buildpack: java_buildpack
    properties:
      SPRING_PROFILES_ACTIVE: cloud
    build-parameters:
      builder: custom
      commands: [ mvn clean package ]
      build-result: "srv/target/*-exec.[wj]ar"
    provides:
      - name: capCustom-api # required by consumers of CAP services (e.g. approuter)
        properties:
          srv-url: ${default-url}
    # resources -> usePostgresqlの定義を使う
    requires:
     - name: usePostgresql
       properties:
          JBP_CONFIG_RESOURCE_CONFIGURATION: '[tomcat/webapps/ROOT/META-INF/context.xml:
             {"service_name_for_DefaultDB" : "~{hdi-container-name}"}]'
resources:
  # 作成済みのHANA DBサービスをresourcesに記載
  # - name: useDemoShared
  #   parameters:
  #     service-name: demoShared
  #     service: hanatrial
  #     service-plan: securestore
  #     config:
  #       schema: sampleSchema
  #   properties:
  #     hdi-container-name: '${service-name}'
  #   type: org.cloudfoundry.existing-service
  # 作成済みのPostgreSQLサービスをresourcesに記載
  - name: usePostgresql
    parameters:
      service-name: demoPostgreSQL
      service: postgresql-db
      service-plan: trial
    properties:
      hdi-container-name: '${service-name}'
    type: org.cloudfoundry.existing-service  
```

# BTP環境へデプロイ

mta.yamlを修正したらBuild→Deployを実行します。
```
$ mbt build
$ cf deploy mta_archives/mtademo_0.0.1.mtar
```

Javaアプリケーションが起動したらUsersエンティティ（/odata/v4/UserService/Users）にアクセスしてみます。
PostgreSQLに登録したデータが参照できていることが確認できました！
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/159467/dee0f683-8e24-a5ce-5ea6-ab04f5520df5.png)

ついでにデータ登録のPOSTリクエストを試してみます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/159467/28d1b21e-eda0-2825-afdb-4b333ce17d53.png)

「KYOSO花子」がPostgreSQLに登録されていることが確認できました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/159467/83cb2fe6-1ba2-0f7d-e4d0-8322b77d7c4c.png)

> メモ
MTAをDeployしてService Bindingを作り直すとDBのユーザー/パスワードが変更していました。
再度Credentialsを確認する必要があります。

# おわりに

Javaで作ったCAP APIをPostgreSQLにつなぐことに成功しました。
システムによってはHANAではなくPostgreSQLを利用する場合もあると思うので、どちらでも利用できる方法があれば便利です。
ただし、CAPでPostgreSQLを使う場合には制約事項もあるのであらかじめ確認が必要と思いました。
[Database Support in Java](https://cap.cloud.sap/docs/java/development/#database-support)