Chapter 23

Controller(import~情報一括登録)

kazpgm
kazpgm
2022.01.04に更新

自動作成されたControllerを説明する。

例:商品情報管理("/members/admin/shohin/shohin")

controller.admin.ShohinController.java

package com.kaz01u.demo.controller.admin;

・SimpleDateFormat:csvダウンロードでCsvMapperに対して、Date型の編集フォーマットを指定するのに使用している。

import java.text.SimpleDateFormat; 
import java.util.ArrayList;

・Calendar:検索条件の日付From、日付Toに+1日するため使用している。

import java.util.Calendar;
import java.util.Date; //使用していない。
import java.util.HashMap;

・LinkedHashMap:入れた順番に取り出すため、プルダウン用DBエレメントMap、商品情報一括登録表示画面の追加行選択Mapに使用する。

import java.util.LinkedHashMap;

・LinkedList:入れた順番に取り出すため、商品情報一括登録List、商品情報リスト一覧更新Listに使用する。

import java.util.LinkedList;
import java.util.List;
import java.util.Locale;
import java.util.Map;

・HttpServletResponse:csvダウンロードでresponseを戻すために使用している。

import javax.servlet.http.HttpServletResponse;

・ConstraintViolationException:CSVアップローメソッドでの、単項目チェックエラー例外

import javax.validation.ConstraintViolationException;

・BeanUtils:商品情報リスト一覧更新サブ処理、csvダウンロードでBeanUtils.copyPropertiesを使用して、同一プロパティ(型名まで同じもの)コピーしている。

import org.springframework.beans.BeanUtils;

・Autowired:@Autowiredアノテーションで、プロパティ、関数、サービス、セッションBeanの各クラスを注入している。

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.MessageSource;

・Page:情報一覧ページングのための、情報一覧データとPageableを含んだオブジェクト。

import org.springframework.data.domain.Page;

・PageImpl:情報一覧ページングのため、new PageImplを使用して、Pageオブジェクトをダミーで作成している。

import org.springframework.data.domain.PageImpl;

・PageRequest:情報一覧ページングのためPageRequest.ofを使用してPageableを作成している。

import org.springframework.data.domain.PageRequest;

・PageRequest:情報一覧ページングのためController のメソッド引数に Pageable pageable を指定している。

import org.springframework.data.domain.Pageable;

・PageableDefault:情報一覧ページングのため、@PageableDefaultで、ページングに関するデフォルト値を設定している。

import org.springframework.data.web.PageableDefault;

・Controller:Controllerクラスには@Controllerを付ける。

import org.springframework.stereotype.Controller;

・Model:htmlで表示する値を渡すため、Controllerメソッドの引数にModelを指定する。

import org.springframework.ui.Model;

・MimeTypeUtils:csvダウンロードのとき、@RequestMapping の producesに"application/octet-stream"を指定するために使用している。

import org.springframework.util.MimeTypeUtils;

・ObjectUtils:空チェックにisEmptyメソッドを使用している。

import org.springframework.util.ObjectUtils;

・BindingResult:html入力値エラーを受け取るために、Controllerメソッドの引数にBindingResultを指定する。

import org.springframework.validation.BindingResult;
import org.springframework.validation.ObjectError;

・Validated:html入力値をチェックするために、Controllerメソッドの引数に@Validatedを指定する。

import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.ModelAttribute; //使用していない。

・PostMapping:ControllerでリクエストPOSTのパラメーターを受け取るために、@PostMappingを使用している。

import org.springframework.web.bind.annotation.PostMapping;

・RequestMapping:ControllerのリクエストマッピングURL指定に@RequestMappingを使用している。

import org.springframework.web.bind.annotation.RequestMapping;

・RequestParam:html入力値を受け取るため、Controllerメソッドの引数に@RequestParamを指定する。

import org.springframework.web.bind.annotation.RequestParam;

・ResponseBody:CSVダウンロードメソッドの、戻り値をそのままレスポンスのコンテンツにするため@ResponseBodyを付けている。

import org.springframework.web.bind.annotation.ResponseBody;

・MultipartFile:CSVアップロードメソッドの、csvファイルのオブジェクト

import org.springframework.web.multipart.MultipartFile;

・JsonProcessingException:CsvMapperのwriterメソッドで作成したObjectWriterのwriteValueAsStringメソッドが当例外を発生させる。

import com.fasterxml.jackson.core.JsonProcessingException;

・InvalidFormatException:CSVアップロードメソッドで、csvファイルに変換できない文字が含まれている場合、例外発生する。

import com.fasterxml.jackson.databind.exc.InvalidFormatException;

・UnrecognizedPropertyException:CSVアップロードメソッドで、CSVファイルのタイトル行(1行目)がEntityの項目名(テーブル項目名ではない)でない場合、例外発生する。

