Spring Boot + npm + Geb で入力フォームを作ってテストする ( その59 )( 確認画面を作成する2 )
概要
記事一覧はこちらです。
Spring Boot + npm + Geb で入力フォームを作ってテストする ( その58 )( 確認画面を作成する ) の続きです。
- 今回の手順で確認できるのは以下の内容です。
- 確認画面の作成
- confirm.js を見たら「修正する」ボタンは実装済で変更する必要もなかったので、「送信する」ボタンを実装します。
- 2回に分けます。1回目は DB保存処理、2回目はメール送信処理を書きます。
参照したサイト・書籍
Flyway - DOCUMENTATION - SQL-based migrations
https://flywaydb.org/documentation/migrations#sql-based-migrationsファクトリ — Doma 2.0 ドキュメント
http://doma.readthedocs.io/ja/stable/query/factory/
目次
手順
DB に保存する処理を実装する
INQUIRY_DATA テーブルを変更する
INQUIRY_DATA テーブルの以下の点を変更します。
- type2 には入力画面3の「お問い合わせの種類2」で選択された項目の値を "," 区切りで結合した文字列を保存するようにします。また必須項目にしたので
NOT NULL
を追加します。VARCHAR(1)
→VARCHAR(32) NOT NULL
に変更します。 - survey には入力画面3の「アンケート」で選択された項目の値を "," 区切りで結合した文字列を保存するようにします。また任意項目にしたので
NOT NULL
を削除します。VARCHAR(1) NOT NULL
→VARCHAR(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」ボタンを押して更新し、カラムの定義が変更されていることを確認します。
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 されたSQLException
はRuntimeException
で 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に以下の画像のデータを入力します。
確認画面に以下のように表示されますので、「送信する」ボタンをクリックします。
完了画面が表示されます。
IntelliJ IDEA の Database Tools から INQUIRY_DATA テーブルのデータを確認すると、問題なく保存されていることが確認できます。
履歴
2018/06/17
初版発行。