概要
ServletからJSPへ渡すデータの量や構造が大きい場合、Javaコードそのままだと記述量が大きくなってしまうので、JavaBeansを使って自前のデータ型でやりとりを行う。(いろいろ定義や解説はあるけど、C言語わかる人むけだと「アクセサ付きの構造体」くらいの感覚で"とりあえず"はいいんじゃないかなぁ。。)
また、JSPでJavaBeansへのアクセスや、以前はがっつりとJavaのコードでループ処理を実装していたのを、JSTLのループ処理を使ってすっきり記述する。
話の流れ
- Eclipseを使ってTomcat+JSP+Servlet+MySQLでメモアプリ作成 - Qiita
- [Eclipse/Tomcat] MavenのwebappプロジェクトでServlet+JSP - Qiita
Beanクラスの作成
「クラスの作成」から普通に…
package jp.example.www;
public class MemoBean {
public MemoBean() {
}
private String title;
private String memo;
private String modify;
}
これだけ書いたらEclipseの「getterおよびsetterの生成」でアクセサは作成できる。
なくても動くことは動くんだけど、JavaBeansの仕様的にはSerializable
を実装するのが決まりらしいのでimplements
する。
package jp.example.www;
import java.io.Serializable;
public class MemoBean implements Serializable {
private static final long serialVersionUID = 1L;
public MemoBean() {
}
private String title;
private String memo;
private String modify;
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getMemo() {
return memo;
}
public void setMemo(String memo) {
this.memo = memo;
}
public String getModify() {
return modify;
}
public void setModify(String modify) {
this.modify = modify;
}
}
Beanを使ったサーブレットの修正
HashMap
のArrayList
で処理していた値の保持とJSPへのsetAttribute()
の実装を、上記のBeanクラスに置き換える
ArrayList<HashMap<String, String>> record_list = new ArrayList<>();
while (result.next()) {
HashMap<String, String> record = new HashMap<>();
record.put("title", result.getString("title"));
record.put("memo", result.getString("memo"));
record.put("modified_date", result.getString("modified_date"));
record_list.add(record);
}
ArrayList<MemoBean> memo_list = new ArrayList<>();
String select_memo = "select title, memo, modified_date from memo_data;";
ResultSet result = smt.executeQuery(select_memo);
while (result.next()) {
MemoBean memoBean = new MemoBean();
memoBean.setTitle(result.getString("title"));
memoBean.setMemo(result.getString("memo"));
memoBean.setModify(result.getString("modified_date"));
memo_list.add(memoBean);
}
request.setAttribute("memo_list", memo_list);
JSPでBeanの受け取りとループ処理
JSTL coreタグライブラリを使えるようにする
Mavenを使って、二つのライブラリを取り込む(pom.xml
に<dependency>
の定義を記述する)
JSTLの有効化
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
これでJSPで<c:out>
とか<c:forEach>
とか使えるようになる。
EL式の有効化
<%@ page isELIgnored="false" %>
ignore=true
がデフォルトらしく、これを書かないと${value}
とかの記述が解釈されない
Beanのループ処理
便利なことにArrayList
などのiterator処理は<c:forEach>
が内部でよしなにしてくれるので、BeanクラスのArrayList処理はこれで動く。
(全ArrayList要素の各メンバをprint)
<c:forEach var="data" items="${memo_list}">
<hr/>
<div><c:out value="${data.title}"/></div>
<div><c:out value="${data.modify}"/></div>
<div><c:out value="${data.memo}"/></div>
</c:forEach>
- forEachの
items
属性がループ処理対象で、ServletのsetAttribute()
のkey文字列を指定すれば中身を取り出してくれる - ループ内処理で各要素は
var
で指定した変数名にセットされる - 直接
${data.***}
と書いても出力はされるが、<c:out>
を使うとhtmlタグのエスケープとかしてくれる -
${data.title}
の部分、privateメンバにアクセスしてるように見えてるけどそんなことはなく、getter/setterのgetXxx()
のxxx
の部分に合わせている。
参考
改行の処理
サンプルにしてるアプリの仕様上、${data.memo}
は複数行の文字列データが入っている。そのため、生データをそのままhtml上に出力しても<br/>
がないため改行されない。また、単純に改行を<br/>
に置換しても、<c:out>
の機能でエスケープされてhtmlタグとして認識されない。
ということで、何がベストソリューションかは置いておいて、今回は改行でsplit
して行ごとに出力してみた。
<%@ taglib prefix="fn" uri="http://java.sun.com/jsp/jstl/functions"%>
...snip...
<% pageContext.setAttribute("newLineChar", "\n"); %>
<c:forEach var="data" items="${memo_list}">
<hr/>
<div><c:out value="${data.title}"/></div>
<div><c:out value="${data.modify}"/></div>
<div>
<c:forEach var="line" items="${fn:split(data.memo, newLineChar)}">
<c:out value="${line}"/><br/>
</c:forEach>
</div>
</c:forEach>
split(data_memo, "\n")
は動作しないようなので、事前にnewLineChar
という変数に\n
をセットしている。
参考
- JSTLのfn:replaceで改行を置換したい - こせきの技術日記 - 技術日記
- string - "fn:replace" 正規表現 - JSPとJSTLを使用して改行文字を置き換えるにはどうすればよいですか? - CODE Q&A 問題解決
備考
<c:forEach>
で例外が出る場合
javax.servlet.ServletException: java.lang.NoClassDefFoundError: javax/servlet/jsp/jstl/core/LoopTag
org.apache.jasper.servlet.JspServlet.service(JspServlet.java:349)
javax.servlet.http.HttpServlet.service(HttpServlet.java:729)
org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:52)
jp.example.www.MemoAppMain.doGet(MemoAppMain.java:87)
javax.servlet.http.HttpServlet.service(HttpServlet.java:622)
javax.servlet.http.HttpServlet.service(HttpServlet.java:729)
org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:52)
taglibs-standard-impl
は設定されているけどjstl
が設定されてないとこの例外がでる。
https://mvnrepository.com/artifact/javax.servlet/jstl/1.2
<jsp:useBean>
について
JSPに
<jsp:useBean type= "java.util.ArrayList<jp.example.www.MemoBean>" id="memo_list" scope="request" />
みたいな記述をする説明は多く見かけるけど、なくても動く。よくわからない(汗)