import com.fasterxml.jackson.databind.exc.UnrecognizedPropertyException;

・Feature:CSVダウンロードメソッドで、すべての項目をダブルクォーテーションで囲むために、Feature.ALWAYS_QUOTE_STRINGSを使用している。

import com.fasterxml.jackson.dataformat.csv.CsvGenerator.Feature;

・CsvMapper:CSVダウンロードおよびCSVアップロードで、CSVファイルのマッピングを行うクラス。

import com.fasterxml.jackson.dataformat.csv.CsvMapper;

・CsvMappingException:CSVアップロードメソッドで、CSVファイルのタイトル行(1行目)と2行目以降の項目数が同じでない場合、例外発生する。

import com.fasterxml.jackson.dataformat.csv.CsvMappingException;

・CsvSchema:CSVダウンロードおよびCSVアップロードで使用するため、CSVのスキーマ定義(カラム定義、ヘッダー有無、囲み文字の定義など)を行います。

import com.fasterxml.jackson.dataformat.csv.CsvSchema;

・ShohinCsv:CSVダウンロードメソッドで使用する。CSVデータ項目及び、CSVヘッダー、及びCSVデータ項目とCSVヘッダーとの紐付けを持っている。

import com.kaz01u.demo.csv.ShohinCsv;

・Biztype:業種情報entity

import com.kaz01u.demo.entity.Biztype;

・Category:大分類情報entity

import com.kaz01u.demo.entity.Category;

・Subcategory:中分類情報entity

import com.kaz01u.demo.entity.Subcategory;

・Extracategory:小分類情報entity

import com.kaz01u.demo.entity.Extracategory;

・Shohin:商品情報entity

import com.kaz01u.demo.entity.Shohin;

・UploadComplexValidException:CSVアップロードメソッドでの、複合項目チェックエラー(自力チックで出したエラー)例外。

import com.kaz01u.demo.exception.UploadComplexValidException;

・ShohinForm:商品情報のFormクラス

import com.kaz01u.demo.form.ShohinForm;

・ShohinInsUpdListForm:商品情報一覧登録更新のFormクラス

import com.kaz01u.demo.form.ShohinInsUpdListForm;

・ShohinSrchForm:商品情報一覧画面の検索部分Formクラス

import com.kaz01u.demo.form.ShohinSrchForm;

・SrchOrderForm:商品情報一覧画面の昇降順部分Formクラス

import com.kaz01u.demo.form.SrchOrderForm;

・SessionShohinSrchForm:商品情報一覧画面の検索部分のセッションForm(sessionスコープのBeanで、できている)クラス

import com.kaz01u.demo.form.SessionShohinSrchForm;

・SessionShohinSrchOrderForm:商品情報一覧画面の昇降順部分のセッションForm(sessionスコープのBeanで、できている)クラス

import com.kaz01u.demo.form.SessionShohinSrchOrderForm;

・DbElementsService:DBエレメントサービスクラス

import com.kaz01u.demo.service.DbElementsService;

・SeqService:シーケンス作成サービスクラス

import com.kaz01u.demo.service.SeqService;

・ShohinService:商品情報サービスクラス

import com.kaz01u.demo.service.ShohinService;

・ShohinUpload:商品情報のCSVアプロードデータクラス

import com.kaz01u.demo.upload.ShohinUpload;

・AppProperties:application.propertiesの設定値がインジェクションされるクラス

import com.kaz01u.demo.utils.AppProperties;

・Consts:Controller、Serviceに共通するコンストを定義するユーティリティクラス現在、コンスト値は1件もありません

import com.kaz01u.demo.utils.Consts;

・CsvUtils:CSVファイル読み込みユーティリティクラス

import com.kaz01u.demo.utils.CsvUtils;

・Functions:共通関数ユーティリティクラス

import com.kaz01u.demo.utils.Functions;

・PageNavi:PageオブジェクトのページングHTMLを戻すユーティリティクラス

import com.kaz01u.demo.utils.PageNavi;

・GroupOrder1:Controllerメソッドの引数で@Validatedと共に使用し、Formクラスの、GroupOrder1指定箇所を入力チェックするのに使用している。

import com.kaz01u.demo.validation.GroupOrder1;

・GroupOrder2:Controllerメソッドの引数で@Validatedと共に使用し、Formクラスの、GroupOrder2指定箇所を入力チェックするのに使用している。

import com.kaz01u.demo.validation.GroupOrder2;

・Slf4j:SLF4Jでログ出力する。pom.xmlで指定した、spring-boot-starter-webがlogbackに依存しているので、実装はlogbackです。

import lombok.extern.slf4j.Slf4j;

/**
 * 商品情報管理のコントローラー
 *
 */

