DaisukeのITメモ

DaisukeのITメモ

一人前になる為に。

【WIP】【学習メモ】DBスペシャリスト

目次

概要

DBスペシャリストに向けた学習メモ。

メモ

用語

  • 選択:selectで行を絞り込む事。
  • 射影:selectで列を絞り込む事。
  • 「列」=「項目」=「カラム」=「属性」=「フィールド」
  • 「行」=「レコード」=「タプル」
  • 3層データモデル
    • データモデルを3層に分けるのは、データ独立を実現し、変更に強いシステムにする為。
      • 概念データモデル
        • データの内容・概念を纏めた抽象的なモデル。
        • システム化対象に対する概念設計によって出来上がる。
        • 概念設計の方法には、トップダウンアプローチボトムアプアプローチの2種類が有る。
      • 論理データモデル
        • DBで実際に実装可能なモデル。
        • 概念データモデルに対する論理設計によって出来上がる。
        • DBと、ユーザやAPとを結び付ける為のモノ。
        • 概念データモデルを元に論理データモデルを作成する事で、論理データ独立が成し遂げられる。
          • APを改修した際、論理データモデルを改修するたけで良いので、概念データモデルには影響を及ぼさない。
          • 逆に、DB構造等を改修した際、概念データモデルを改修するだけで良いので、論理データモデルには影響を及ぼさない。
      • 物理データモデル
        • データをDBMSで物理艇に配置するモデル。
        • 概念データモデルに対する物理設計によって出来上がる。
        • DBと、DBMSやハードウェアとを結び付ける為のモノ。
        • 概念データモデルを元に物理データモデルを作成する事で、物理データ独立が成し遂げられる。
          • DBMSやハードウェアを改修した際、物理データモデルを改修するたけで良いので、概念データモデルには影響を及ぼさない。
          • 逆に、DB構造等を改修した際、概念データモデルを改修するだけで良いので、物理データモデルには影響を及ぼさない。
  • 3層スキーマアーキテクチャ
    • ANSIアメリカ規格協会)で標準化されたモノ。
      • 外部スキーマ
        • APが使用するDBの記述。
          • 代表例:ビュー。
      • 概念スキーマ
        • エンティティやテーブル、テーブル間の関連などの記述。内部スキーマと外部スキーマの間に位置する。
      • 内部スキーマ
        • DBMSで使用する、DBを物理的にどのように配置するのか等、具体的に実装する為の記述。
          • 代表例:インデックス。
  • 基幹システム
    • 業務上必須なシステム。在庫管理・物流管理・販売管理システム等。
  • データレイク
    • 収集した加工前のデータを保管しておく場所。
  • データクレンジング
    • データレイクに貯めたデータに対し、データ重複・誤記・表記揺れなどを探し出し、削除・修正・正規化する事。
  • データウェアハウス
    • データクレンジング後のデータを保管しておく場所。
  • データマート
    • データウェアハウスから特定の利用者・用途向けに加工して取り出したデータ。
  • 更新時異常
    • 正規化していないテーブルを更新(Insert・update・delete)した際に起こる、下記3種類の異常。
      1. タプル挿入時異常
        • あるデータ(カラム)を新規作成しようとした際、他のデータ(カラム)も決まらないと挿入できない等。
      2. タプル更新(修正)時異常
        • あるデータ(カラム)を更新しようとした際、そのデータが複数箇所に存在し、一部レコードのみしか更新されない事で、整合性が破綻してしまう等。
      3. タプル削除時異常
        • あるデータ(カラム)を削除しようとした際、他のデータ(カラム)の存在も一緒に消えて失くなってしまう等。
  • 関数従属性
    • カラムXの値が決まればカラムYの値が一意に定まる場合、「YはXに関数従属している」と言い、「X→Y」と表現する。
    • Xを決定項、Yを従属項と言う。
  • 導出属性
    • 単価と数量が分かれば、乗算する事で金額が分かるように、他のカラムから演算する事で導出可能な事。
  • 情報無損失分解
    • 正規化によって分解した表どうしを自然結合したら元の状態に戻せるように分解する事。正規化によって分解した際に元の一部の情報が欠損してしまわない事。
  • 外部キー制約
    • 「参照制約」とも言う。
    • テーブルBの主キーが、テーブルAの外部キーの時、下記2つの制約が発生する。
      1. テーブルAの外部キーには、テーブルBに存在する値しか入れられない。(テーブルBの主キーに存在しない新たな値をテーブルAの外部キーには入れられない)
      2. テーブルAの外部キーに存在する値は、テーブルBからは削除できない。

select

distinct(ディスティンクト)

  • 重複レコードの排除。
  • select distinct x from TableName ~で、select結果に重複が存在する場合は重複排除して結果表示される。(表示行数が減る)
  • select distinct x, y from TableName~の場合、xとyを組み合わせた表示結果が完全重複している場合に重複排除される。つまり、xとyの片方だけ重複している行同士は排除されない。

coalesce(コアレス)

  • 任意のカラムがnullのレコードに対して、代わりの値を表示する。
  • select coalesce(x, y) from TableName ~で、カラムxの値がnullの行は、nullの代わりにyを表示する。
  • 例:select coalesce(型番, '無し') from 商品;の場合、型番が存在するレコードはそのまま表示、型番がnullのレコードは「無し」が表示される。

case when then else end

  • 任意のカラムに対して、任意の条件の場合に任意の値を表示する。
  • 例:select case when 単価 <= 300 then '割引対象' else '通常価格' end from 商品;で、単価が300円以下の商品のレコードは「割引対象」を、301円以上の商品のレコードには「通常価格」を、表示する。
  • 例:select case when 型番 is null then '無し' else 型番 end from 商品;は、select coalesce(型番, '無し') from 商品;と同義。

where

null

  • 比較演算子に利用できない。
  • 該当カラムがnullか評価する場合は「x = null」ではなく「x is null」を使う。

in

  • where x IN (a, b, c)で、where x=a or x=b or x=cの意味。

between

  • where x BETWEEN y and zで、where x>=y and x<=zの意味。

all

  • where x > ALL (2, 3, 10)で、where x > 10の意味。

some・any

  • where x > SOME (2, 3, 10)where x > ANY (2, 3, 10)で、where x > 2の意味。

exists

  • select結果が存在しているかどうかのみを判定する。
  • 例:select 商品名 from 商品 where EXISTS (select * from 売上明細);
    • 「存在するかどうか?」しか判定しないため、副問合せの射影部分は何でも良いため、*になっている事が多い。
  • 判定を逆転させたい場合は、NOT EXISTSとすれば良い。

order by

  • 並べ替え。
  • order by ○○ ASCで昇順、order by ○○ DESCで降順。(デフォルトはASC。)
  • order by ○○ ASC, △△ DESCのように、複数カラム指定することも可能。(より左に書いたカラムを優先で並び替えが実施される。)

