#Java教育応用編
##Bean Validator
はじめに
Springで簡単なWeb画面を提供するアプリケーションを作成する場合を例として説明する。
クライアントから送信されるHTTPリクエストは、HTTPヘッダとHTTPボディ部に分かれる。
例えばWebシステムでフォームに入力したデータ、システム間で通信する際にデータをやり取りするJSONなど、おもなデータはHTTPボディで送信される。
クライアントからHTTPのリクエストを受け取った場合、Springなどのフレームワークでは、HTTPボディに設定されたデータを、JavaのObjectへ変換する機構を備えている。
オレンジがJavaのコードで作成する部分となる。
リクエストをJavaのオブジェクトに設定した状態でリクエストを受け取ったアプリケーションのControllerクラスは、受け取った値により処理(ロジック)を行い、レスポンスを設定する。
ロジックで適切に処理を行うためには、入力値(Validation)の検査を行う。
- 必須値が空になっていないか
- DBに登録する値の桁数があふれていないか
- アプリケーションの動作に悪影響を及ぼす値が設定されていないか
など、処理継続に不備がある入力がないか、検証する。
このValidation処理を実装する方法はどうするか?
自分でif文やCase分を使って全部実装するのはあまりに効率が悪いため、通常の業務ではValidationをサポートするライブラリを使用する。
今回説明するBeanValidatorもその一種。
Bean Validator とは
正確には、JSR 303 - Bean Validation 、アノテーションを使いオブジェクトのバリデーションを設定する規格のことを指す。
@NotNull
@Size(min = 1, max = 20)
private String name;
規格として定められているということは、上記のようなコードを書くとvalidationが動作する!
・・・わけではなく、実際には規格に沿ってチェック処理を実装したライブラリがないと使用できない。
BeanValidatorはリファレンスの実装としてHibernate Validatorというライブラリがあるため、これをSpringと組み合わせて使うことが多い。
https://hibernate.org/validator/
Springチュートリアル解説
Springチュートリアルを使って詳細な説明を行う
IDE:STS4.10.0
JDK:amazon-corretto-11.0.10.9.1-windows-x64
前準備
※お手元でも再現しやすいようにWindows上で実施した経過を残す。
springのtutorialに、validationが動作するセットがあるのでこれを今回は利用する。
「vali」くらいまで入れると候補が出てくる。
gradleを選択して「Finish」
チュートリアルは教材の
XXX-initial
と完成版の
XXX-complete
がある。今回はcompleteを使う。自分で作ってみる場合はinitialを使うとよい。
powershellやcmdから、プロジェクトルートのディレクトリに行く。
gradlew bootRunするとビルドされてサーバが立ち上がる。
画面をたたくとフォームがでてくる。
順を追って解説する。
画面描画
<!DOCTYPE HTML>
<html xmlns:th="http://www.thymeleaf.org">
<body>
<form action="#" th:action="@{/}" th:object="${personForm}" method="post">
<table>
<tr>
<td>Name:</td>
<td><input type="text" th:field="*{name}" /></td>
<td th:if="${#fields.hasErrors('name')}" th:errors="*{name}">Name Error</td>
</tr>
<tr>
<td>Age:</td>
<td><input type="text" th:field="*{age}" /></td>
<td th:if="${#fields.hasErrors('age')}" th:errors="*{age}">Age Error</td>
</tr>
<tr>
<td><button type="submit">Submit</button></td>
</tr>
</table>
</form>
</body>
</html>
チュートリアルでは、Thymeleafを使用してHTMLにJavaの値を設定している。
Thymeleafとは? については別途機会を設けたい。
https://www.thymeleaf.org/doc/tutorials/3.0/usingthymeleaf_ja.html
以下、詳細に動作が知りたい場合の資料。
https://macchinetta.github.io/server-guideline-thymeleaf/current/ja/ArchitectureInDetail/WebApplicationDetail/Thymeleaf.html
上記では「th」で始まる箇所がそれにあたる。
フィールド名age,nameがあるはず、JAVAのコードを見る。
package com.example.validatingforminput;
import javax.validation.Valid;
import org.springframework.stereotype.Controller;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.servlet.config.annotation.ViewControllerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Controller
public class WebController implements WebMvcConfigurer {
@Override
public void addViewControllers(ViewControllerRegistry registry) {
registry.addViewController("/results").setViewName("results");
}
@GetMapping("/")
public String showForm(PersonForm personForm) {
return "form";
}
@PostMapping("/")
public String checkPersonInfo(@Valid PersonForm personForm, BindingResult bindingResult) {
if (bindingResult.hasErrors()) {
return "form";
}
return "redirect:/results";
}
}
WebControllerで、HTTPのリクエストを受け取っている。
@Mappingアノテーションで、HTTPメソッドはGETか? POSTか? URLパスは? などを指定している。
@Validアノテーションで、続くObjectのValidationを行うことを設定している。
PersonFormオブジェクトが先ほど説明した、リクエストにマッピングさせるオブジェクト。
BindingResultは、Validationでエラーが発生した場合にエラーを入れるオブジェクト。
Validation処理の具体的な設定は、受け取るオブジェクトに記載する。
package com.example.validatingforminput;
import javax.validation.constraints.Min;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;
public class PersonForm {
@NotNull
@Size(min=2, max=30)
private String name;
@NotNull
@Min(18)
private Integer age;
public String getName() {
return this.name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public String toString() {
return "Person(Name: " + this.name + ", Age: " + this.age + ")";
}
}
@NotNull、@Sizeなどが、各フィールドに設置するValidationのルールになる。
先ほど説明した、Validationライブラリの種類、またimportしているframeworkにより、微妙に使えるアノテーションが違うため都度確認したほうが良い。
例:Spring Frameworkで使用できるBean Validationアノテーション一覧
https://penguinlabo.hatenablog.com/entry/springframework/validator
実際に、Validationルールに記載されているルール外のパラメータを入れると・・・
エラーが表示される。
特に設定しない場合、設定したNULLチェックにひっかかるため、Validator標準のエラー文言が表示される。
この際表示されるエラー文言を独自に設定したい場合は、同様にアノテーションで設定することもできる。
##Map
###概要の参考
自分で書こうとしたがわかりやすいサイトがもうあったので紹介
https://java-code.jp/232
###HashMapによる違い
種類によって違う。(順番を保持したりしなかったり。)
おおまかには、機能が増えるほど処理に負荷がかかる。なんでもかんでも高機能な「LinkedHashMap」で、とはいかないので注意。
https://www.infoscoop.org/blogjp/2014/06/26/hashmap-treemap-linkedhashmap/
##Stream
###概要の参考
同上
MAPなども含めたコレクションに対して操作を実行するAPI。
https://java-code.jp/category/stream_api
###処理は工場のラインをイメージする
Java SE 8のストリームを使用したデータ処理、パート1
https://qiita.com/KentOhwada_AlibabaCloudJapan/items/24413024602843820a26
###記載方法にラムダでの省略が使われる
どこまで省略するかが記載した人の匙加減次第なので混乱のもと
https://qiita.com/munieru_jp/items/6c0dbada463e00429fd1
この辺をみつつ、自分が扱えるレベルで使い始めるとよい。
###処理には様々な種類がある
中間処理、終端処理など処理できる場所に違いがある。
http://www.ne.jp/asahi/hishidama/home/tech/java/stream.html
##自動テスト
キーワードは「CI/CD」
https://codezine.jp/article/detail/11083
#第二部
##「シングルトン」にまつわる話
Javaのシングルトンとして検索すると、デザインパターンとして説明されることが多い
https://qiita.com/shoheiyokoyama/items/c16fd547a77773c0ccc1
より実務的な話をする。
例として、Springフレームワークにおいて、@Serviceなどのアノテーションを付与してクラスを記載する場合。
この際、「スコープ」を設定する。
設定をミスると事故る。
https://docs.spring.io/spring-framework/docs/current/reference/html/core.html#beans-factory-scopes
以下例
- singleton:DIコンテナの起動時にBeanの新スタンスを生成しSingletonとして扱う。デフォルト値
- prototype:Beanの取得時に毎回インスタンスを生成。スレットアンセーフなBeanの場合に用いる
- session:HTTPのセッション単位でBeanインスタンスを生成
- request:HTTPのリクエスト単位でBeanインスタンスを生成
例えば(ふつうはほぼやらないが)@Controllerのグローバルなフィールドに変数を置いて、書き換えるとする。
この場合、@Controllerはデフォルトでシングルトンである。サーバが起動して、サーバプロセス上にJavaが起動しているうちはインスタンスが1つになる。
単独のインスタンス上で値を共有し、相互に書き換えると、異なるユーザで同じ変数を取り合う形になる。
上記のようなことが起こらないように、シングルトンなスコープのオブジェクトに、ユーザ固有の値などが混入しないように注意が必要。
逆に、サーバ全体で共有してよい情報。
例えば
- DB接続先のアドレス、ポート
- 環境設定情報
などはシングルトンにすることによって、余計なインスタンスが生成されず、サーバのリソースの節約になる。
##SQLのIndexについて
最初に
「つければいい、というものではない」
###その1:INDEXとは & メリットとデメリット
つけると検索は確かに早くなる、しかしInsertやUpdateが遅くなっていく。
https://www.dbonline.jp/sqlite/index/index1.html
###その2:カーディナリティの低いカラムには効果が薄い(ことが多い)
####カーディナリティとは
https://www.shift-the-oracle.com/words/cardinality.html
####(ことが多い)とした理由
特定条件下では効果がある場合もある
https://qiita.com/rubytomato@github/items/cf997f710b6b744fd850#概要
###その3:複合インデックスは融通が利かない
カラムの順番があってないと利かないとか。
例:入力非必須のフォームの複合検索など・・・
https://use-the-index-luke.com/ja/sql/where-clause/the-equals-operator/concatenated-keys