・@Controllerアノテーションを記述することで、Spring Boot はController として認識します。

@Controller
//リクエストマッピングURL指定

・@RequestMappingアノテーションを記述することで、クライアントからのリクエストに対してメソッドやハンドラをマッピングします。
・当ShohinControllerは、URLが"/members/admin/shohin/shohin"のときマッピングされます。★contoller-01

@RequestMapping(value = "/members/admin/shohin/shohin") 

・@Slf4jアノテーションを記述することで、log という名前でロガーを使用できるようになります。

//log出力用
@Slf4j

・Controller、Serviceに共通するコンストクラス(Consts.java)をimplementsしてます。
・importでも説明しましたが、まだConsts.javaにコンスト値は1件もありません。
・コンスト値を登録して、リクエスト処理のメソッドで「model.addAttribute("Controller", this); 」とすれば、HTMLテンプレートから、Controller.変数名で見ることができます。

public class ShohinController implements Consts{
    /**
     * 一覧表示時の、ソート項目の項目名(Entityの項目名。テーブル項目名ではない)
     */

・一覧表示時の、ソート項目名をEntity項目名で列挙する。
・一覧表示▲▼でSQLのソート項目を決定しているが、リクエストが改ざんされていた場合SQLインジェクションされる恐れがあるので、リクエスト値がこの配列に存在するかチェックする。存在しない場合先頭"products"でソートする。 ★contoller-02

    private static String[] sortItemNames = {"products", "productsname", "biztypeCd", 
        "categoryCd", "subcategoryCd", "extracategoryCd", "openkbn1", "insdt", "updt"};
    /**
     * PageableDefaultのsize(1画面中の表示レコード数)を指定
     */

・一覧表示時画面の1画面中の表示レコード数。
・リクエスト処理のメソッドで「model.addAttribute("Controller", this); 」としているので、HTMLテンプレートから、Controller.pageableDefaultSizeで見ることができます。★contoller-03

    public static final int pageableDefaultSize = 10; 

    /**
     * Shohinのproducts項目のsizeを指定
     */

・主キーをシーケンスとしたときのキーのサイズを設定する。
・seqService.getDbSeqメソッドでシーケンスキーを作成する時使用する。

    public static final int shohinProductsLen = 10; 

・@Autowiredで、各クラスをnewしてインスタンス化(Singleton)してくれる。

    @Autowired
    AppProperties appProperties;
    @Autowired
    Functions functions;
    @Autowired
    ShohinService shohinService;
    @Autowired
    SeqService seqService;
    @Autowired
    DbElementsService dbElementsService;

・@Autowiredで、各クラスをsessionスコープのBean(SessionShohinSrchForm、SessionShohinSrchOrderForm)をInjectする。

    @Autowired
    SessionShohinSrchForm sessionShohinSrchForm;
    @Autowired
    SessionShohinSrchOrderForm sessionShohinSrchOrderForm;
    @Autowired
    MessageSource messageSource;

■商品情報一括登録初期表示(mode=insList)

    //=======================================================
    // 商品情報一括登録
    //=======================================================
    /**
     * 商品情報一括登録表示初期処理
     * 商品情報一括登録を表示する初期処理
     *
     * @param mode モード
     * @param model モデル
     * @return 遷移先
     */

・サイドメニューの商品情報一括登録をクリックすると、JavaScriptのsubmitFrm5関数 によりサブミットされます。
・URL:/members/admin/shohin/shohinによりShohinController.javaにマッピングされ、mode=insListによりshohinInsListメソッドにマッピングされます。shohinInsListメソッドは商品情報一括登録画面を初期表示します。★insList-01

    @PostMapping(params="mode=insList")
    public String shohinInsList(

・リクエストパラメータmodeを受け取ります。
・Model:htmlで表示する値を渡すため、引数にModelを指定する。

                   @RequestParam("mode") String mode,
            Model model) {

・mode=insList、mode=insList_doのメソッドで共通に使用しているメソッドを呼ぶ。

        shohinInsListSub(model);

・遷移先として、/resources/templates/members/admin/shohin/shohinRegisterList.htmlをテンプレートファイルとする。★insList-02

        return "/members/admin/shohin/shohinRegisterList";
    }

・mode=insList、mode=insList_doのメソッドで共通に使用しているメソッドです。