group by

  • 表示レコードを任意のカラムの値でグルーピングする。
  • 例:select 曜日 from 予定表 group by 曜日;で、「月」「火」・・・「日」という7レコードだけが表示される。
  • group byで指定したカラムしか、select表示できない。つまり、上記例でselect 曜日, 予定名 from 〜とはできない。group byでグルーピングすると、各グループ内の個別データにはアクセスできないため。
    • 冷静に考えると、そもそも、曜日が「月」で予定名がバラバラのレコードが複数存在する状況で、「月」1レコードに「予定名」にどのレコードの値を表示すべきか選べない。

集約関数

group by句でデータをグループ化した後、または、データ全体に対して処理を実行する関数。

count()

  • レコード数をカウントする。
  • select 商品種別コード, count(*) from 商品 group by 商品種別コード;の場合、商品種別コード毎にグルーピングした後、各グループ内のレコード数をカウントする。
  • 一方で、count()関数の引数に任意のカラム名を指定すると、そのカラムがnullではないレコードの件数をカウントする。
    • 例えば、select 商品種別コード, count(型番) from 商品 group by 商品種別コード;の場合、商品種別コード毎にグルーピングした後、各グループ内の型番カラムがnullではないレコード数をカウントする。
  • また、count()関数の引数の任意カラム名の前にdistinctを指定すると、そのカラムがnullではない、かつ、重複排除したレコードの件数をカウントする。
    • 例えば、select 商品種別コード, count(distinct 最終入荷日) from 商品 group by 商品種別コード;の場合、商品種別コード毎にグルーピングした後、各グループ内の商品種別コードカラムがnullではない、かつ、重複排除したレコード数をカウントする。

having

  • group by句でデータをグループ化した後に条件を指定して絞り込む。
  • 例:select 商品コード, sum(数量) from 売上明細 group by 商品コード having 商品コード <= 10;で、group byで商品コードをグループ化した後に、10以下の商品コードのみに絞り込んでいる。

関係代数

  • 複数のテーブルを使った演算を行う。直積・和・差・積・商。

    和(UNION)

  • union句を使用する。
  • ベン図で言うorの事で、union句前のselect結果のレコードにプラスで、union句後のselect結果のレコードも表示する。
    • そのため、union句前後のselect文で指定するカラム数やデータ型が一致している必要が有る。(この特性を和両立と言う。)
  • 重複は排除される。(表示する全てのカラムが同一の場合に「重複」と判断されるため、1カラムでも非同一の場合は排除されない。)
    • 重複を排除したくない場合は、union all
  • 例:select 最終入荷日 from 商品 where 商品種別コード = 1 UNION select 最終入荷日 from 商品 where 商品種別コード = 2;

差(EXCEPT)

  • except句を使用する。
  • except句前のselect結果から、except句後のselect結果を差し引く。(and部分は非表示)
    • そのため、except句前後のselect文で指定するカラム数やデータ型が一致している必要が有る。(この特性を和両立と言う。)
  • 重複は排除される。(表示する全てのカラムが同一の場合に「重複」と判断されるため、1カラムでも非同一の場合は排除されない。)
    • 重複を排除したくない場合は、except all
  • 例:select 商品コード from 商品 EXCEPT select 商品コード from 商品 where 商品種別コード = 2;

積(共通集合)(INTERSECT)

  • intersect句を使用する。
  • ベン図で言うandの事で、intersect句前後のselect結果の重複部分を表示する。
    • そのため、intersect句前後のselect文で指定するカラム数やデータ型が一致している必要が有る。(この特性を和両立と言う。)
  • 例:select 商品コード from 商品 INTERSECT select 商品コード from 商品 where 商品種別コード = 2;

直積(CROSS JOIN)

  • 和・差・積が、2テーブルのレコードを連結していた(テーブルの縦幅だけが伸びる)のに対し、直積は、2テーブルのカラムを連結する(テーブルの縦幅に加えて横幅も長くなる)。
    • そのため、2テーブルは和両立な関係でなくて良い。
  • 例:カラムが「商品コード」「商品名」「単価」の「商品」テーブル(3レコード)と、カラムが「店舗コード」「店舗名」「店長コード」の「店舗」テーブル(2レコード)の直積を実行すると、カラム数が6、レコード数が6(3*2)のテーブルが出来上がる。
  • SQLの記載方法は下記2種類。
    1. select * from 商品, 店舗;のように、select対象テーブルを,区切りで記載する方法。
    2. select * from 商品 CROSS JOIN 店舗;のように、CROSS JOIN句で記載する方法。

  • 直積の状態から、直積されてない状態に戻す事。
  • 商を行うSQLは存在しない。
  • 例:カラム数が6(「商品コード」「商品名」「単価」「店舗コード」「店舗名」「店長コード」)、レコード数が6(3*2)のテーブルの商を実行すると、カラムが「商品コード」「商品名」「単価」の「商品」テーブル(3レコード)と、カラムが「店舗コード」「店舗名」「店長コード」の「店舗」テーブル(2レコード)が出来上がる。
  • R / S = Xという関係の際、試験問題ではRSが与えられX(問題の選択肢)を求めると言う形が多い。この際は、「R * Xのレコードが全てRのレコードに含まれるか?」という観点で見るのが良い。含まれていないレコードが存在する場合は、その選択肢は即刻省く。

結合

内部結合(INNER JOIN)

  • 直積と同じく、2テーブルのカラムを連結する(テーブルの縦幅に加えて横幅も長くなる)。
  • 例:select * from 商品 INNER JOIN 売上明細 ON <条件>;のように、INNER JOIN句・ON句で記載する。
  • 実は、下記SQLのようなwhere句を用いた直積でも同じselect結果を得られるが、直積では2テーブルの全レコードの組み合わせが生成された後にwhere句で表示結果を絞り込むため、中間データセットが大きくなってしまい処理効率が悪い。一方で、INNER JOIN句・ON句を用いた内部結合では、ON句によって先に絞り込みが行われた後に対象レコードの組み合わせが生成されるため、中間データセットも小さくなり処理効率が非常に良い。そのため、INNER JOIN句・ON句を用いた内部結合を使用する事が推奨される。
    • select * from 商品, 売上明細 where 〜;
  • USING句を使うと、ON句で記載していた絞り込み条件を簡略化できる。
    • before:select 商品名, 数量 from 商品 INNER JOIN 売上明細 ON 商品.商品コード = 売上明細.商品コード;
    • after:select 商品名, 数量 from 商品 INNER JOIN 売上明細 USING (商品コード);
  • NATURAL JOIN句を使うと、絞り込み条件の記載を更に簡略化できる。
    • before:select 商品名, 数量 from 商品 INNER JOIN 売上明細 USING (商品コード);
    • after:select 商品名, 数量 from 商品 NATURAL JOIN 売上明細;
      • 2テーブルで同名のカラムを自動的に結合条件として使用してくれる。(同名カラムが複数有る場合は、全て用いられるので要注意。)

左外部結合(LEFT OUTER JOIN)

  • 左側テーブルの全レコード(条件一致しないレコードも含め)と、右側テーブルで条件に一致した行を、列結合して表示する。
  • 左側テーブルには存在するが、条件的に右側テーブルには存在しないレコードは、右側テーブル側のカラムをNULLで表示する。
  • 例:select 商品名, 売上数 from 商品 LEFT OUTER JOIN 売上明細 ON 商品.商品コード = 売上明細.商品コード;

