職業訓練覚え書き

制作日誌まとめ(61〜72日目)

作っていくうちにやってみたいことが増えてしまい、多対多のリレーションで、中間テーブルを作って複数のユーザー、複数のアイテムが相互に結びつくような構成に挑戦

ここから資料の振り返りや、理解の薄かったテーブルの正規化のあたりの話など改めて復習。

 

many-to-manyで、かつマッピングファイルを用いたHibernate ORMの利用の挑戦が後半で、ほとんど調べ物のためにどんどん時間が過ぎていってしまうという事態に。

 

残念ながら、期間内に完成はできず。

改めて、次回制作でリベンジしよう。

制作日誌まとめ(54~60日目、休日など)

初日(10/1)

制作物の簡単な概要の作成。

今回はログイン機能を持った欲しい物リストを作ることに。

 

仮のビュー作成をSublimeで作り始める

 

2日目

各ビューにBootstrap連携を試みる
思い出しながらなので案の定手こずる。

作ったビューをSpringMVCプロジェクトに変えて、eclipseで作業開始

 

3日目

セッションを使ったログイン機能を実装する

ここで授業でどの部分が実現したい機能と関連するかみたいな、簡単な教材まとめも作成し始めた。

 

また、学校での制作物を自宅に持ち帰って開発したいと思って、workspaseからプロジェクトをFTPサーバーに入れた


休校日(10/4~10/7)

インポート方法、開発環境構築でハマる

学校では
ファイル>インポート>一般の中のフォルダまたはアーカイブ…でいけると聞いた気が

したのでそのままやってたけど、どうやら違う…?

ファイル>インポート>既存プロジェクトをワークスペース

これでとりあえずエラーは消えた。

ちゃんとした理解ではないけどできたので今回はよし

 

感想:自宅での開発環境構築が非常に難しかった。

 

学校ではSpringレガシープロジェクトが普通に出てるけど、自宅のMacWindowseclipseでは出てない?

STSというプラグインが必要、というのをネットでちょっと見ただけで勢いで入れようとするも、インストールに失敗し続ける。

 

で、eclipseをクリーン起動してみたり、管理者権限でインストールしてみたりしてどうにかSTSを入れることができたものの、Springレガシープロジェクトなんて一覧に出てこない。

 

どうもおかしいと思い、確認したところどうやら学校ではSpringIDEを入れてると気づく
(どんなプラグインeclipseに入ってるかの確認方法を学べたのは良かったかも)


また、SpringIDEのプラグインのインストール時にすべて選択ではなく、必要なもの(SpringIDE関連)のみチェックを入れてインストールしたほうが良いらしい

これでWindowsは学校と同じ環境にできた。

 

ただ、Macだと勝手が違いさらに混乱した

sudoでrootユーザーとしてeclipseを起動、プラグインインストールとやってしまうとセキュリティ的な問題なのか、普通にeclipse.appを立ち上げようとするとゴミ箱に入れるように促される。

sudoじゃだめなのか、と思い再インストールし直してみると、ファイルが壊れてるだったり、クラッシュした履歴が残ったりでeclipseを起動できなくなる問題が発生。
これの解決にかなり時間を食う

 

結果として、
Macはsudoじゃなくて、普通に起動で問題ない

eclipseのみならず、アプリが一度クラッシュしたりすると

ダイアログ画面(~が予期しない理由で終了しました。)が出現し、「再度開く」ボタンを押下しても、まったく動かない状態になったりするらしい