    private void shohinInsListSub(Model model) {
        List<ShohinForm> shohinFormList = new LinkedList<ShohinForm>();
        ShohinForm shohinForm = new ShohinForm();
        //DBエレメントを取得する

・DBエレメントを使用している場合、以下DBエレメント取得ロジックが自動作成されます。
・商品情報Formの場合、業種ID、大分類、中分類、小分類のプルダウンがDBエレメントを使用します。

        LinkedHashMap<String, Map<Object, String>> dbELEMENTS = new LinkedHashMap<String, Map<Object, String>>();
        LinkedHashMap<String, Map<Object, String>> dbELEMENTS1;

・業種情報を対象に、業種マップをDBエレメントに追加するという処理。
・パラメータの意味
 ・getAllFlg=false:非公開は取得しない。なのだけど、公開フラグを持ってないので、公開フラグを選択するロジックはないので、設定値は無視される。
補足:テーブルに公開フラグがない場合でもgetAllFlgパラメータは存在する。
 ・lrgMapKey="biztypeCd":dbELEMENTSに、キー"biztypeCd"で、業種マップ(キー:業種ID、データ:業種ID + ":" +業種名)を登録する。★insList-03

        dbELEMENTS1 = dbElementsService.getDbEleAryLrgmidsmlBiztype(false, 
                        "biztypeCd");
        dbELEMENTS.putAll(dbELEMENTS1);

・大分類情報、中分類情報、小分類情報を対象に、大分類が選択されていないときは、大分類マップのみ、大分類が選択されてれば、さらに、中分類マップ、中分類が選択されていればさらに、小分類マップをDBエレメントに追加するという処理。
・パラメータの意味
 ・getAllFlg=false:非公開は取得しない。
 補足:テーブルに公開フラグがない場合でもgetAllFlgパラメータは存在する。
 ・categoryCd=null:設定値に関係なく、大分類情報全データ取得し、大分類マップを作成する。nullはまだ大分類が選択されていないということ。
 ・categoryCdFlg=true:categoryCd値がDBにある時、trueとする。categoryCdFlg=trueかつ、categoryCdがnull以外の時、中分類情報全データ取得し、中分類マップを作成する。
 ・subcategoryCd=null:中分類を設定する。nullはまだ中分類が選択されていないということ。
 ・subcategoryCdFlg=true:中分類がDBにある時、trueとする。subcategoryCdFlg=trueかつ、subcategoryCdがnull以外の時、小分類情報全データ取得し、小分類マップを作成する
 ・lrgMapKey="categoryCd":dbELEMENTSに、キー"categoryCd"で、大分類マップ(キー:大分類、データ:大分類+ ":" +大分類名)を登録する。
 ・midMapKey="subcategoryCd":dbELEMENTSに、キー"subcategoryCd"で、中分類マップ(キー:中分類、データ:中分類+ ":" +中分類名)を登録する。
 ・smlMapKey="extracategoryCd":dbELEMENTSに、キー"extracategoryCd"で、小分類マップ(キー:小分類、データ:小分類+ ":" +小分類名)を登録する。★insList-04

       dbELEMENTS1 = dbElementsService.getDbEleAryLrgmidsmlCategory(false, null, true, 
                        null, true, 
                        "categoryCd", "subcategoryCd", "extracategoryCd");
        dbELEMENTS.putAll(dbELEMENTS1);

・shohinForm(商品情報Form)にDBエレメントを設定する。

        shohinForm.setDbELEMENTS(dbELEMENTS);
        shohinFormList.add(shohinForm);

・shohinForm(商品情報Form)を追加したshohinFormListを、商品情報一括登録Fromに設定する。★insList-05

        ShohinInsUpdListForm shohinInsUpdListForm = new ShohinInsUpdListForm(shohinFormList);

・画面上で追加行選択できるようMapを作成し、modelアトリビュートに設定している。

       setAddNumMapForAttribute(model, shohinInsUpdListForm);       

・商品情報一括登録Fromをmodelアトリビュートに設定している。

       model.addAttribute("shohinInsUpdListForm", shohinInsUpdListForm);

・遷移先画面がサブミットするモード("insList_do")を、modelアトリビュートに設定している。

        model.addAttribute("mode", "insList_do");
    }
    