右外部結合(RIGHT OUTER JOIN)

  • 左外部結合の、左右逆パターン。

全外部結合

  • 両テーブルの全レコードを表示し、条件一致しないレコードのカラムにはNULLを表示する。
  • 左外部結合右外部結合を同時にやるイメージ。
  • 例:select 商品名, 数量 from 商品 FULL OUTER JOIN 売上明細 ON 商品.商品コード = 売上明細.商品コード;

副問合せ

  • SQLの実行結果を用いてSQLを実行する為に、SQL文の中に入れ子SQLを記載する事。
  • 例:select 商品名 from 商品 where 商品コード = (select 商品コード from 売上明細 where 売上番号 = 1);
  • 内側(副問合せ)のSQLが先に実行され、その結果を受けて外側のSQLが実行される。

相関副問合せ

  • 内側(副問合せ)SQL文の中で、外側SQLで登場するテーブルを用いた、副問合せの事。
  • 例:select 商品名 from 商品 where exists (select * from 売上明細 where 商品コード = 商品.商品コード);
    • 外側SQLで登場する商品テーブルを、副問合せにて使用している。
  • 普通の副問合せとは違い、外側SQLfromが走ってレコードを取得する度に1レコードずつ副問合せが実行されるため、普通の副問合せよりも処理速度は落ちる。

CREATE

  • テーブル作成するSQL
  • 基本は、下記のように、カラム名とデータ型を指定するだけ。
    • CREATE TABLE テーブル名 (カラム名 データ型, ・・・);
    • CREATE TABLE 返品 (返品番号 INTEGER, 売上番号 INTEGER, 返品日付 DATE, 理由 VARCHAR(100));
  • 各カラムに特定の条件を付与する場合は、下記のように指定する。
    • CREATE TABLE テーブル名 (カラム名 データ型 制約, ・・・);
    • CREATE TABLE 返品 (返品番号 INTEGER PRIMARY KEY, 売上番号 INTEGER UNIQUE NOT NULL, 返品日付 DATE NOT NULL, 理由 VARCHAR(100) DEFAULT NULL);
      • 「返品番号」カラム:主キー制約。(主キー制約を付与すると、同時に非NULL制約も付与される。)
      • 「売上番号」カラム:ユニーク(一意)制約・非NULL制約。(制約の記載順序はどっちでも良い。)
      • 「返品日付」カラム:非NULL制約。
      • 「理由」カラム:デフォルト値をNULLに指定。

制約の種類

  • 主キー(PRIMARY KEY)制約
    • <カラム名> PRIMARY KEY句を使用する事で、そのカラムを主キーに設定する。
      • PRIMARY KEY (<カラム名>)でも可能。
    • 主キー制約を付与すると、同時に、一意(UNIQUE)制約・非NULL制約も付与される。
  • 一意(UNIQUE)制約
    • <カラム名> UNIQUE句を使用する事で、そのカラムに重複した値の挿入を禁止する。
      • UNIQUE (<カラム名>)でも可能。
    • 重複禁止なだけなので、主キー(PRIMARY KEY)制約とは異なり、NULL値の入力が可能。
  • 非NULL(NOT NULL)制約
    • <カラム名> NOT NULL句を使用する事で、そのカラムにNULL値の挿入を禁止する。
  • DEFAULT制約
    • <カラム名> DEFAULT <任意の値>句を使用する事で、そのカラムのデフォルト値を設定する。
    • カラムへのデータ入力を省略した場合、デフォルト値がカラムに自動格納される。
  • 外部キー(参照・参照整合性・FOREIGN KEY)制約
    • <カラム名> REFERENCES <他テーブル名(カラム名)>句を使用する事で、そのカラムを、<他テーブル名(カラム名)>を参照する外部キーとして設定する。
      • FOREIGN KEY (<カラム名>) REFERENCES <他テーブル名(カラム名)>でも可能。
    • 参照先カラムに存在する値以外の格納を禁止する。これにより、両テーブル間のデータ整合性を維持する。(当たり前だが、参照先カラムは、参照先テーブルの主キー。)
    • ON DELETE句により参照先テーブルの値が削除された際の、ON UPDATE句により参照先テーブルの値が更新された際の、参照元テーブルの挙動を指定できる。
      • FOREIGN KEY (<カラム名>) REFERENCES <他テーブル名(カラム名)> ON DELETE <挙動> ON UPDATE <挙動>
      • 挙動部分は下記を指定可能。
        • RESTRICT:エラーにする。
        • CASCADE参照元テーブルのレコードも削除(更新)される。
        • SET NULL:参照元テーブルのデータをNULLに変える。
        • NO ACTION:何もしない。

DROP

  • テーブルを削除するSQL
  • DROP TABLE テーブル名;

VIEW(ビュー)

  • 実在しない仮装テーブル(導出テーブル)を作成するSQL。もっと言うと、実テーブルに対する決まった形のSQL文を定型化したモノ。
  • CREATE VIEW <ビュー名> (<カラム名, ・・・>) AS SQL文;
  • セキュリティと利便性の向上が目的。
    • セキュリティ(情報のカプセル化):
      • 例えば、会員テーブルのようなセキュリティ情報(ユーザパスワード等)を含むテーブルを、誰でも参照可能にしたくないような場合、公開可能なテーブルのみSELECTするSQL文を会員ビューとして作成する。
        • CREATE VIEW 会員ビュー AS select 会員コード, 氏名 from 会員;
    • 利便性向上:
      • よく使うSQL文を簡単に呼び出せるようにできるので、利便性が向上する。
        • CREATE VIEW 会員ビュー AS select * from 会員 where 最終使用日 <= '2024-12-31';
  • 下記条件を満たす場合は、VIEWに対して追加・更新・削除が可能。
    • 複数の行を1行に纏めていない事。
    • VIEWに含まれていないカラムに非null制約が設定されていない事。
      • VIEWに追加・更新した際、VIEWに含まれていないカラムにはnullが設定されるため。
    • 結合している場合には、対象のテーブルが特定できる事。

GRANT(権限付与)

  • テーブル・VIEW毎に、誰に何の操作を許可するか設定する。
  • GRANT <権限>, ・・・ ON <テーブル名> TO <ユーザ名>;
    • <ユーザ名>部分をPUBLICにすると、全員という意味。
  • GRANT SELECT ON 会員 TO daisuke;で、daisukeユーザに、会員テーブルのselect権限を付与できる。
  • REVOKE SELECT ON 会員 FROM daisukeで、daisukeユーザから、会員テーブルのselect権限を剥奪できる。