(その前に「The last time you opened Eclipse,」と出てきて、Don't open/Reopenを聞かれるがどっち押しても上のダイアログ)

解決方法は
finder>移動>optionボタンを押しながらライブラリをクリック>Saved Application Stateを開いて、elipseのフォルダを探して削除
これで治った。

 

●spring elements以下にservlet-context.xml等の表示が出てこない

プロジェクト上で右クリック、プロパティ>Spring>Beanサポート>SpringBean構成ファイルの保持>xml構成の追加で、servlet-context.xmlを追加すると表示された

 

tomcat起動時に「8080は使用中です」と表示されて起動しない問題

現在使用中のものを止めることで解決した

lsof -i:8080

と打ち込み出てきたPIDの数値(例えば9999)を

kill 9999

としてやれば現在のものが止まり、tomcatが動くようになった

 

とりあえず環境はなんとかこれでWindowsMac両方で用意できたけど、学校でWindows&自宅でもメイン機がWindowsになってるため、なるべくMacに慣れようと思い自宅での開発はMacを使うことに。

 

4日目

自宅で開発環境構築でハマったため、ほとんど3日目の内容の続き

ログイン周りのフォームや、画面のレイアウトを直したりが主。

画面にこだわると情報がいっぱい検索に引っかかるけど、肝心の機能面が完成しなかった、なんてことになりかねないため最低限にとどめようと反省
(Bootstrapのことの検索でほとんど一日費やしてしまった。奥が深すぎる&情報が多すぎる)
BootplyというBootstrapのデザインをテストできるサービスもあるらしい。時間がある時にやってみよう

 

5日目

ログイン認証のAuthFilterがどうやらうまくいってない?と気づく

確認したところweb.xml(デプロイメント記述子)でAuthFilter関連の記述をしないといけないとわかり、以下を追記

<filter>
 <filter-name>AuthFilter</filter-name>
 <filter-class>com.example.mvc.filter.AuthFilter</filter-class>
</filter>

<filter-mapping>
 <filter-name>AuthFilter</filter-name>
 <url-pattern>/admin</url-pattern>
</filter-mapping>

 

これでやっと認証フィルターが効いてくれた

まだ連携はできないけどDBのテーブルも用意を始める

 

6日目

欲しい物の追加フォームの作成(addItem.jsp)

formタグライブラリの使用

ログイン後の画面(admin.jsp)にも欲しい物一覧を表示させようとするも、それを表示させるメソッドの記述箇所が間違っていることに気づかずにこの日は終了

 

7日目

認証用によういしていたAuthController内にて干し芋の一覧を表示させるメソッドを記述し、やっとログイン後でも欲しい物一覧を表示させることに成功。

脱線に次ぐ脱線によってこの日はこれくらい…

 

休校日

商品追加後に、戻るボタンで再度管理画面admin.jspに戻そうとしたところエラー、またナビゲーションバーに用意しているHOMEのリンクもエラーになっていることに気づく
(PageNotFound no mapping found for HTTP request…的なやつ)

これがが出た時は、対応するControllerがなくてTomcatが判断できないエラー?
HOMEに戻りたい場合、
→@RequestMapping(value={"/", "/index"})
としてあげることで戻るボタンが使えるようになった。

 

ここまでのまとめとして、環境構築は非常に大変、というのがよくわかった

ただ、詰まってたところが解決したりすると脳汁ハンパないので詰まるほど面白いとも言えるかも

53日目(Spring Framework11)

ファイルアップロード

以前やったファイルアップロードをSpring MVCと連携させて実装する

 

利用方法

formタグにenctype属性をつける

 <form action="" method="post" enctype="multipart/form-data">
 ※Springの<form:form>でも同じ

 

フォーム部品を用意

 <input type="file" name="upfile" />

 

デプロイメント記述子(web.xml)

  <multipart-config>
    <location>C:/Users/tmp</location>
    <max-file-size>1048576</max-file-size>
  </multipart-config>
※1048576は1MBのこと

 locationタグで決めたフォルダは作成しておく
 (C:/Users/tmp)

 

Bean定義ファイル

<bean id="multipartResolver"       class="org.springframework.web.multipart.support.StandardServletMultipartResolver">
</bean>

※multipartResolverは、先のenctype属性の値であるmultipart/form-dataでアップロードされたファイルをSpringで扱うためのオブジェクト

 

さらに

<mvc:resources mapping="/uploads/**" location="/uploads/" />
uploadsフォルダに配置する予定なので、そのフォルダに画像リクエストがあった場合にSpringMVCが弾かないように、mvc:resourcesにuploadsフォルダを画像置き場として使えるように追記する

Controllerでの処理

  • アップロードされたファイルデータをMultipartFileオブジェクトとして取得
  • Multipart#transferTo()メソッドでディスクに書き込み

 ※引数はweb.xmlのlocationタグで決めた一時保存パス

 

記述例

UploadController.java

@GetMapping("/upload")
public String get(
   HttpServletRequest request,
   Model model) {
  // 画像一覧の取得
 File uploadsDirectory = new File(request.getServletContext().getRealPath("/uploads"));

 File[] fileList = uploadsDirectory.listFiles();

 model.addAttribute("fileList", fileList);

 return "upload";
}

 

@PostMapping("/upload")
public String post(
   @RequestParam MultipartFile upfile,
   HttpServletRequest request,
 // ※ファイル保存先指定時にHttpServlet型のオブジェクトが必要
 Model model) throws Exception {
 if(upfile.isEmpty()) {
  // ファイル未選択の場合
  return "redirect:/upload";
 } else {

 // ファイルのアップロード処理
 // ①元のファイル名を取得
  String fileName = upfile.getOriginalFilename();
  System.out.println("fileName:" + fileName);

 // ②保存先のパス情報を生成
  File destination = new File( // ※destination 目的地
  request.getServletContext().getRealPath("/uploads") + "/" + fileName);

 // ③保存(一時ファイルパスから移動させる)
  upfile.transferTo(destination);

 // 完了画面に遷移
  model.addAttribute("fileName", fileName);
  return "uploadDone";
 }
}

upload.jsp
アップロードフォームと画像一覧
<form action="" method="post" enctype="multipart/form-data">
 <p>ファイル:
 <input type="file" name="upfile" /></p>
 <input type="submit" value="アップロード" />
</form>

<h2>画像一覧</h2>
 <c:forEach var="file" items="${fileList}">
 <img src="uploads/<c:out value="${file.name}" />" width="100" />
</c:forEach>

 

uploadDone.jsp
完了メッセージと、戻るボタンのみ記述


今日はここまで。
というか今後は制作に入る。

まだスケジュールの見積もりもできないので、簡単なものを動作確認しつつ少しずつ作っていきたい所存

52日目(Spring Framework10)

HibernateORMの設定方法から昨日のおさらい

今日は少し応用

 

  • 1対1(one-to-one)
  • 多対1(many-to-one)

ER図で少し触れた、テーブル同士の対応関係を扱う


one-to-one

それぞれのテーブルに対応したdomainクラスを作成
Typeクラス(親)
 [フィールド]

  • id
  • name
  • TypeDetail typeDetail※紐づけに必要

 

TypeDetailクラス(子)
 [フィールド]

  • id
  • target
  • description

 

マッピングファイルの用意

①Typeクラスのマッピングファイル 

 one-to-oneタグ 

 name属性の値がtypeDetail

 class属性でTypeDetailの完全修飾クラスを記述する

 

②テーブルとTypeDetailクラスを紐づけるためのマッピングファイル
 generatorタグに親クラスの情報が必要
 <generator class="native">
  <param name="property">types</param>
 </generator>
 ※typesは親クラス


many-to-one

membersテーブル(親)

  • id
  • name
  • type_id

member_types(子)

  • id
  • name

domainクラスの作成

Memberクラス

  • id
  • name
  • MemberType memberType※typeIdではなく、指すのはMemberTypeクラス

MemberTypeクラス

  • id
  • name

one-to-one,many-to-oneどちらも親クラス側に子クラスと紐づけるフィールドを用意する

 

many-to-oneの子クラスのマッピングファイルはこれまでに習ったものと同じ

 

親クラスのマッピングファイル
 many-to-oneタグ
 name属性にmemberType(domainクラスのフィールド変数名)
 column="type_id"※これはmemberテーブルのtype_id

 

(補足)データベースからのデータの取得方法としてFetch Typeというものがあり、LAZY、EAGERという種類があるらしいが基本は記述をしない方針らしい
EAGERは紐づくデータをすべて取得できるが処理がそのぶん重くなり、LAZYは指定したテーブルのみ取得するものとのこと

 

要するに

one-to-one,many-to-oneともに

が必要

 

先日作成した会員一覧のページでmany-to-oneを実践

 

domainクラスのMemberType.javaの作成

  • id
  • name

のフィールド、アクセッサを用意

また、先日作成していたMember.java(domainクラス)の修正

フィールドのtypeIdをMembertype membertype;に変更、アクセッサも併せて変更


次はマッピングファイル
既存のhbm/Member.hbm.xmlの修正
 many-to-oneタグを記述し、
 name属性にmemberType(domainのフィールド)
 column="type_id"

 

MemberTypeのxmlも作成
ほぼMember.javaマッピングファイルの流用
idとnameフィールド以外のpropertyタグは削除
マッピングファイルを増やしたので、Bean定義ファイルのmappingResourcesに追記をする

 

MemberDaoの修正
member.setMemberType(newInfo.getMemberType());

 

listMember.jspの修正
<td><c:out value="${member.memberType.name}" /></td>


memberDaoの.createQuery()で使うHQLを修正
from Member m join fetch m.memberType
※mはエイリアスというらしい


個別表示の修正
<form:select path="memberType.id">

 

ここまでで会員一覧と会員の個別表示ができた。

 

HQL文のよく使うもの

テーブルの全データ取得 getResultList()
IDを指定した単一オブジェクト uniqueResult()

 

検索条件のHQL

HQL文のみ
from Member where age >= :age1 and age < :age2
 ※age1、age2はdomainクラスのフィールド

全体の書き方例
List<Member> memberList = getSession()

.createQuery(from Member where age >= :age1 and age < :age2)

.setParameter(":age1", 20).setParameter(":age2", 30).getResultList();

 

LIMITに相当する取得範囲の限定

 

Query<Member> query

= getSesion().createQuery("from Member", Member.class);
query.setFirstResult(0);
query.setMaxResults(3);

List<Member> members = query.getResultList();

 


ここまでがHibernateORMの学習内容で主に触れた部分

あとはページネーションの実装
これは本当に複雑だった。
まだ消化しきれてないのと、ブログに書くには煩雑すぎるので断念
頭の中が整理できたら書く

51日目(Spring Framework9)

Hibernate ORM

データベースとのやり取り
ORM…Object Relational Mapping
データベースの情報とJavaのdomainクラスの情報を対応させる

 

オブジェクトを操作する感覚で、メソッドによってデータを取ってきたりできる
ただ細かいところに手が届かないため、細かい操作に対応したHQL (Hibernate Query Language)という言語も用意されている

 

データベースとオブジェクトの対応関係の指定の方法

マッピングファイル…domainクラスとテーブルを紐づけることを示すxmlファイル

※授業ではバリデーションをアノテーションで行うため、マッピングファイルでの指定を採用

HibernateORM単品で利用もできるが、その場合手順がけっこう多くなる
Springと連携させることで簡略化

 

準備


①ライブラリ追加

②Bean定義ファイル
 名前空間jeetxを追加(名前空間タブでjeeとtxにチェックを入れる)
 データソース指定とHibernateORMの設定を記述

 

以下はBean定義ファイルに記述

・データソース指定
<property name="dataSource" ref="dataSource" />

マッピングファイルの指定
<property name="mappingResources">
<list>
<value>hbm/Member.hbm.xml</value>
</list>
</property>

・利用するデータベースの方式の指定
<property name="hibernateProperties">
<props>
 ★<prop key="hibernate.dialect">org.hibernate.dialect.MySQLInnoDBDialect</prop>
<prop key="hibernate.show_sql">true</prop>出力用
<prop key="hibernate.format_sql">true</prop>改行用
</props>
</property>
MySQLを利用する場合この記述は必須

・Transaction Manager
<bean id="txManager"
class="org.springframework.orm.hibernate5.HibernateTransactionManager">
<property name="sessionFactory" ref="sessionFactory"/>
</bean>

<tx:annotation-driven transaction-manager="txManager"/>

-------------------------
↑ここまでがBean定義ファイルの記述。長い(汗)

 

マッピングファイル
<property name="mappingResources">の部分の、valueタグで指定したものに合わせた場所に作成

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
 <hibernate-mapping package="domain">
  <class name="Member" table="members">
   <id name="id" column="id">
    <generator class="native" />
   </id>
   <property name="name" column="name" type="string" />
   <property name="age" column="age" type="integer" />
   <property name="typeId" column="type_id" type="integer" />
   <property name="created" column="created" type="date" />
  </class>
 </hibernate-mapping>

classタグ
 name属性(というかname属性全部)=domainクラス
 tableがデータベースのテーブル名と対応。

PRIMARY KEYで設定されてるidは特殊な書き方。class="native"で指定。
(nativeにするとDB固有のid生成をしてくれる。AUTO_INCREMENTが有効)

propertyタグ
 name属性はdomainクラスのフィールド名
 column属性はテーブルのカラム名
 type属性はデータ型

 

 

ここまでが下準備。

 

DBのmembersテーブル全体のデータを取得

・DAOを用意する

クラス宣言の前にアノテーション
MemberDao.java(実装例)
@Transactional
@Repository
public class MemberDao {

// フィールドでDI
 @Autowired
 private SessionFactory sessionFactory;

 private Session getSession() {
  return sessionFactory.getCurrentSession();
 }
// 全員のデータを取得
public List<Member> findAll(){
 // HQLの記述 select * from membersに相当
 // ⇒ from Member(SQLを省略したような形、またdomainクラス)
 return getSession().createQuery("from Member", Member.class)
 .getResultList();
 }
}

 

・Controllerクラスの作成

MemberController.java
クラス宣言前に@Controller
以下中身

@Autowired
private MemberDao dao;

@RequestMapping("/listMember")
public String list(Model model) {
List<Member> members = dao.findAll();
model.addAttribute("members", members);
return "listMember";
}

 

・ビューの作成
listMember.jsp
tableで会員情報一覧を表示する
<c:out value="${member.name}" />
などで氏名等を出力


●個別にデータ取得

URIテンプレート変数でidごとに区切る
(id=3なら/member/3みたいな)

・先に作ったlistMember.jspの会員情報一覧のtableに個別表示の枠を追加、IDごとのリンクを作成

<td>
<a href="member/<c:out value="${member.id}" />">
<c:out value="${member.id}" />
</a>
</td>

・MemberController.javaにメソッド追加

@GetMapping("/member/{id}")
public String member(
@PathVariable("id") Integer id,
Model model) {

// idから1人分の情報を取得
※Member member = dao.findById(id);
// 取得した情報をjspで表示
※model.addAttribute(member);
return "member";
}
※ここの実装前に下記のDAOを書いてしまう

・MemberDao.java
// 1人分のデータを取得
public Member findById(Integer id) {
// select * from members where id=Xに相当
return getSession().get(Member.class, id);
}

・個別のビュー
member.jsp
これは普通にmemberのフィールドをcoutで出力するだけ
<h1>個別表示</h1>
<p>氏名:<c:out value="${member.name}" /></p>
<p>年齢:<c:out value="${member.age}" /></p>


●データの追加

.save()メソッドを使う
SQL文を書くことなくデータの追加ができる!

 

・まずは入力フォーム
form:selectでは今回はデータベースから種別を取ってくるのではなくform:optionで用意。
<form:form modelAttribute="member">
 <p>氏名:
  <form:input path="name"/></p>
 <p>年齢:
  <form:input path="age"/></p>
 <p>住所:
  <form:input path="address"/></p>
 <p>会員種別:
  <form:select path="typeId">
   <form:option value="1">通常会員</form:option>>
   <form:option value="2">プレミアム会員</form:option>>
  </form:select>
 </p>
 <input type="submit" value="追加"/>
</form:form>

 

・Controllerクラス
MemberController.java

@GetMapping("/addMember")
public String addMember(Model model) {
   model.addAttribute("member", new Member());
    return "addMember";
  }

@PostMapping("/addMember")
public String addMemberPost(
   @Valid Member member,
   Errors errors) {
  if(errors.hasErrors()) {
   return "addMember";
  } else {
   dao.insert(member);※MemberDaoにinsert()メソッド記述後に追加
   return "redirect:/listMember";
  }
 }

※簡単にバリデーションを実装
Member.javaのフィールドにアノテーションを追加
pom.xmlhibernate.Validatorを追加


・データを追加するメソッドをDAOに記述
MemberDao.java

public void insert(Member member) {
  member.setCreated(new Date());
  getSession().save(member);
 }


編集 update
削除 update

 

個別表示ページに更新ボタンと削除ボタンを追加
 <input type="submit" name="btn" value="更新"/>
 <input type="submit" name="btn" value="削除"/>

 

●MemberDao

// 会員情報を削除するメソッド
public void deleteById(Integer id) {
  Member member = findById(id);
  getSession().delete(member);
 }

// 会員情報を更新するメソッド
public void updateById(Member newInfo, Integer id) {
  // 更新したいIDのデータを取得
  Member member = findById(id);
  member.setName(newInfo.getName());
  member.setAge(newInfo.getAge());
  member.setAddress(newInfo.getAddress());
  member.setTypeId(newInfo.getTypeId());
  getSession().save(member);
 }

 

●MemberController.java
MemberDaoで定義した更新、削除のメソッドを利用する

@PostMapping("/member/{id}")
public String memberPost(
   @PathVariable("id") Integer id,
   @Valid Member member,
   Errors errors,
   @RequestParam String btn) {
 if(btn.equals("更新")) {
   if(errors.hasErrors()) {
    return "member";
   } else {

    // 更新処理
    dao.updateById(member, id);
    return "redirect:/listMember";
   }
 } else {
  // 削除処理
  dao.deleteById(id);
  return "redirect:/listMember";
 }
}

※@GetMappingは記述済

 

今日はここまで

CRUDがわりと簡略化されて嬉しい
ただ、処理の流れを追いきれてない感じがあるので要復習

50日目(Spring Framework8)

昨日のおさらい

hibernate validatorでアノテーションでバリデーションができるようにする


エラー表示

<form:errors />

フィールドエラー、グローバルエラーの表示

フィールドエラー⇒オブジェクトの特定のフィールドのエラー
 例:<form:errors path="register.name" />
グローバルエラー⇒オブジェクト全体のエラー
 例:<form:errors path="register" />


@PostMapping("/register")
public String registerPost(
 @Valid Register register, // @ModelAttribute("register")省略
 Errors errors) {
 if(!register.getConfMail().equals(register.getMail())) {
 // フィールドエラーの追加
 // path="register.confMail"で出力される
  errors.rejectValue("confMail", "register.mail.notsame");
 } ※第一引数がフィールド名、第二引数は任意。あとで使う
 if(errors.hasErrors()) {
 // グローバルエラーの追加
 // path="register"で出力される
  errors.reject("register.imperfect");
※引数は任意。これも後で使う
  return "register";
 } else {
  return "done";
 }
}

errorsオブジェクトのrejectValue()、reject()メソッドで設定した↓

  • register.mail.notsame
  • register.imperfect

これらをプロパティファイル(messages.properties)に追記、それぞれ出力されるエラーメッセージを設定する

書き方例:
register.mail.notsame=メールアドレスが一致しません
register.imperfect=入力に不備があります


バリデーショングループ

特定の場面でバリデーションを行う場合にグループ分けする
(会員登録の際は氏名、ログインID、パスワードが必要だが登録後のログインはログインIDとパスワードの入力のみ、みたいなやつ)

 

①以下のフォームをもつjspを用意

  • 氏名 name
  • ログインID loginId
  • パスワード loginPass
  • パスワード(確認用) loginPassConf

 

②対応したdomainクラスを定義
 フィールドとアクセッサを用意
 かつ、フィールドすべてに@NotBlankをつける
 ただし、ログイン時にはログインIDとパスワードのみ必須にしたい

 

③それを実現するためにマーカーインターフェースを作る
1.AddGroupインタフェースをdomain内に
※なにも中身を記述しない(マーカーインターフェースの特徴)
2.LoginGroupインタフェースをdomain内に
※やはり中身なし

  パッケージはdomainでなくてもいい。

 

④先に定義していたdomainクラスに戻り、

 

@NotBlank(groups= {AddGroup.class})
private String name;
↑登録時(AddGroupにのみ属する)

 

@NotBlank(groups= {AddGroup.class, LoginGroup.class})
private String loginId;
↑登録時、ログイン時(AddGroup、LoginGroup両方に所属)

 

他のフィールドもルールに沿ってグループ分け

 

⑤以上を踏まえてControllerクラスを作る

まずは@GetMappingから用意

登録時のjsp
@GetMapping("/addUser")
public String addGet(Model model) {
 model.addAttribute("user", new User());
 return "userForm";
}
ログイン時のjsp
@GetMapping("/login")
public String loginGet(Model model) {
 model.addAttribute("user", new User());
 return "userForm";
}

2つともアクセスするURLが異なるだけで、表示されるフォームは同じ

 

次にPostMapping
グループ分けした部分をバリデーションできるように記述

@PostMapping("/addUser")
public String addPost(
 @Validated(AddGroup.class) User user,
 Errors errors) {
  if(errors.hasErrors()) {
   return "userForm";
  } else {
   return "done";
  }
 }

@PostMapping("/login")
public String loginPost(
 @Validated(LoginGroup.class) User user,
 Errors errors) {
  if(errors.hasErrors()) {
   return "userForm";
  } else {
   return "done";
  }
 }

エラーがなければ完了ページ(done.jsp)に飛び、そうでなければフォームが表示され続ける。
※今回はエラーメッセージ等は用意せず

 

今日はここまで。

昨日詰まってたところが単なるタイプミスだったことがわかった一日

睡眠って大事、ということを改めて学んだ32歳。

49日目(Spring Framework7)

formタグライブラリのおさらい、補足

 

form部品の装飾

一部書き換えが必要なものがある

  • cssClass
  • cssErrorClass
  • disabled
  • element

id,placeholder等はそのまま利用できる

 

 

<form:radiobutton>の補足

checked="checked"でデフォルトのチェックを入れたいが、これだけだと記述が不十分

三項演算子を使ってnullの場合はチェック、そうでないなら空にする
<form:radiobutton path="ageGroup" value="2" checked="${survey.ageGroup == null ? 'checked' : ''}"/>20代

 

生年月日の受け取り
<form:input path="birthday" type="date"/>
⇒文字列で送られるデータをDate型では受け取れない
SimpleDateFormatを@InitBinderの中で使う

@InitBinder
public void initBinder(WebDataBinder binder) {
  SimpleDateFormat fmt = new SimpleDateFormat("yyyy-MM-dd");
  fmt.setLenient(false);※日時のチェックを厳密にする
  binder.registerCustomEditor(Date.class, new CustomDateEditor(fmt, true));
}
※覚えるというよりは、定型文として書く感じ


Springタグライブラリ

2つの異なる階層のURLに対応する1つのビューがあるような状況


@RequestMapping("/test")
public String test() {
    return "test";
}

@RequestMapping("/test/{id}")
public String testPath(
        @PathVariable("id") Integer id) {
        return "test";
        }

①番は問題ないが、②番からアンケート調査のページに行こうとするとリンク先に飛べない。

解決策として
●コンテキストルートを含めてリンクを記述する
例:
  <p><a href="/validation/contact">お問い合わせ</a></p>
●Springタグライブラリを利用
例:
  <p><a href="<spring:url value="/contact"/>">お問い合わせ</a></p>
※こっちのほうが変更に強いらしい。

 

Springのバリデーション

実際にはHibernate Validatorというライブラリを使う
アノテーションでバリデーションができる

 

利用方法

●ライブラリ追加
 pom.xmlhibernate-validatorを追加する

●domainオブジェクトにバリデーションの内容を記述
 @NotBlankで必須項目にしたり、@Min、@Maxで文字数制限をつけたりできる

●Controllerでバリデーションの結果に応じた処理を記述

●Springのformタグライブラリでエラーメッセージを表示

 

 

ちょっとだけバリデーションの動作確認をして今日は終了。
けっこう難しいので、明日改めて復習する