    /**
     * setAddNumMapForAttributeメソッド
     * 追加最大残行数を求め、画面上で選択できるようMapを作成する。
     * 
     * @param model モデル
     * @param shohinInsUpdListForm
     */
    private void setAddNumMapForAttribute(Model model, ShohinInsUpdListForm shohinInsUpdListForm) {
        //追加最大残行数を求める

・一覧表示時画面の1画面中の表示レコード数(pageableDefaultSize=10)から、現在の商品情報一括登録の行数を引いて追加最大残行数を求めている。★contoller-03

        int addMaxLine = pageableDefaultSize -  shohinInsUpdListForm.getShohinFormList().size();

・追加最大残行数が0以下なら0にする。

        addMaxLine = addMaxLine < 0?0:addMaxLine;
        Map<String,String> addNumMap= new LinkedHashMap<String,String>();

・画面の追加行選択プルダウンリスト表示用addNumMapに、”1行追加”から+1UPしつつ、追加最大残行数分作成する。

        addNumMap.put("", "追加行選択");
        for (int i = 1 ; i <= addMaxLine; i++) {
            addNumMap.put(Integer.toString(i), i+"行追加");
        }

・modelアトリビュートにaddNumMapを設定する。★insList-06

        model.addAttribute("addNumMap", addNumMap);

・modelアトリビュートに追加行選択ナンバー(addNum="")を設定する。

        model.addAttribute("addNum", "");

・画面上"${Controller.変数名}"で、変数が見れるように、modelアトリビュートに当クラス(("Controller", this)を設定する。

        model.addAttribute("Controller", this); //画面上"${Controller.変数名}"で、変数が見れる。例:Controller.pageableDefaultSize
    }

■商品情報一括登録行追加(mode=insList_add)

    /**
     * 商品情報一括登録行追加表示処理
     * 商品情報一括登録に行追加する処理
     *
     * @param shohinInsUpdListForm 商品情報一括登録
     * @param mode モード
     * @param model モデル
     * @return 遷移先
     */

・操作欄の追加行選択を選択すると、JavaScriptのdo_Submit_Clk2関数 によりサブミットされます。
・URL:/members/admin/shohin/shohinによりShohinController.javaにマッピングされ、mode=insList_addによりshohinInsListAddメソッドにマッピングされます。shohinInsListAddメソッドは商品情報一括登録画面に行追加します。★iinsList_add-01
・「ShohinInsUpdListForm shohinInsUpdListForm」に@Validatedつけていないので、商品名に半角"123abc"を入れてもOK。必須入力項目に入力なしでもOK。★iinsList_add-02
・もしも、必須チェックエラーになると、値を入れないと、行を増やすことができなくなるので@Validatedつけない。
・画面内容はshohinInsUpdListFormにセットされる。

    @PostMapping(params="mode=insList_add")
    // 入力チェックをやりたくないので、@Validatedはつけない。
    // ただし、typeMismatchエラー(例、金額欄に文字を入れた時)は拾うので、BindingResult resultは必要。
    public String shohinInsListAdd(ShohinInsUpdListForm shohinInsUpdListForm,
            BindingResult result,

・リクエストパラメータmodeを受け取ります。
・リクエストパラメータaddNum(追加行数)を受け取ります。
・Model:htmlで表示する値を渡すため、引数にModelを指定する。

            @RequestParam("mode") String mode,
            @RequestParam("addNum") String addNum,
            Model model) {

・商品情報一括登録画面内容(shohinInsUpdListForm)の商品情報行のリスト(getShohinFormList)の入力値(業種、大分類、中分類、小分類)をDBエレメントでチェックし、DBエレメント(業種、大分類、中分類、小分類)をShohinFormに追加する。
 補足:DBエレメントでチェック(checkAndEditLrgmidsmlForShohinFormメソッド)を、パラメータBindingResult=nullで呼んでいるので、エラーは起きない。

        for (ShohinForm shohinForm: shohinInsUpdListForm.getShohinFormList()) {
            //大分類、中分類、小分類の、DBエレメントを作成する
            checkAndEditLrgmidsmlForShohinForm(shohinForm, null);
        }

・入力値(業種、大分類、中分類、小分類)をDBエレメントでチェックした結果エラーの時。
 補足:業種、大分類、中分類、小分類は前述説明のとおりエラーにはならない。かつ、@Validatedを付けていないので、属性エラー、必須チェックエラーも起きない。

        if (result.hasErrors()) {

・エラーの時は、
 ・insList同様に、遷移先画面がサブミットするモード("insList_do")を、modelアトリビュートに設定している。
 ・insList同様に、画面上で追加行選択できるようMapをmodelアトリビュートに設定している。
 ・insList同様に、商品情報一括登録Fromをmodelアトリビュートに設定している。
 ・insList同様に、遷移先として、/resources/templates/members/admin/shohin/shohinRegisterList.htmlをテンプレートファイルとする。

            //モードを設定
            model.addAttribute("mode", "insList_do");
            setAddNumMapForAttribute(model, shohinInsUpdListForm);
            return "/members/admin/shohin/shohinRegisterList";
        }
        //追加する行数
        int addNumInt = Integer.parseInt(addNum);

・現在の商品情報一括登録の行数+追加する行数が、一覧表示時画面の1画面中の表示レコード数(pageableDefaultSize=10)を越した場合pageableDefaultSize-現在の商品情報一括登録の行数とする。以外は追加する行数とする。

        addNumInt = (shohinInsUpdListForm.getShohinFormList().size()+addNumInt)>pageableDefaultSize?
                            (pageableDefaultSize - shohinInsUpdListForm.getShohinFormList().size()):addNumInt;
        //追加行作成

・追加する行数分ShohinFormを作成する。★insList_add-03

      for (int i = 0 ; i < addNumInt; i++) {
            ShohinForm shohinForm = new ShohinForm();

・DBエレメント(業種、大分類、中分類、小分類)をShohinFormに追加する。
・各パラメータは★insList-03、★insList-04参照。
 補足:追加行のDBエレメントは、insList同じ方法で作成しています。

            //DBエレメントを取得する
            LinkedHashMap<String, Map<Object, String>> dbELEMENTS = new LinkedHashMap<String, Map<Object, String>>();
            LinkedHashMap<String, Map<Object, String>> dbELEMENTS1;
            dbELEMENTS1 = dbElementsService.getDbEleAryLrgmidsmlBiztype(false,  
                            "biztypeCd");
            dbELEMENTS.putAll(dbELEMENTS1);
            dbELEMENTS1 = dbElementsService.getDbEleAryLrgmidsmlCategory(false, null, true, 
                            null, true, 
                            "categoryCd", "subcategoryCd", "extracategoryCd");
            dbELEMENTS.putAll(dbELEMENTS1);
            shohinForm.setDbELEMENTS(dbELEMENTS);
            shohinInsUpdListForm.getShohinFormList().add(shohinForm);    
        }

・insList同様に、画面上で追加行選択できるようMapをmodelアトリビュートに設定している。

        setAddNumMapForAttribute(model, shohinInsUpdListForm);

・insList同様に、商品情報一括登録Fromをmodelアトリビュートに設定している。

        model.addAttribute("shohinInsUpdListForm", shohinInsUpdListForm);

・insList同様に、遷移先画面がサブミットするモード("insList_do")を、modelアトリビュートに設定している。

        model.addAttribute("mode", "insList_do");

・insList同様に、遷移先として、/resources/templates/members/admin/shohin/shohinRegisterList.htmlをテンプレートファイルとする。

        return "/members/admin/shohin/shohinRegisterList";
    }

■商品情報一括登録1行削除(mode=insList_del)

    /**
     * 商品情報一括登録1行削除処理
     * 商品情報一括登録から1行削除する処理
     *
     * @param shohinInsUpdListForm 商品情報一括登録
     * @param mode モード
     * @param model モデル
     * @return 遷移先
     */

・操作欄の削除を押下すると、JavaScriptのdo_Submit_Clk2関数 によりサブミットされます。
・URL:/members/admin/shohin/shohinによりShohinController.javaにマッピングされ、mode=insList_delによりshohinInsListDelメソッドにマッピングされます。shohinInsListDelメソッドは商品情報一括登録画面から行削除します。★iinsList_del-01
・「ShohinInsUpdListForm shohinInsUpdListForm」に@Validatedつけていないので、商品名に半角"123abc"を入れてもOK。必須入力項目に入力なしでもOK。★iinsList_del02
・もしも、必須チェックエラーになると、値を入れないと、行を削除ことができなくなるので@Validatedつけない。
・画面内容はshohinInsUpdListFormにセットされる。

    @PostMapping(params="mode=insList_del")
    // 入力チェックをやりたくないので、@Validatedはつけない。
    // ただし、typeMismatchエラー(例、金額欄に文字を入れた時)は拾うので、BindingResult resultは必要。
    public String shohinInsListDel(ShohinInsUpdListForm shohinInsUpdListForm,
            BindingResult result,

・リクエストパラメータmodeを受け取ります。
・リクエストパラメータnum(削除行)を受け取ります。
・Model:htmlで表示する値を渡すため、引数にModelを指定する。

            @RequestParam("mode") String mode,
            @RequestParam("num") String num,
            Model model) {

・insList_add同様に、商品情報一括登録画面内容(shohinInsUpdListForm)の商品情報行のリスト(getShohinFormList)の入力値(業種、大分類、中分類、小分類)をDBエレメントでチェックし、DBエレメント(業種、大分類、中分類、小分類)をShohinFormに追加する。
 補足:DBエレメントでチェック(checkAndEditLrgmidsmlForShohinFormメソッド)を、パラメータBindingResult=nullで呼んでいるので、エラーは起きない。

        for (ShohinForm shohinForm: shohinInsUpdListForm.getShohinFormList()) {
            //大分類、中分類、小分類の、DBエレメントを作成する
            checkAndEditLrgmidsmlForShohinForm(shohinForm, null);
        }

・入力値(業種、大分類、中分類、小分類)をDBエレメントでチェックした結果エラーの時。
 補足:業種、大分類、中分類、小分類は前述説明のとおりエラーにはならない。かつ、@Validatedを付けていないので、属性エラー、必須チェックエラーも起きない。

        if (result.hasErrors()) {

・エラーの時は、
 ・insList同様に、遷移先画面がサブミットするモード("insList_do")を、modelアトリビュートに設定している。
 ・insList同様に、画面上で追加行選択できるようMapをmodelアトリビュートに設定している。
 ・insList同様に、商品情報一括登録Fromをmodelアトリビュートに設定している。
 ・insList同様に、遷移先として、/resources/templates/members/admin/shohin/shohinRegisterList.html)をテンプレートファイルとする。

            //モードを設定
            model.addAttribute("mode", "insList_do");
            setAddNumMapForAttribute(model, shohinInsUpdListForm);
            return "/members/admin/shohin/shohinRegisterList";
        }
        int i = 0;

・shohinFormListを新規作成し、削除指定行以外のShohinFormをshohinFormListに追加する。★insList_del-03

        List<ShohinForm> shohinFormList = new LinkedList<ShohinForm>();
        for (ShohinForm shohinForm: shohinInsUpdListForm.getShohinFormList()) {
            i++;
            //削除指定行以外の場合
            if(Integer.parseInt(num) != i) {
                shohinFormList.add(shohinForm);
            }
        }

・作成したshohinFormListを商品情報一括登録From(shohinInsUpdListForm)に設定する。

        shohinInsUpdListForm.setShohinFormList(shohinFormList);

・insList同様に、画面上で追加行選択できるようMapをmodelアトリビュートに設定している。

        setAddNumMapForAttribute(model, shohinInsUpdListForm);

・insList同様に、商品情報一括登録Fromをmodelアトリビュートに設定している。

        model.addAttribute("shohinInsUpdListForm", shohinInsUpdListForm);

・insList同様に、遷移先画面がサブミットするモード("insList_do")を、modelアトリビュートに設定している。

        model.addAttribute("mode", "insList_do");

・insList同様に、遷移先として、/resources/templates/members/admin/shohin/shohinRegisterList.htmlをテンプレートファイルとする。

        return "/members/admin/shohin/shohinRegisterList";
    }

■商品情報一括登録登録ボタン押下(mode=insList_do)