スーパータイプ・サブタイプ

  • 「AはBとCに分けられる」場合、Aをスーパータイプ、B・Cをサブタイプと呼ぶ。
  • 排他的サブタイプ共存的サブタイプ
    • 排他的サブタイプ:
      • あるデータがBとCの両方に属することは無く、どちらか一方にのみ属する事。
      • リレーション表記は、スーパータイプから1本線が伸び、△部分で2本に分岐する形になる。
    • 共存的サブタイプ:
      • 排他的サブタイプと逆で、あるデータがBとCの両方に属する事が有り得る事。
      • リレーション表記は、スーパータイプから2本線に分かれた形で線が伸び、それぞれの線に△が記載された形になる(△部分で分岐する形ではない)。
    • 排他的サブタイプの場合は「〜区分」、共存的サブタイプの場合は「〜フラグ」、と呼ばれることが多い。
  • サブタイプ間の共通項目は、サブタイプではなくスーパータイプに持たせる。一方で、特定のサブタイプしか持たない項目はサブタイプに持たせる。
  • サブタイプには、スーパータイプの主キーを持たせる。一方で、スーパータイプには、どのサブタイプかを判断するための項目を持たせる。

-

  • -

  • -

  • -

SELECT文の実行順序

select文の各句の実行順序は下記の順番。

  1. from
  2. join
  3. where
  4. group by
  5. 集約関数
  6. having
  7. select
  8. order by
  9. limit

上記の通り、limit句は1番最後に実行されるため、limitで表示行数を絞ったとしても、DBサーバへの処理負荷や処理時間は全件処理とほぼ変わらない(らしい)。
また、sum()count()等の集約関数は、グルーピングされたデータに対して処理する関数のため、group byより前のwhere句等では実行不可。

正規化

正規化とは、更新時異常を起こさないように、1箇所1事実を実現する為に、テーブルを分解する事。

第1正規系

  • 全てのカラムが単一の値をとる状態の事。
  • 1つのカラムに複数の値が入っていたり、碁盤のような綺麗なマス目状態になっていない場合、非正規系

第2正規系

  • 全ての非キー属性(候補キーではないカラム)が、各候補キーに完全関数従属している状態の事。言い換えると、「候補キーの一部のカラムによって一意に定まる非キー属性が有るか?」。無ければ既に第2正規系、有れば部分関数従属性を排除できていないため、第2正規化が必要。
  • コツは、先に、候補キーの組み合わせを全て洗い出す。候補キーにならなかった残りのカラムを全て非キー属性として、「候補キーの一部のカラムによって一意に定まる非キー属性が有るか?」を検討する。
  • 第2正規化する際は、部分関数従属性における候補キーを分解先のテーブルの主キーとし、非キー属性はそのまま分解先のテーブルに移動させる。

第3正規系

  • 「主キー以外のカラムが決まれば一意に定まるカラムが有るか?」。無ければ既に第3正規系、有れば推移的関数従属性を排除できていないため、第3正規化が必要。
  • 第3正規化する際は、推移的関数従属性における推移元カラムを分解先のテーブルの主キーとし、非キー属性はそのまま分解先のテーブルに移動させる。

参考文献

Gitについて_2

目次

概要

昨年、Gitの基本ついて下記記事を社内勉強会で発表した。 daisukeinoue.hatenablog.com

今年も続編としてGit基礎を発表をすることにしたので、Conflictcherry-pickrebase辺りについて以下に纏める。

Conflict(コンフリクト)

Conflictとは?

  • Merge先ブランチとMerge元ブランチにおいて、同じファイルの同じ行を互いに改修する事で、相反する内容になってしまっている状態の事。
    • Gitからすると「どっちが正しいの?」な状態。
  • Mergeする際に起こる。
  • なお、パワポやエクセル等のバイナリファイルの場合は、step(行数)という概念が無いため、同ファイルを改修した時点で改修箇所に依らずConflictを起こす。

解決方法は?

  • 手動でファイルを書き直し、再度CommitすればOK。

cherry-pick(チェリーピック)

cherry-pickとは?

  • 任意のブランチの任意のCommitを、別ブランチに反映する事。
  • 下記図の状態の場合に、ブランチBのCommitA・BだけをブランチBに反映した際、ブランチBをブランチAにMergeしてしまうと、不要なCommitCも反映されてしまう。このような場面でcherry-pickを利用すれば、CommitA・Bだけ反映する事が可能。
  • ある版数(Ver)に対してパッチしたバグ改修を、別版数へ反映(横展開)したい際などに有用。

rebase(リベース)

rebaseとは?

  • Mergeと同じく、Commitをあるブランチから別のブランチに統合する事。
    • Mergeとはその方法が異なる。

Mergeとの違いは?

下記状況の場合で説明。

Mergeを実行すると

MergeCommitが打たれる。 2本の枝が1本に纏まるイメージ。

rebaseを実行すると

featureブランチがmainブランチの先頭から生えている形になる。
featureの生え元を、mainブランチの先頭に移動させ、1本の綺麗なブランチに整えるイメージ。

rebaseのメリット

主なメリットは、MergeCommitが打たれない事や、ブランチが1本線になる事により、ブランチ履歴が非常にすっきりして綺麗になる事。

rebaseのデメリット

rebaseの正体(裏側での動きは)は、featureブランチで打ったCommitを、mainブランチの先頭から生やした新規ブランチにcherry-pickをしている。
そのため、元CommitとCommitハッシュ値(リビジョン)が異なってしまうというデメリットが有る。
他にも細々したデメリットが有るため、使用する際は要注意。

参考文献

長岡花火の過ごし方

目次

概要

長岡花火に2019年と今年の2度参戦したので、得た気付き・学びをTips化する。

全体の流れ

  1. 長岡花火のチケットの予約
  2. 長岡花火のチケット抽選結果発表
  3. ホテルの予約
  4. レンタカー・駐車場の予約
  5. 参戦

詳細

長岡花火のチケットの予約

  • 公式サイトからチケットを予約する。
  • 公式転売サイトが有るため、複数人で応募し、チケットが余ってしまった場合でも安心。
  • 結論、A会場側の大手大橋と長生橋の間のベンチ席が無難そう。
    • 大手大橋と長生橋に挟まれる間のエリアがメイン(花火の目の前)なので、「視界一面が花火」感を味わいやすい。
    • 長岡駅を利用したいなら、A会場の方が駅に近い。(もしかしたら、会場までの屋台も、長岡駅が有るA会場側の方が多いかも?)
    • 飲食物置ける方が便利。
    • どこかのサイトに、「花火自体は左岸側(B会場)から上がるから、右岸(A会場)の方が全体を見やすい。右岸の方が人気で倍率高い。」と書いてたらしい。(他人情報)

長岡花火のチケット抽選結果発表

  • 【参考】チケット当選戦績(2023年):
    • A会場_マス席:2/2枚落選。
    • A会場_ベンチ席:2/2枚当選。

ホテルの予約

  • 筆者は抽選結果発表よりも前(6月頭時点)にホテルを探していたが付近のホテルはほぼ全て埋まっていた事と、直前までキャンセル料無料なホテルも有るため、可能であれば抽選結果発表前から予約する事を強くお勧めする。

レンタカー・駐車場の予約

  • 花火大会の行き帰り(前日・翌日含む)に道中のプチ旅行も楽しみたい場合はレンタカーを借りると思うが、ホテルから花火会場まで離れている場合は会場近くの駐車場の予約が必要。
  • 筆者は軒先パータキングを利用して駐車場を予約したが、駐車場によって値段や予約開始日がバラバラ。
    • さいわいプラザAさいわいプラザBという駐車場が、安くて比較的近くて良かった。

