かんがるーさんの日記

最近自分が興味をもったものを調べた時の手順等を書いています。今は Spring Boot をいじっています。

Spring Boot + npm + Geb で入力フォームを作ってテストする ( その59 )( 確認画面を作成する2 )

概要

記事一覧はこちらです。

Spring Boot + npm + Geb で入力フォームを作ってテストする ( その58 )( 確認画面を作成する ) の続きです。

  • 今回の手順で確認できるのは以下の内容です。
    • 確認画面の作成
    • confirm.js を見たら「修正する」ボタンは実装済で変更する必要もなかったので、「送信する」ボタンを実装します。
    • 2回に分けます。1回目は DB保存処理、2回目はメール送信処理を書きます。

参照したサイト・書籍

目次

  1. DB に保存する処理を実装する
    1. INQUIRY_DATA テーブルを変更する
    2. ModelMapper で使用する SessionData → InquiryData データ変換用クラスを作成する
    3. InquiryConfirmService クラスを作成する
    4. InquiryConfirmController クラスを変更する
    5. 動作確認

手順

DB に保存する処理を実装する

INQUIRY_DATA テーブルを変更する

INQUIRY_DATA テーブルの以下の点を変更します。

  • type2 には入力画面3の「お問い合わせの種類2」で選択された項目の値を "," 区切りで結合した文字列を保存するようにします。また必須項目にしたので NOT NULL を追加します。VARCHAR(1)VARCHAR(32) NOT NULL に変更します。
  • survey には入力画面3の「アンケート」で選択された項目の値を "," 区切りで結合した文字列を保存するようにします。また任意項目にしたので NOT NULL を削除します。VARCHAR(1) NOT NULLVARCHAR(32) に変更します。

今回は V1__init.sql はそのままで、V1.1__Alter_column.sql を追加してみます。src/main/resources/db/migration の下に V1.1__Alter_column.sql を新規作成し、以下の内容を記述します。

ALTER TABLE INQUIRY_DATA ALTER COLUMN type2 VARCHAR(32) NOT NULL;
ALTER TABLE INQUIRY_DATA ALTER COLUMN survey VARCHAR(32);

動作確認します。Tomcat を起動した後、IntelliJ IDEA の Database Tools で「Synchronize」ボタンを押して更新し、カラムの定義が変更されていることを確認します。

f:id:ksby:20180616200648p:plain

ModelMapper で使用する SessionData → InquiryData データ変換用クラスを作成する

まず今回は INQUIRY_DATA テーブルの Entity クラス ksbysample.webapp.bootnpmgeb.entity.InquiryData に Clob 型を使用しているので、

@Entity
@Table(name = "INQUIRY_DATA")
public class InquiryData {

    ..........

    @Column(name = "INQUIRY")
    Clob inquiry;

    ..........
}

CLOB を使用するためのメソッドを Dao インターフェースに追加します。src/main/java/ksbysample/webapp/bootnpmgeb/dao/InquiryDataDao.java の以下の点を変更します。

@Dao
@ComponentAndAutowiredDomaConfig
public interface InquiryDataDao {

    ..........

    /**
     * Clob 生成用
     * @return {@Clob} オブジェクト
     */
    @ClobFactory
    Clob createClob();

}
  • @ClobFactory Clob createClob(); を追加します。

次に src/main/java/ksbysample/webapp/bootnpmgeb/mapper の下に SessionData2InquiryDataTypeMap.java を新規作成し、以下の内容を記述します。

package ksbysample.webapp.bootnpmgeb.mapper;

import com.github.rozidan.springboot.modelmapper.TypeMapConfigurer;
import ksbysample.webapp.bootnpmgeb.dao.InquiryDataDao;
import ksbysample.webapp.bootnpmgeb.entity.InquiryData;
import ksbysample.webapp.bootnpmgeb.session.SessionData;
import ksbysample.webapp.bootnpmgeb.web.inquiry.form.InquiryInput03Form;
import org.modelmapper.TypeMap;
import org.springframework.stereotype.Component;

import java.sql.Clob;
import java.sql.SQLException;
import java.time.LocalDateTime;
import java.util.stream.Collectors;

/**
 * SessionData --> InquiryData データ変換用クラス
 */
@SuppressWarnings({"checkstyle:LineLength", "PMD.AvoidThrowingRawExceptionTypes"})
@Component
public class SessionData2InquiryDataTypeMap extends TypeMapConfigurer<SessionData, InquiryData> {

    private final InquiryDataDao inquiryDataDao;

    /**
     * コンストラクタ
     *
     * @param inquiryDataDao {@InquiryDataDao} オブジェクト
     */
    public SessionData2InquiryDataTypeMap(InquiryDataDao inquiryDataDao) {
        super();
        this.inquiryDataDao = inquiryDataDao;
    }

