spring bootプロジェウトでAPIを作成、postgreSQLを接続

#### 目次
- [spring boot関連](#springboot関連)
  - [プロジェクト視点で、各ファイルの役目](#プロジェクト視点で各ファイルの役目)
  - [`@valid`アノテーションを使って、request bodyの項目をチェック](#validアノテーションを使ってrequestbodyの項目をチェック)
  - [追加のプラグイン](#追加のプラグイン)
- [postgreSQL関連](#postgresql関連)
  - [heroku（API、postgreSQL）・salesforce連携の時、データベースに日時データ保存時の注意点](#herokuapipostgresqlsalesforce連携の時データベースに日時データ保存時の注意点)
-----

### spring boot関連
- swaggerはAPIの仕様を記録するファイル、それをサーバーで走ると、webページの様にAPIの種類、数、request　body構成、response構成なのが書かれています。コードをここに入れば、webページの様に表示される[online swagger編集道具](https://editor.swagger.io/)
- Spring FrameworkあJavaEE開発用の基礎フレーム、spring bootはそのフレームを快速生成用の道具、[ここのサイト](https://start.spring.io/)で欲しいプラグインを選び、フレームを生成
- javaEEフォームベースの開発は、まず普通objecctのインスタンス作成、でも毎回毎回操作の時objecctのインスタンス構築すると、インスタンスの作成と管理は複雑になり、そのためspringでIoC容器でインスタンスの作成と管理を行う ＝ 依頼注入（DI：Dependency Injection）。その為もし一つのobject Aの作成はもう一つのobject Bが必要の場合、object Aはobject Bを貰う方法は二つある、一つは
  ```java:set()メソッドを追加
  public class BookService {
    private DataSource dataSource;

    public void setDataSource(DataSource dataSource) {
        this.dataSource = dataSource;
    }
  }
  ```
  もう一つは
  ```java:直接インスタンスBを渡す
  public class BookService {
    private DataSource dataSource;

    public BookService(DataSource dataSource) {
        this.dataSource = dataSource;
    }
  }
  ```
  参考：[IoC容器原理](https://www.liaoxuefeng./wiki/1252599548343744/1282381977747489)
- springフォームの下、 依頼注入DIはbeamを通じで実現する、簡単にゆうと、beanで作られたclassを表す。そして`@Component`でbeanを宣言し、`@Autowired`でインスタンスを投入。のそ途中、Javaパッケージの層関係を注意
参考：[装配Bean](https://www.liaoxuefeng.com/wiki/1252599548343744/1282382145519649)　[使用Annotation配置](https://www.liaoxuefeng.com/wiki/1252599548343744/1282382596407330)
- beanの作成はAPPが起動される時一気に作成、APPシャットダウンの時一気に消す；或いは毎回objecctが呼ぶ時作る。もし毎回呼ぶ時に作成を希望の場合、`＠Scope`をつける必要ある。
参考：[定制Bean](https://www.liaoxuefeng.com/wiki/1252599548343744/1308043627200545)　[使用Resource](https://www.liaoxuefeng.com/wiki/1252599548343744/1282383017934882)
- springプロジェクトにはmain classがあり、このmain classでAPPを起動する、main classを指定したい場合、root/pom.xml中に`<spring.boot.mainClass>project ... path.herokuapi.SightsHerokuApiApplication</spring.boot.mainClass>`の様にapplication classの階層と名前を追加、そして、<configuration>に<mainClass>${spring.boot.mainClass}</mainClass>をつける
- spring bootがどの様なJava環境で走るのかを設置したい場合、root/system.propertiesを追加し、その中Java versionを指定、例えば：`java.runtime.version=11`
- 今のAPPのservice.java中で、さらに外部サービスにrequest出したい場合、
これを参考：[参考URL01](https://b1san-blog.com/post/spring/spring-rest-template/) [参考URL02](https://jeannie-blog.online/springboot-webapi-call/)
- Cross-Origin Resource Sharing問題
（ブラウザがAPP１から貰ったWebページをベースに、APP２へrequestを投げるを拒否する）
簡単に言と、ブラウザはA（クライアントサイド）サーバーからファイルや脚本をとって、そしてB（クライアントサイド）サーバーをアクセスすると、BサーバーのAPPにはAサーバーからの脚本のアクセスを許可しない場合、responseはエラーが出る。springフォームでは、[参考URL](https://qiita.com/m_kikuchi/items/df8c070b228df44a5462)の方法で単独APIとAPP全体に許可を追加できる。
Cross-Origin Resource Sharingに関する紹介：[参考URL1](https://developer.mozilla.org/zh-CN/docs/Web/Security/Same-origin_policy) [参考URL2](https://developer.mozilla.org/zh-CN/docs/Web/HTTP/CORS)
さらに、springプロジェクトを複数の環境（ローカル、開発環境クラウド、本番環境クラウド）で走りたい場合、[参考URL](https://qiita.com/someone7140/items/6aa740860781a62654f7)に合わせ、APPのmain　classを以下の様に修正：
  ```java:SampleApplication.java
  @SpringBootApplication
  public class SampleApplication {
  
  	@Autowired
  	private Environment env;
  
  	@Bean
  	public WebMvcConfigurer corsConfigurer() {
  		return new WebMvcConfigurer() {
  			@Override
  			public void addCorsMappings(CorsRegistry registry) {
  				String url = env.getProperty("cors.allowed.url");
  				registry.addMapping("/**")
  						.allowedMethods("GET", "POST", "PUT", "DELETE")
  						.allowedOriginPatterns(url)
  						.allowCredentials(true);
  			}
  		};
  	}
  
  	public static void main(String[] args) {
  		SpringApplication.run(SampleApplication.class, args);
  	}
  
  }
  ```
  そして許可したいallowed.urlをapplication-xxx.propertiesに配置：
  ```:aapplication-xxx.propertiesに配置
  # CORS情報
  cors.allowed.url=${CORS_ALLOWED_URL}  //他すべてのサイトからのアクセスを許可：cors.allowed.url=*
  ```
  そして対応の各環境で、allowed.url（${ALLOWED_URL}）にそれぞれのURL値を付与する、herokuの例：
  ![image (6).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2747384/91de9fcb-b941-f57e-e67a-ef2234d3b130.png)


#### プロジェクト視点で、各ファイルの役目
- /.vscode はvs code各設定を保存、すべてのvscodeプロジェクトは単独設定
- /misc/postman import用のpostman API設定ドキュメント、swaggerで表示されるものと似ている
- /src/main/java/project ... path/herokuapi/domain webAPPで使う各java　object、コンパイルされたclassファイルは`/target`に保存され、もしspring起動時調子がおかしい場合、この`/target`中のclassフォルダを消し、もう一回起動する。
- /src/main/java/project ... path/herokuapi/web はhttp通信用API定義とAPP起動用のApplication.javaを保存
  - Application.javaは起動用object、普通一つのweb　APPには一個のmain関数、pathは以下：src/main/java/project ... path/herokuapi/ApiApplication.java
- resourceフォルダは、WebページやDB制御ファイルを保存。DB操作ファイル例：/src/main/resources/project ... path/herokuapi/domain/application/ApplicationRepository.xml
  - “.properties”ファイルはデータベースアクセス用のパラメータを保存。例えばDB　URL、username、password。接続したいDBの設定と噛み合わないと、DBに接続出来ない、大体５００エラーが出る。
- 上記のhttp通信用機能、App内部ロジックや機能定義、ソース制御機能の関係を表す図：
  ![スクリーンショット 2022-08-22 18.54.34.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2747384/e9348da4-67d0-d1c1-4557-27f6efd1e42b.png)
- 各ファイルの機能と間の関係
  ![スクリーンショット 2022-08-24 15.20.57.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2747384/6961a0da-91fb-5350-5c49-e8605477a52f.png)
  デモ用作った四つのオブジェクト、Javaファイル、get・post用のAPIは以下図の様に。そのうち
  - controllerファイルはAPのURL`例：GET http://localhost:8080/cdm/applications`を定義
  - serviceファイルは具体な機能を定義
  - Javaのrepositoryファイルはinterfaceを定義、xmlのrepositoryファイルは実際のデータベース操作を定義（MyBatis使用）
  - formファイルはpost内容がオブジェクト項目と一致の時、コードを簡易化する為の物

#### @validアノテーションを使って、request bodyの項目をチェック
- まずはXXXcontroller.javaで、APIが受けるrequest body Entityに`@valid`をつける、そしてEntity内の各項目に対し、具体的なバリデーションコードをつける。もしrequest bodyのEntity中に更にsub Entityが含む場合、そのsab Entity項目にも`@valid`をつける。
具体例：
controllerにとって、RequestInfoFormに`@valid`をつけ、バリデーションチェエクが必要を声明する
```java:OpportunityController.java
@RequestMapping("/cdm/applications")
public class OpportunityController {

    private final OpportunityService opportunityService;

    @PostMapping
    public ResponseEntity<ResponseInfoForm> create(@RequestBody @Valid RequestInfoForm requestInfoForm) {
        return new ResponseEntity<ResponseInfoForm>(opportunityService.create(requestInfoForm), HttpStatus.OK);
    }
```
そして、RequestInfoFormの中にApplicationRequestInfoが含まれる為、RequestInfoForm.Entity内部のApplicationRequestInfo項目に`@valid`をつけ。
```java:RequestInfoForm.java
@Data
// 申込フォームの入力情報
public class RequestInfoForm {
    @Valid
    @NotNull(message = "application_request_infoがNullではなら無い")
    private ApplicationRequestInfo application_request_info;
```
最後ApplicationRequestInfo.Entityの各項目に具体的なバリデーション設定を追加
```java:ApplicationRequestInfo.java
@Data
public class ApplicationRequestInfo {
    @NotNull(message = "カメラ台数を1〜10まで入力して下さい")
    @Min(value = 1, message = "camera_numの最小値は1")
    @Max(value = 10, message = "camera_numの最大値は10")
    private int camera_num;
```
[参考URL1](https://cloud.tencent.com/developer/article/1776567)　[参考URL2](https://blog.csdn.net/minkeyto/article/details/105502423)　[参考URL3](https://qiita.com/MrMs/items/19ea03b069c226c323bc)

- request bodyにはListが含まれる場合、以下を参考：[参考URL](https://www.oodlestechnologies.com/blogs/spring-request-body-validation-for-list/)

#### 追加のプラグイン
- flywaydb データベース更新や修正をバーションに分けて記録、spring起動の時自動実行[参考资料](https://blog.csdn.net/yangfei001/article/details/51045189)
  flywaydbコントロールファイルはここに保存：src/main/resources/db/migration
- APIテスト用toolpostman [参考资料](https://www.damon-liu.cn/103.html)
  作ったjson形式request bodyをpostmanにimport出来，例文は普通以下に入れる：misc/postman，
- MyBatis、postgreDBコントロールをjavaのメソッドコードで書く；tableのprimary keyを自動追加形式に設定、そして操作を定義するxmlファイル内でinsert、update操作にuseGeneratedKeys、keyProperty、keyColumn属性をつけ。[参考URL](https://blog.csdn.net/C_AJing/article/details/108357096)
  - useGeneratedKeys:primary key自動生成するかどうか，黙認false。
  - keyColumn:primary keyを保存する列の名前、黙認は第一列、もし特定の列に設置したい場合、手動設定必要。
  - keyProperty:primary key自動生成後、java　Entityインスタンスに返す時、どの項目を指定。
- `例文：<insert id="insert" useGeneratedKeys="true" keyColumn="id" keyProperty="id">`

### postgreSQL関連
- postgreのインストールについて　[MAC上のインスドール](https://www.runoob.com/postgresql/mac-install-postgresql.html)
- postgre接続vs code　[VSCodeからPostgreSQLへ接続してSQL発行まで](https://obenkyolab.com/?p=1280)
- 管理用APP【PostgreSQL Management Tool】、インストール上pgAdmin4のAPP logoが現れ、GUIを使って、postgresqlデータベースに接続や設置ができる。postgreは専用のcommand line　toolがあり、MAC上“command+space”でキーワード”psql”を検索、そして専用terminalが現れ、postgresql操作するにはpaql言語で行う。
- heroku上postgreSQLを使って、salesforce DBと連携を取りたい場合、この記事を参考：[参考URL](https://devcenter.heroku.com/ja/articles/handling-object-relationships-in-heroku-connect#simple-relationships-between-two-objects-and-relationship-external-ids)
- 最初にpostgreインストール時、初期ユーザ“postgres”を作る、passwordは自分設定、毎回runpsql.shを開ける時、大体初期アカウト“postgres”を使って接続、これは管理者アカウント、host名は”localhost“、port番号は“5432”。登録後、command　lineは“postgres=#”で始まる
コマンド例：
`create user yyds with password 'yyds';`新ユーザを作成；
`create database yyds owner yyds;`ローカル環境でsub DBを作成、そしてこのsub DBを一つのユーザにあげる；
spring bootと連携の時、データベースと接続用ユーザの情報はapplication-XXX.propertiesで設置；複数環境で同じspring走る時、環境に合わせて、複数の”.properties”を用意できる。
- vscode使う場合、launch.jsonでローカル環境で使いたいapplication.propertiesを指定、そしてDBを接続。
例えば、ローカルパラメータは`application-local.properties`で保存、ならlaunch.json中に`--spring.profiles.active=local"`を追加。
- updateとinsert方法以外、DB更新の時upsertも使用できる、一つまたは複数の参照項目を指定；参照項目が既存と同じ場合、他の項目データを更新；参照項目が新しい場合、新データを全部入れる。ただし、参照項目がuniqueの必要がある。
例文`alter table table_name add constraint unique_columXXX unique(columXXX)`（unique_columXXX是随便设置的约束名称）。初始化table的时候可以直接在列名后面加上UNIQUE来声明，例文`application_number VARCHAR(20) NOT NULL UNIQUE,`
[参考URL1](https://qiita.com/velphedia/items/bddb996497900f0bce38)，[参考URL2](https://blog.csdn.net/nmgcfyxl/article/details/105773010)，[参考URL3](https://developer.aliyun.com/article/74419)

#### heroku（API、postgreSQL）・salesforce連携の時、データベースに日時データ保存時の注意点
- sql DBの中普通日時の保存は`timestamp`を使う、with timezoneとwithout timezone二つの形式あって。一番いいのは、APIで日時データ受ける時、それをUTC標準時間を変え、without timezoneで保存。そしてDBから取得時、ユーザのtimezoneに合わせて、ユーザのローカル時間に変え、渡す。この機能の実現はユーザオブジェクトに、ユーザのローカルtimezoneを保存する必要がある。 [timezone命名参考１](https://blog.csdn.net/zhou16333/article/details/104046519)  [timezone命名参考２](https://www.hulft.com/help/ja-jp/WebFT-V3/COM-ADM/Content/WEBFT_ADM_COM/TimeZone/timezonelist.htm)

```java:ユーザから”分”までのstringデータを貰い、LocalDateTimeに変え、timezone設置
String dateTime_1 = "2022-09-14 11:20";
DateTimeFormatter fmt = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
this.japanTime = LocalDateTime.parse(dateTime_1 + ":00", fmt);

System.out.println("japanTime is : " + japanTime);  // 出力2022-09-14T11:20
System.out.println("japanTime is : " + fmt.format(japanTime));  // 出力2022-09-14 11:20:00
        
ZoneId japanZone = ZoneId.of("Asia/Tokyo");
ZoneId utcZone = ZoneId.of("UTC");  //UTC標準時刻
LocalDateTime utcTime = japanTime.atZone(japanZone).withZoneSameInstant(utcZone).toLocalDateTime(); //UTC標準時刻に変える
        
System.out.println("greenwich is : " + utcTime);  //出力2022-09-14T02:20
System.out.println("greenwich is : " + fmt.format(utcTime)); //出力2022-09-14 02:20:00
        
LocalDateTime localTime = utcTime.atZone(utcZone).withZoneSameInstant(japanZone).toLocalDateTime(); //日本時間に変える
```