参戦

  • 関東圏から車で行く場合、当日の早朝に出発すれば、長岡着いてからランチ食べて会場向かうくらいの余裕は有り、充分間に合う。(事故等で大渋滞にハマった場合は例外かも)
  • ランチ:
    • 有名なへぎ蕎麦屋に行ってみたが13時過ぎで100人待ちだった。
    • 代わりに行ったラーメン屋は結構美味かったのでお勧め。
    • どこのお店もだいたい14時閉店のお店が多いため、当日困らないようにいくつか候補を見つけておくのが良さそう。
  • 屋台は、長岡駅から大手大橋までの大通り(国道351線)沿いに多く出店されている。
  • 休憩所:
    • 早めに会場に着いた際、開演まで席で待っていると日なたでしんどい。ここは日陰になっているのと、すぐ近くに屋台が有るので、レジャーシートを敷いて開演まで待つのに最適。(2,3時間前に着かないと空いてないかも)
  • 会場のトイレは意外と空いている。
  • 花火会場ではスマホ通信がほぼ出来ないため、公式アプリのインストールや、花火プログラムのダウンロード等は事前に済ませておくべし。

オススメの持ち物

  • 虫除けスプレー
    • 会場には、見た事無い大きさのコオロギがはびこっていた。(虫除けスプレーで効果有るかは不明)
  • 簡易トイレ
    • 高速で大渋滞にハマってしまった時の為に念の為。
  • ウェットシート
    • 汗かくし、屋台で飲み食いするので、必需品。
  • 日焼け止め
  • レジャーシート・クッション
    • 席種にも依るが、2h近く地べたに座るタイプの席の場合は重宝する。ケツ痛い。
  • キャップ・サングラス・汗拭きタオル

【Appendix】行き帰りのプチ旅行

  • The Sauna
    • サウナシュランを獲得しているサウナ施設。
    • 森林に囲まれており、水風呂は川水の樽水風呂、外気浴は森林浴なので、最高に整う。今後他のサウナで満足できるか不安なレベル。
    • 男女混浴可能な代わりに水着が必要なので、忘ないように要注意。
    • マップ
  • うずら家
    • 安くて美味しい蕎麦屋
    • お店の雰囲気も良く、接客も驚くほど丁寧。
    • マップ

【WIP】お小遣い管理アプリの企画に向けて

目次

概要

お小遣い管理アプリの企画に向け、事前に整理・考察すべき情報を以下に纏める。

整理が必要そうな情報

  • 小遣い管理の課題とその根拠。
  • 課題解決できるサービスの概要・要件。
  • 競合サービスや類似サービスの調査。
  • サービスの実現方式。
  • サービスによるマネタイズ構造。

【整理】小遣い管理の課題とその根拠

仮設

自分の経験や今の日本の時代性を基に、下記のような状況・課題が有ると予想。

要因背景 要因 課題・需要
共働き時代 晩御飯代等を渡される鍵っ子の増加。
 → お小遣いの額や重要度の増加。
お小遣い管理の需要増。
デジタルネイティブスマホネイティブ世代 子供がいつでもどこでも何でも購入可能な状況。
 → 浪費リスクの増加。
お小遣い利用の監視の需要増。
グローバル化 購入可能な商品の多様性・複雑性の向上。
 → 不適切商品(年齢制限や違法商品等)の購入リスクの増加。
子供の消費活動の制御の需要・必要性増。

実値調査

(要追記)

【整理】課題解決できるサービスの概要・要件

まず、サービスの特性上、親・子それぞれ下記が非常に重要となりそう。

  • 親:子供への送金自体は既存サービスで実現できるため、本サービスをわざわざ導入する利点・ウマミをしっかり訴求する事。
    • 親が抱えている課題感の的確な把握と的確なサービスデザインが必要。
  • 子供:親に消費活動を監視・管理されるという特性上、サービスに対するダサさや抵抗感を限り無く0に近付ける事。

【整理】競合サービスや類似サービスの調査

Gitについて

目次

概要

Gitの基本ついて社内勉強会で発表することになったので、説明に使えるメモを下記に纏める。

Gitとは

何者?

  • 分散型バージョン管理システム
    • バージョン管理が可能。
    • 分散型なので、複数人開発に最適。
  • リーナスさんがLinux開発時のバージョン管理の為に作った。

そもそもバージョン管理とは?

ファイルの変更履歴を保存・管理する事。
GitやSubversionが代表的なバージョン管理システム
バージョン管理する事で下記のような事が可能。

  • 過去バージョンに戻す。
  • いつ誰かどんな変更を実施したのか把握。

どんな課題を解決する為のモノ?

下記サイトのGitが生まれた理由を参照。
【絶対理解できる】Gitとは?特徴やできることまとめ! | 侍エンジニアブログ

基本用語(イメージつきやすい言葉でのザックリ解説なので、詳細・正確な解説は別サイトをご確認ください。)

  • リポジトリ
    • GitのCommitが管理されている領域。
  • リモートリポジトリ:
  • ローカルリポジトリ
  • ブランチ:
    • リポジトリ内に存在する世界。
    • ブランチとブランチは並行世界(パラレルワールド)のようなイメージで、あるブランチ上での出来事は他のブランチには一切影響しない。
      • → ブランチを分けて開発する事で、他機能や既存資材に影響を及ぼさずに開発を進められる。
  • Merge:
    • ブランチとブランチを統合する事。

全体像

Gitの全体像

Gitの操作と、その時起こっている事

上記全体像の画像内の開発着手中開発後の部分にフォーカスし、流れに沿って一部操作について纏める。
なお、共有資材への反映_MergeRequest以外、下記全てローカルリポジトリ上での操作です。

①開発用ブランチ作成

mainブランチ等から、自分の開発用のブランチを作成する。
git checkout -b <ブランチ名>

②開発

先ほど作成した自分のブランチ上でコーディングやDoc修正等を実施する。
修正内容は、Git内のワークツリーという場所上で行われる。そのため、ファイルを保存してもGit(ローカルリポジトリ)にはまだ反映されない。

add前

③開発内容の保存_ステージング

自分の内容をローカルリポジトリに反映する為にこの後Commitを行なうが、Commit対象のファイルを宣言し、対象ファイルをワークツリーからインデックスに反映する。
この操作をステージングと呼ぶ。
git add <ファイル名>

add後

④開発内容の保存_Commit

先ほどステージングしたファイルをローカルリポジトリに反映する。
この操作をCommitと呼ぶ。
git commit -m "<コミットメッセージ>"

Commit完了

【参考】⑤バグ改修

新規ファイルを実装中に既存ファイルのバグを見つけたので、バグ改修。

改修add前

【参考】⑥バグ改修の保存_ステージング

新規実装とバグ改修のCommitは分けたい。そのため、バグ改修ファイルのみを指定してadd。
git add <ファイル名>

改修add後

【参考】⑦バグ改修の保存_Commit

バグ改修ファイルのみCommit。
git commit -m "<コミットメッセージ>"

改修Commit後

⑧リモートリポジトリ(共有資材)への反映_push