    @Override
    public void configure(TypeMap<SessionData, InquiryData> typeMap) {
        typeMap.setPreConverter(context -> {
            SessionData sessionData = context.getSource();
            InquiryInput03Form inquiryInput03Form = sessionData.getInquiryInput03Form();
            InquiryData inquiryData = context.getDestination();

            inquiryData.setType2(inquiryInput03Form.getType2().stream().collect(Collectors.joining(",")));
            Clob inquiryClob = inquiryDataDao.createClob();
            try {
                inquiryClob.setString(1, inquiryInput03Form.getInquiry());
            } catch (SQLException e) {
                throw new RuntimeException(e);
            }
            inquiryData.setInquiry(inquiryClob);
            inquiryData.setSurvey(inquiryInput03Form.getSurvey().stream().collect(Collectors.joining(",")));
            inquiryData.setUpdateDate(LocalDateTime.now());

            return context.getDestination();
        });

        typeMap.addMappings(mapping -> mapping.map(src -> src.getInquiryInput01Form().getLastname(), InquiryData::setLastname))
                .addMappings(mapping -> mapping.map(src -> src.getInquiryInput01Form().getFirstname(), InquiryData::setFirstname))
                .addMappings(mapping -> mapping.map(src -> src.getInquiryInput01Form().getLastkana(), InquiryData::setLastkana))
                .addMappings(mapping -> mapping.map(src -> src.getInquiryInput01Form().getFirstkana(), InquiryData::setFirstkana))
                .addMappings(mapping -> mapping.map(src -> src.getInquiryInput01Form().getSex(), InquiryData::setSex))
                .addMappings(mapping -> mapping.map(src -> src.getInquiryInput01Form().getAge(), InquiryData::setAge))
                .addMappings(mapping -> mapping.map(src -> src.getInquiryInput01Form().getJob(), InquiryData::setJob))
                .addMappings(mapping -> mapping.map(src -> src.getInquiryInput02Form().getZipcode1(), InquiryData::setZipcode1))
                .addMappings(mapping -> mapping.map(src -> src.getInquiryInput02Form().getZipcode2(), InquiryData::setZipcode2))
                .addMappings(mapping -> mapping.map(src -> src.getInquiryInput02Form().getAddress(), InquiryData::setAddress))
                .addMappings(mapping -> mapping.map(src -> src.getInquiryInput02Form().getTel1(), InquiryData::setTel1))
                .addMappings(mapping -> mapping.map(src -> src.getInquiryInput02Form().getTel2(), InquiryData::setTel2))
                .addMappings(mapping -> mapping.map(src -> src.getInquiryInput02Form().getTel3(), InquiryData::setTel3))
                .addMappings(mapping -> mapping.map(src -> src.getInquiryInput02Form().getEmail(), InquiryData::setEmail))
                .addMappings(mapping -> mapping.map(src -> src.getInquiryInput03Form().getType1(), InquiryData::setType1))
                .addMappings(mapping -> mapping.skip(InquiryData::setType2))
                .addMappings(mapping -> mapping.skip(InquiryData::setInquiry))
                .addMappings(mapping -> mapping.skip(InquiryData::setSurvey))
                .addMappings(mapping -> mapping.skip(InquiryData::setUpdateDate));
    }

}
  • .addMappings(mapping -> mapping.map(src -> src.getInquiryInput01Form().getFirstname(), InquiryData::set~)) の行が 120文字を超えているのですが、改行したくないので @SuppressWarnings"checkstyle:LineLength" を付加しています。
  • Clob::setString メソッドは SQLException を throw する可能性があるので try-catch 文を記述していますが、throw された SQLExceptionRuntimeException で throw することにします。このままだと PMD で警告が出るので @SuppressWarnings"PMD.AvoidThrowingRawExceptionTypes" を付加しています(実際には適切な例外クラスを定義して使用しましょう)。
  • typeMap.setPreConverter( ... ) 内には個別の変換処理が必要なものだけ記述し、単に Getter メソッドで取得して Setter メソッドでセットすればよいものは .addMappings(mapping -> mapping.map(src -> src.getInquiryInput01Form().getFirstname(), InquiryData::set~)) で記述します。

今の config/checkstyle/google_checks.xml の設定では @SuppressWarnings が反映されないので、以下の点を変更します。

<module name="Checker">

    ..........

    <module name="SuppressWarningsFilter"/>

    <module name="TreeWalker">
        <module name="SuppressWarningsHolder"/>
        <module name="OuterTypeFilename"/>
        ..........
  • 以下の2行を追加します。
    • <module name="SuppressWarningsFilter"/>
    • <module name="SuppressWarningsHolder"/>

