LoginSignup

This article is a Private article. Only a writer and users who know the URL can access it.
Please change open range to public in publish setting if you want to share this article with other users.

More than 3 years have passed since last update.

4/17 テキスト

Last updated at Posted at 2021-04-04

Java教育応用編

Bean Validator

はじめに

Springで簡単なWeb画面を提供するアプリケーションを作成する場合を例として説明する。

image.png

クライアントから送信されるHTTPリクエストは、HTTPヘッダとHTTPボディ部に分かれる。
例えばWebシステムでフォームに入力したデータ、システム間で通信する際にデータをやり取りするJSONなど、おもなデータはHTTPボディで送信される。

クライアントからHTTPのリクエストを受け取った場合、Springなどのフレームワークでは、HTTPボディに設定されたデータを、JavaのObjectへ変換する機構を備えている。

image.png

オレンジがJavaのコードで作成する部分となる。
リクエストをJavaのオブジェクトに設定した状態でリクエストを受け取ったアプリケーションのControllerクラスは、受け取った値により処理(ロジック)を行い、レスポンスを設定する。

ロジックで適切に処理を行うためには、入力値(Validation)の検査を行う。

  • 必須値が空になっていないか
  • DBに登録する値の桁数があふれていないか
  • アプリケーションの動作に悪影響を及ぼす値が設定されていないか

など、処理継続に不備がある入力がないか、検証する。

このValidation処理を実装する方法はどうするか?
自分でif文やCase分を使って全部実装するのはあまりに効率が悪いため、通常の業務ではValidationをサポートするライブラリを使用する。
今回説明するBeanValidatorもその一種。

Bean Validator とは

正確には、JSR 303 - Bean Validation 、アノテーションを使いオブジェクトのバリデーションを設定する規格のことを指す。

ExampleBean.java
  @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が動作するセットがあるのでこれを今回は利用する。

image.png

「vali」くらいまで入れると候補が出てくる。
gradleを選択して「Finish」

image.png

チュートリアルは教材の
XXX-initial
と完成版の
XXX-complete
がある。今回はcompleteを使う。自分で作ってみる場合はinitialを使うとよい。

powershellやcmdから、プロジェクトルートのディレクトリに行く。

gradlew bootRunするとビルドされてサーバが立ち上がる。

image.png

画面をたたくとフォームがでてくる。

image.png

順を追って解説する。

画面描画

/gs-validating-form-input-complete/src/main/resources/templates/form.html
<!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のコードを見る。

/gs-validating-form-input-complete/src/main/java/com/example/validatingforminput/WebController.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処理の具体的な設定は、受け取るオブジェクトに記載する。

/gs-validating-form-input-complete/src/main/java/com/example/validatingforminput/PersonForm.java
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ルールに記載されているルール外のパラメータを入れると・・・
image.png

エラーが表示される。
特に設定しない場合、設定した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://qiita.com/rubytomato@github/items/cf997f710b6b744fd850#概要

その3:複合インデックスは融通が利かない

カラムの順番があってないと利かないとか。
例:入力非必須のフォームの複合検索など・・・
https://use-the-index-luke.com/ja/sql/where-clause/the-equals-operator/concatenated-keys

0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up