先ほどローカルリポジトリに保存した開発内容を、リモートリポジトリ(共有資材)へ反映する。
git push origin <ブランチ名>

push後

⑨リモートリポジトリ(共有資材)のmain資材への反映_MergeRequest(PullRequest)

mainブランチ等へ、自分の開発用ブランチの開発内容を反映する為に、mainブランチへのMergeRequestを作成する。
その後、RV権者によるRVが完了したらmainブランチへMergeされる流れ。

【参考】コマンドメモ

  • git ls-files
    • ステージにaddされているファイルを確認。
  • git log --stat
    • 各Commitでどのファイルが修正されたかも併せてCommitLogを表示する。
  • git log --graph
    • ブランチツリーをグラフィカルに表示する。

【参考】rebaseについて

  • rebaseを使うウマミは大きく下記2つ。
    1. Commit履歴が綺麗にできる。(ブランチがなるべく1本の流れになるようにできる)
    2. 切り出し元ブランチ(mainブランチ等)のCommitを切り出しブランチ(featブランチ等)に取り込める。
  • rebaseは、ブランチ切り出し元(mainブランチ等)に対して、切り出しブランチ(featブランチ等)のCommitのCherry-pickを繰り返す事で実現されている。
    • そのため、featブランチにmainブランチのCommitを取り込む際、Mergeを実施するとConflictの解消は1度で良いが、rebaseを実施した場合はcherry-pickの回数分だけConflict解消が必要となる。
  • rebaseすると、featブランチ上のCommitハッシュ値が変わるので、同featブランチを複数人で使用している場合は、他開発者がPushできなくなってしまう等の迷惑がかかる。
  • featブランチにmainブランチのCommitを取り込む際、Mergeではなくrebaseを使用するのは、下記基準を全て満たしている場合にした方が良さそう。
    • Conflict解消の手間よりもCommit履歴の綺麗さを優先したい。
    • 対象のfeatブランチを他開発者が使用していない。

参考文献

DB_正規化について

目次

概要

リレーショナルデータベース(RDB)の正規化について学習したので、以下に纏める。
また、説明上、図が必要な場面も有るため、今回はブログ上につらつら記載する形式ではなく、スライド形式で説明を記載する。

説明資料(SpeakerDeck)

参考文献

所感

  • 性能(パフォーマンス)を重視し正規化を実施し過ぎない方針も有るとの事だが、正規化の実施具合が実際にどれくらい性能に影響するのか要学習。(もちろん、カラムの数やリレーションの持ち方等によって一概に定義できないと思うが)
  • 今回学習したのはRDBにおける正規化だが、Firestoreの様なレコードやテーブルという概念が無いNoSQLなDBでは、同じような品質(DBの整合担保性)向上作業をどの様に実施するのか気になるので、要学習。

Springについて_「SpringFramework超入門〜やさしくわかるWebアプリ開発〜」を読んで

目次

概要

Springについて学ぶ為にSpringFramework超入門〜やさしくわかるWebアプリ開発〜*1を読んだので、重要そうなポイントを以下に纏める。

SpringFrameworkとは

Javaアプリケーション開発におけるフレームワーク。単に「Spring」とも呼ばれる。

開発が楽になる様々な機能を提供してくれていて、機能毎に下記の様なプロジェクトが存在する。

SpringBoot

Springアプリケーションを煩雑な設定をせず迅速に作成する為の機能を提供している。

Springが多機能になりすぎて、逆に開発をスタートする際のコストが大きくなってしまったという課題を解決してくれる。

Springプロジェクト

Spring MVC

Webアプリを簡単に作成する為の機能を提供。

Spring Data

データアクセスの為の機能を提供。

Spring Batch

バッチ処理機能を提供。

Spring Security

認証・認可の機能を提供。

Springコア

Spring DI

依存性注入の機能を提供。

Spring AOP

アスペクト指向プログラミングの機能を提供。

アプリケーションのレイヤ化

アプリケーションを理解・管理しやすくする為に、アプリケーション全体をひと纏まりとして捉えるのではなく、役割によってレイヤ化する。

DDD(ドメイン駆動開発)で定義されているレイヤは下記3層。また、各レイヤは複数のコンポーネントを包括する。

  1. Application層
  2. Domain層
  3. Infrastructure層

Application層とInfrastructure層はDomain層に依存して良いが、Domain層は他2層から疎な状態で、再利用可能でなくてはならない。

つまり、Domain層に変更が加わった際に他2層に変更が生じても良いが、他2層に変更が生じた際にDomain層に変更が生じてはいけない。

【概要図】

Application層

クライアントとの入出力となるUIを提供したり、リクエストを基にドメイン層を呼び出すなど、アプリケーションを構築する為のレイヤ。

この層はなるべく薄く保たれるべきなので、ビジネスルールを含んではいけない。

包括するコンポーネントは下記の通り。

Controller

クライアントからのRequestを受け取り、Domain層の適したServiceを呼び出す。

処理結果をViewに返却する。

View

クライアントへのUIを提供する。

Form

クライアントとController間でやり取りするデータを格納する入れ物。

具体的には、HTMLのformタグ内のデータを格納。(「Form」という名前の由来は恐らくこれ。)

Domain層がApplication層に依存しない為に、Application層→Domain層にデータを引き渡す際は、Formのまま渡すのではなく、Domain層のDomain Objectに変換してから引き渡すべき。また、その変換処理はApplication層(Controller)で行う。

Domain層

アプリケーションのコアとなるビジネスルール(業務処理)を担当し、Entityに対するサービス処理を提供するレイヤ。

包括するコンポーネントは下記の通り。

Service

業務処理を提供する。

Domain Object

  • 業務処理を行う中で発生する管理すべきデータの入れ物となるクラス。
  • その1種であるEntityは、DBとアプリがデータをやり取り(アプリ→DB,DB→アプリ)する際の、データの入れ物となるクラス。
    • Application層のFormに格納されていたデータがEntityに格納される。
    • DAO,DTOデザインパターンDTOがこれに当たる。
    • フィールドとアクセッサ(ゲッター,セッター)で構成され、クラス名はDBテーブル名と同じ名前にする。
    • DBテーブルの1レコードが1クラスに対応し、クラスが保有する各フィールドが対象レコードの各フィールド(各カラムの値)に対応する。