InquiryConfirmService クラスを作成する

src/main/java/ksbysample/webapp/bootnpmgeb/web/inquiry の下に InquiryConfirmService.java を新規作成し、以下の内容を記述します。

package ksbysample.webapp.bootnpmgeb.web.inquiry;

import ksbysample.webapp.bootnpmgeb.dao.InquiryDataDao;
import ksbysample.webapp.bootnpmgeb.entity.InquiryData;
import ksbysample.webapp.bootnpmgeb.session.SessionData;
import ksbysample.webapp.bootnpmgeb.web.inquiry.form.ConfirmForm;
import org.modelmapper.ModelMapper;
import org.springframework.stereotype.Service;

/**
 * 確認画面用 Service クラス
 */
@Service
public class InquiryConfirmService {

    private final InquiryDataDao inquiryDataDao;
    private final ModelMapper modelMapper;

    /**
     * コンストラクタ
     *
     * @param inquiryDataDao {@InquiryDataDao} オブジェクト
     * @param modelMapper    {@ModelMapper} オブジェクト
     */
    public InquiryConfirmService(InquiryDataDao inquiryDataDao
            , ModelMapper modelMapper) {
        this.inquiryDataDao = inquiryDataDao;
        this.modelMapper = modelMapper;
    }

    /**
     * 入力されたデータを INQUIRY_DATA テーブルに保存してメールを送信する
     *
     * @param sessionData {@SessionData} オブジェクト
     * @param confirmForm {@ConfirmForm} オブジェクト
     */
    public void saveToDbAndSendMail(SessionData sessionData, ConfirmForm confirmForm) {
        // INQUIRY_DATA テーブルに保存する
        InquiryData inquiryData = modelMapper.map(sessionData, InquiryData.class);
        inquiryDataDao.insert(inquiryData);

        // メールを送信する
    }

}

InquiryConfirmController クラスを変更する

src/main/java/ksbysample/webapp/bootnpmgeb/web/inquiry/InquiryConfirmController.java の以下の点を変更します。

@Controller
@RequestMapping("/inquiry/confirm")
@SessionAttributes("sessionData")
public class InquiryConfirmController {

    ..........

    private final ModelMapper modelMapper;
    private final InquiryConfirmService inquiryConfirmService;

    /**
     * コンストラクタ
     *
     * @param modelMapper           {@ModelMapper} オブジェクト
     * @param inquiryConfirmService {@InquiryConfirmService} オブジェクト
     */
    public InquiryConfirmController(ModelMapper modelMapper
            , InquiryConfirmService inquiryConfirmService) {
        this.modelMapper = modelMapper;
        this.inquiryConfirmService = inquiryConfirmService;
    }

    ..........

    /**
     * 確認画面 「送信する」ボタンクリック時の処理
     *
     * @param sessionData {@SessionData} オブジェクト
     * @param builder     {@UriComponentsBuilder} オブジェクト
     * @return 完了画面の URL
     */
    @PostMapping("/send")
    public String send(SessionData sessionData
            , UriComponentsBuilder builder) {
        ConfirmForm confirmForm = modelMapper.map(sessionData, ConfirmForm.class);
        inquiryConfirmService.saveToDbAndSendMail(sessionData, confirmForm);

        return UrlBasedViewResolver.REDIRECT_URL_PREFIX
                + builder.path(UrlConst.URL_INQUIRY_COMPLETE).toUriString();
    }

}
  • private final InquiryConfirmService inquiryConfirmService; を追加します。合わせてコンストラクタにセットする処理を追加します。
  • send メソッドの引数に SessionData sessionData を追加します。
  • send メソッド内に以下の2行を追加します。confirmForm はメールの送信処理で使用します。
    • ConfirmForm confirmForm = modelMapper.map(sessionData, ConfirmForm.class);
    • inquiryConfirmService.saveToDbAndSendMail(sessionData, confirmForm);

動作確認

動作確認します。npm run springboot コマンドを実行し Tomcat を起動した後、ブラウザで http://localhost:9080/inquiry/input/01/ にアクセスします。

入力画面1~3に以下の画像のデータを入力します。

f:id:ksby:20180617123513p:plain f:id:ksby:20180617123602p:plain f:id:ksby:20180617123651p:plain

確認画面に以下のように表示されますので、「送信する」ボタンをクリックします。

f:id:ksby:20180617123817p:plain f:id:ksby:20180617123910p:plain

完了画面が表示されます。

f:id:ksby:20180617124107p:plain

IntelliJ IDEA の Database Tools から INQUIRY_DATA テーブルのデータを確認すると、問題なく保存されていることが確認できます。

f:id:ksby:20180617124232p:plain

履歴

2018/06/17
初版発行。