    /**
     * 商品情報一括登録画面の登録ボタン押下処理
     * 商品情報一括登録画面で登録ボタンを押下したときの処理
     *
     * @param shohinInsUpdListForm 商品情報一括登録
     * @param mode モード
     * @param model モデル
     * @return 遷移先
     */

・登録ボタンを押下すると、JavaScriptのdo_Submit_Clk2関数 によりサブミットされます。
・URL:/members/admin/shohin/shohinによりShohinController.javaにマッピングされ、mode=insList_doによりshohinInsListDoメソッドにマッピングされます。shohinInsListDoメソッドは商品情報一括をDBに登録します。★iinsList_do-01
・「ShohinInsUpdListForm shohinInsUpdListForm」に(@Validated({GroupOrder1.class, GroupOrder2.class}) をつけているので、ShohinInsUpdListFormのチェックを行います。チェックはまず、Fromクラスに書かれたGroupOrder1.classグループ(必須チェック、桁数チェック)が行われ、OKの時、GroupOrder2.classグループ(属性チェック)が行われます。
・エラーはBindingResultに設定されます。
・画面内容はshohinInsUpdListFormにセットされる。

    @PostMapping(params="mode=insList_do")
    public String shohinInsListDo(@Validated({GroupOrder1.class, GroupOrder2.class}) ShohinInsUpdListForm shohinInsUpdListForm,
            BindingResult result,

・リクエストパラメータmodeを受け取ります。
・Model:htmlで表示する値を渡すため、引数にModelを指定する。

            @RequestParam("mode") String mode,
            Model model) {
        //モードを設定

・モード("insList")を、modelアトリビュートに設定している。

        model.addAttribute("mode", "insList");
        List<ShohinUpload> shohinUploadList = new LinkedList<ShohinUpload>();    
        HashMap<String, String> map = new HashMap<String, String>();
        int i = 0;

・商品情報一括登録画面内容(shohinInsUpdListForm)の商品情報行のリスト(getShohinFormList)の入力値(業種、大分類、中分類、小分類)をDBエレメントでチェックし、DBエレメント(業種、大分類、中分類、小分類)をShohinFormに追加する。
 補足:パラメータBindingResultを設定し、パラメータprefixes=, "shohinFormList[" + i + "]."としている。これによりエラー時はBindingResultにエラーメッセージが設定される。
 但し、実際には、プルダウンリストから、業種、大分類、中分類、小分類を選択するので、エラーにならない。

        for (ShohinForm shohinForm: shohinInsUpdListForm.getShohinFormList()) {
            //大分類、中分類、小分類をチェックし、DBエレメントを作成する

・checkAndEditLrgmidsmlForShohinFormメソッドは「Controller(情報登録)」チャプターにある。

            checkAndEditLrgmidsmlForShohinForm(shohinForm, result, "shohinFormList[" + i + "].");
            i++;
            //商品情報に設定する

・商品情報アップロードに商品Formの内容を設定し、商品情報アップロードリストに追加する。

            ShohinUpload shohinUpload = new ShohinUpload(
                    shohinForm.getProductsname(),
                    shohinForm.getBiztypeCd(),
                    shohinForm.getCategoryCd(),
                    shohinForm.getSubcategoryCd(),
                    shohinForm.getExtracategoryCd(),
                    shohinForm.getOpenkbn1()
                    );
            shohinUploadList.add(shohinUpload);
        }
        if (result.hasErrors()) {

・エラーの時は、
 ・insList同様に、画面上で追加行選択できるようMapをmodelアトリビュートに設定している。
 ・insList同様に、遷移先として、/resources/templates/members/admin/shohin/shohinRegisterList.htmlをテンプレートファイルとする。

            setAddNumMapForAttribute(model, shohinInsUpdListForm);
            return "/members/admin/shohin/shohinRegisterList";
        }

・商品情報一覧をDBに登録する。

        try {
            shohinService.registerForShohinInsUpdListForm(shohinUploadList);
        //その他の更新時エラー

・DB例外発生時はshohinServiceでrollbackされている。例外をキャッチしたら、ログを書いて例外を投げる。画面は例外表示画面になる。「システム共通例外発生画面」Chapter参照
 補足:Throwable.Errorは、ここでキャッチせずに、システム共通例外発生画面で処理している。

        } catch(Exception e) {
            e.printStackTrace();
            log.error("エラーが発生しました", e);
            throw e;
         }      

・DB更新OK時は、successMessageをmodelアトリビュートに設定している。★iinsList_do-02

        model.addAttribute("successMessage", "商品情報登録が完了しました");

・mode=insList、mode=insList_doのメソッドで共通に使用しているメソッドを呼ぶ。

        shohinInsListSub(model);
        return "/members/admin/shohin/shohinRegisterList";
    }
    /**
     * checkPkForShohinFormメソッド
     * 主キーをチェックする。(u:すべての主キーをチェックできる。 i:主キーにシーケンスが使われていないときチェックできる。)
     * 
     * @param kbn i:挿入時チェック、u:更新時チェック
     * @param shohinForm フォーム
     * @param result result。指定しないときはnullとすること。
     */

・主キーのチェック処理。
 ・kbnがu:更新チェックの時、主キーがDBにないとき”既に削除されています”エラーをBindingResultに設定する。
 ・kbnがi:挿入時チェックの時、主キーがDBにあるとき”既に登録されています”エラーをBindingResultに設定する。
  但し、kbnがiで主キーがシーケンスのときは、シーケンス番号作成時に重複しないことを保証しているので、このメソッドを使用しない。よって、商品情報一括登録画面の登録ボタン押下処理はDB挿入で、商品情報の主キーはシーケンスなので、このメソッドを使用していない。

    private void checkPkForShohinForm(String kbn, ShohinForm shohinForm, BindingResult result) {
        checkPkForShohinForm(kbn, shohinForm, result, "");
    }
    /**
     * checkPkForShohinFormメソッド
     * 主キーをチェックする。(u:すべての主キーをチェックできる。 i:主キーにシーケンスが使われていないときチェックできる。)
     * 
     * @param kbn i:挿入時チェック、u:更新時チェック
     * @param shohinForm フォーム
     * @param result result。指定しないときはnullとすること。
     * @param prefixes リスト配列に当オブジェクトがある時のprefixes。指定しないときは空文字とすること。
     */
    private void checkPkForShohinForm(String kbn, ShohinForm shohinForm, BindingResult result, String prefixes) {
        //更新時チェック
        if (kbn.equals("u")) {
            //主キーがテーブルになければtrue
            if (shohinService.findByPk(shohinForm.getProducts()) == null) {
                if (result != null) {
                    result.rejectValue(prefixes + "products", "validation.already-deleted", new String[] {"『商品CD』"}, "");    
                }
            }
        //挿入時チェック。主キーにシーケンスが使われていないときのみチェックする。
        //主キーにシーケンスを使っているときは、ここではチェックせずに、挿入時の重複例外を使う。

・kbnがiで主キーがシーケンスのときは、シーケンス番号作成時に重複しないことを保証しているので、このメソッドを使用しない。よって、商品情報の主キーはシーケンスなので、このメソッドを使用していない。デッドロジックになってしまうが、このままにしてある。

        } else {
            //主キーがテーブルにあればtrue
            if (shohinService.findByPk(shohinForm.getProducts()) != null) {
                if (result != null) {
                    result.rejectValue(prefixes + "products", "validation.already-registered", new String[] {"『商品CD』"}, "");    
                }
            }
        }
    }