Repository(リポジトリ

  • DBとアプリがデータをやり取り(アプリ→DB,DB→アプリ)する際に、DBへのCRUD処理機能を提供するインターフェース。
  • 実体はInfrastructure層のRepositoryImplで実装するため、 どのようなデータアクセスが行われているかについての情報は持たない。
    • DAO,DTOデザインパターンDAOがこれに当たる。

Infrastructure層

Domain Object(Entityなど)にCRUD処理(永続化)を提供するレイヤ。

包括するコンポーネントは下記の通り。

RepositoryImpl

  • Domain層のRepositoryインターフェースを実装したクラスで、実際のCRUD処理を提供する。
  • 必ず、インターフェースを定義したうえで実装する。
    • 使用する側(Domain層)のクラスが、クラス依存ではなくインターフェース依存でRepositoryを利用できるようにし、依存性を低くする為。
    • RepositoryインターフェースがCrudRepositoryクラス(Springが提供)を継承する事で、自動的にCRUDメソッドが利用可能になる。
  • O/R MapperにSpringDataJDBCを使用する場合は、実装したRepository(インターフェース)から自動生成されるため、実装不要。

O/R Mapper

  • Object(Domain Object)とRDBとのデータのマッピング機能を提供する。
  • SpringDataJDBCがその一種。

DispatcherServlet

  • フロントコントローラと呼ばれ、クライアントからの全てのリクエストを受信してくれる。
  • リクエストを受信した後に、リクエストURLを基に然るべきリクエストハンドラメソッドを呼び出す。

リクエストハンドラメソッド

  • MVCControllerの中に定義されるメソッドで、クライアントからのリクエストを処理する。
  • リクエストURLとリクエストメソッド(GETorPOST)をアノテーションで記載する事で、リクエスト毎に正しいリクエストハンドラメソッドを呼び出せる。
  • 戻り値として、クライアントに返却すべきViewの名前をDispatcherServletにreturnする事で、DispatcherServletはその名前に応じたViewに、HTMLを生成させクライアントに返却させる。

サニタイズ

  • 危険なコードやデータを変換や除去する事で、無力化する処理。
  • 例えば、Webサイトの入力フォームに悪意あるコード等が入力された際に、無力化する。

Thymeleaf(タイムリーフ)

  • SpringMCVフレームワークにて推奨されている、動的にViewを生成する為のテンプレートエンジン。
    • HTMLベースのテンプレと、動的データをバインド(組み合わせる)してViewを構成する。
  • JavaのWebアプリのViewとしてはJSPがよく利用されるが、JSPファイルはWebブラウザが読み込めないため、開発段階でWebブラウザにてViewを表示して明示確認できないという課題が有る。
  • 一方で、ThymeleafはHTMLベースのため、ファイルをWebブラウザ上で明示確認しながら開発を進める事ができる。
    • そのため、UI(View)デザイナーとの分業が簡単になる。

Viewからのリクエストパラメータの受け取り方

@RequestParamアノテーションを使う方法とFormクラスを用いる方法の2種類有る。

@RequestParamアノテーションを利用する方法

  • リクエストハンドラメソッドの引数に@RequestParamアノテーションを付与する事で、リクエストパラメータを引数として受け取る事ができる。
    • Viewname(入力項目名)と同じ引数名を宣言する。
  • リクエストパラメータが増えるほど引数も増えるので、冗長になってしまう可能性が有る。

Formクラスを利用する方法

  • Formは、リクエストパラメータを格納する為のクラス。
    • リクエストパラメータを纏めて引き渡す事ができるので、パラメータ数が増えても冗長にならない。
  • POJOで実装する。
  • Viewのリクエストパラメータのnameと、Formクラスの変数名を同一名にする事で、リクエストパラメータが自動でFormクラスのフィールドに格納される。
    • リクエストパラメータはフィールドの型に自動変換される。

POJO

  • Plain Old Java Objectの略。
  • 「ポジョ」と読むらしい。
  • java.lang.Objectを継承して、それ以外何も継承していない、シンプルなJavaクラスの事。
    • java.lang.Objectは、全てのJavaクラスが暗黙的に自動で継承しているので、つまりは、明示的には何も継承していないクラスの事。

バリデーションチェック

大きく、単項目チェック相関項目チェックの2つに分かれる。

単項目チェック

  • 入力項目(フィールド)1つ1つに対して行う入力チェックの事。
  • 多くのフレームワークでは、だいたいの入力チェック機能は、アノテーションを付与するだけで提供されるようになっている。

相関項目チェック

  • 複数の入力項目(フィールド)に対して同時に行う入力チェックの事。
    • 例えば、左の入力項目が奇数、かつ、右側の入力項目が偶数、の様なモノ。
  • 自分で新たなアノテーションを作成するか、Validatorインターフェースを実装するかの2択。

form-backing bean(Formクラス)

  • バリデーションを行う際に必要となる、HTMLのformタグにバインドするFormクラスのインスタンスの事。
  • @ModelAttributeアノテーションを付与したメソッドでform-backing beanを作成する。
  • @ModelAttributeアノテーションが付与されたメソッドは、そのクラス(Controller)のリクエストハンドラメソッドの実行前に呼ばれ、returnするform-backing bean(Formオブジェクト)model.addAttribute(form)相当の処理が実行され、Model(ControllerからViewに渡される、表示データ等が格納されるオブジェクト)に格納される。
    • Modelに格納される際、デフォルトではリクエストスコープで格納されるので、クライアントへのResponse後に自動削除される。

プレースホルダ

実際の内容(値)を後から挿入する為に、とりあえず仮に確保した場所の事。

ジェネリッククラス

  • クラスを定義する際に、クラス名の右側にと記載されたクラスの事。

    • Tは「型パラメータ」と呼ばれる。

    • <>内の文字はTでなくても良いらしい。

    • class GenericClass<T> {
          private T data;
      
          public void setData(T value) {
              this.data = value;
          }
      
          public T getData() {
              return data;
          }
      }
      
  • クラスを利用する際に、Tの部分に型を指定すると、クラス内のTが指定した型に置き換わる。

    • 型は参照型(クラスやインターフェースなど)しか指定できず、基本型(intなど)を指定してしまうとコンパイルエラーになる。

    • 下記例では、String型に置き換わる。

    • GenericClass<String> genericClass = new GenericClass<String>;
      

トランザクション

複数の処理をひと纏まりにしたモノで、トランザクションが完了した際に、処理が全て成功していれば保存(コミット)、1つでも失敗していればトランザクションで行った処理を全て巻き戻す(ロールバック)する。

トランザクションの開始と修了の範囲を「トランザクション境界」と呼び、Domain層(MVCモデルにおけるModel)の入り口であるService(ServiceImplクラス)の中で定義する。

トランザクションを管理したいクラスやメソッドに@Transactionalアノテーションを付与すれば、トランザクションが自動で管理される。

application.properties

SpringBootプロジェクトにおいて、例えばDBへの接続情報等の環境設定を行う為のファイル。

Flash Scope(フラッシュスコープ)

1回のリダイレクトの間のみ有効なスコープ。

アノテーション

Springでは、「@」に続いて特定のキーワードを記載する形式でソースコードアノテーション(注釈)を記載する事で、Springが標準で提供している何かしらの機能を対象のソースコードに付与する事ができる。

例えば、AOPアスペクト思考プログラミング)を実現する際や、DIを実現する際などに使用する。

本書で勉強する際に頻出度が高かった一部のアノテーションをいかにメモる。

全体を通して使われるモノ

@Autowired

DI(依存性の注入)を担うアノテーションで、自動生成したインスタンスを、アノテーションを付与した変数に自動代入(注入)してくれる。これを行う事で、「new」キーワードによるインスタンス生成を記載しなくてよくなるため、newされる側のクラスに対する依存性が下がる。

Controllerクラスで使われるモノ

@Controller

クラスに付与することで、Spirngのコンポーネントとして認識される。

@RequestMappng

クラスやクラス内のメソッドに付与する事で、リクエストURLに対してどのメソッドの処理を実行するかのマッピングを行える。

アノテーションの第一引数にURLキーワード、第二引数にHTTPリクエストのメソッドを記載する。

@GetMapping

GETリクエストを処理する@RequestMappngの簡略アノテーション(「@RequestMapping」の第二引数に「Get」を指定した場合と同じ)。

@PostMapping

POSTリクエストを処理する@RequestMappngの簡略アノテーション(「@RequestMapping」の第二引数に「Post」を指定した場合と同じ)。

@ModelAttribute

メソッドか引数に付与する事で、それぞれ違った効果を発揮する。

メソッドに付与した場合は、リクエストハンドラメソッドが呼び出される前に、そのメソッドが呼ばれ、メソッドの戻り値がリクエストスコープでModelに格納される。form-backing beanの初期化処理等に利用される。

引数に付与した場合は、リクエストハンドラメソッドが呼び出される前に、その引数に

リダイレクト

クライアントからの任意のReqに対応するリクエストハンドラメソッドでクライアントへのResの画面名をreturnする代わりに、他のリクエストハンドラメソッドに処理を引き継ぐ為に、他リクエストハンドラメソッドが対応するURLをreturnする事で、仮想的なReqを投げるイメージ。

例えば、本書のQuizアプリの中では、下記の様に使用していた。

   // クイズを1件登録。
    @PostMapping("/insert")
    // 「@Validated」アノテーションで「QuizForm」オブジェクトをバリチェックした結果が「BindingResult」インターフェースに格納される。
    public String insert(@Validated QuizForm quizForm, BindingResult bindingResult, Model model, RedirectAttributes redirectAttributes) {
        // Formの中身をEntityに詰め替える。
        Quiz quiz = makeQuiz(quizForm);  

        // バリチェック。
        if(!bindingResult.hasErrors()) {
            service.insertQuiz(quiz);
            redirectAttributes.addFlashAttribute("complete", "登録が完了しました。");
            return "redirect:/quiz";
        }else {
            // バリチェックがエラーになった場合は、一覧表示処理を呼ぶ。
            return showList(quizForm, model);
        }
    }

上記は、クイズを登録する際のリクエストハンドラメソッドだが、11行目が該当箇所。

画面名をreturnせず、/quizというURLに対するReqが飛んで来た時と同じ処理を後続で走らせている。

【メモ】クライアントに同画面を返却する場合でも、Formの状態によって処理の走らせ方を変える

クライアントに同じ画面を返却する場合でも、Modelに詰めてるFormをどういう状態で返却したいかに応じて処理の走らせ方を変えている事に気付いたので、以下にメモを残す。

対象部分のソースは下記。

@Controller
@RequestMapping("/quiz")
public class QuizController {
    @Autowired
    QuizService service;
  
// 「form-backing bean」の初期化。
    // 「ModelAttribute」アノテーションを付与する事で、リクエストハンドラメソッドが実行される前に毎回このメソッドが実行される。
    // returnされるFormクラスは、model.addAttribute(form)相当の処理が実行され、「quizForm」という名前でModelに追加される。
    // 今回は、Formインスタンス内のラジオボタンの初期値設定のみ行っている。Formインスタンス内の他フィールドについては、後続処理の中で設定する。
    @ModelAttribute
    public QuizForm setUpForm() {
        QuizForm form = new QuizForm();

        // ラジオボタンの初期値設定。
        form.setAnswer(true);
        return form;
    }

    // クイズの一覧を表示する。
    @GetMapping
    public String showList(QuizForm quizForm, Model model) {
        // 新規登録設定。
        quizForm.setNewQuiz(true);

        // クイズの一覧を取得する。
        Iterable<Quiz> list = service.selectAll();

        // 表示用「Model」への格納。
        model.addAttribute("list", list);
        model.addAttribute("title", "登録用フォーム");

        // クライアントに返却するView名(HTMLのファイル名)をreturnする。
        return "crud";
    }

    // クイズを1件登録。
    @PostMapping("/insert")
    // 「@Validated」アノテーションで「QuizForm」オブジェクトをバリチェックした結果が「BindingResult」インターフェースに格納される。
    public String insert(@Validated QuizForm quizForm, BindingResult bindingResult, Model model, RedirectAttributes redirectAttributes) {
        // Formの中身をEntityに詰め替える。
        Quiz quiz = makeQuiz(quizForm);

        // バリチェック。
        if(!bindingResult.hasErrors()) {
            service.insertQuiz(quiz);
            redirectAttributes.addFlashAttribute("complete", "登録が完了しました。");
            return "redirect:/quiz";
        }else {
            // バリチェックがエラーになった場合は、一覧表示処理を呼ぶ。
            return showList(quizForm, model);
        }
    }
}

ユーザがクイズ登録機能を利用した際に、入力したクイズデータがバリデーションチェックを通るか弾かれるかによって、処理の走らせ方を変えている。

具体的には、41行目と44行目。

処理の走り方の違い

それぞれどういう処理の走らせ方をしているか、簡単に記載する。

①バリチェックが通った時の処理

  1. 41行目で/quizというURLに対してリダイレクト。
  2. 12行目のsetUpForm()メソッドが呼ばれる。
  3. リクエストハンドラメソッドとして、20行目のshowList(QuizForm quizForm, Model model)メソッドが呼ばれる。

②バリチェックで弾かれた時の処理

  1. 44行目で、Formオブジェクトを引数として渡してshowList(QuizForm quizForm, Model model)メソッドを呼ぶ。
  2. 20行目のshowList(QuizForm quizForm, Model model)メソッドが走る。

①と②の具体的な違い

具体的には、①と②では下記2点が大きく異なる。

  1. Formオブジェクトを初期化する、12行目のsetUpForm()メソッドが呼ばれるかどうか。
    • ①では呼ぶが、②では呼ばない。
  2. クライアントに返却するView名をreturnする20行目のshowList(QuizForm quizForm, Model model)メソッドに、Formオブジェクトを渡せるか渡せないか。
    • ①では渡せないが、②では渡せる。

処理の走らせ方を変えた意図

結論から言うと、Resした後のUIに、先ほどユーザが入力したデータを表示させたいかどうかによって①と②を使い分けているのだと理解した。

バリチェックが通った時は、UI上のクイズデータ入力フォーム等を初期状態(空文字等)にしておきたいので、先ほどユーザが入力したデータをResに含める必要が無い。つまり、Formオブジェクトは初期化されていてほしいという事。

一方で、バリチェックに弾かれた時は、UI上のクイズデータ入力フォーム等には、先ほどユーザが入力したデータを表示しておきたいので、Formオブジェクトは初期化してはいけないし、先程のReqで受け取ったFormオブジェクトをそのまま持ち回りたい(引数としてshowList(QuizForm quizForm, Model model)メソッドに渡したい)という事。

所感

  • 「Model」という言葉の意味がより分からなくなった。(多分、定義が結構曖昧)
    • MVCの「Model」と、クライアントにResする際にFormを詰める「Model」は別のモノを指している。
  • Springはアノテーションに着目すると、グッと読みやすくなる。
/* -----codeの行番号----- */