Java Web

HTML CRUD サーブレットまとめ jQuery Spring Boot

スッキリわかるサーブレット&JSP入門  Web付録 サンプルコード

Webアプリケーション

  1. ブラウザからの要求・入力を受ける(リクエスト)
  2. Javaでデータベースにアクセスし必要なデータをそろえる
  3. JavaでHTMLを生成しブラウザに返す(レスポンス)

Eclipseの操作

プロジェクトの作成

  1. メニューから[ファイル]-[新規]-[動的Webプロジェクト]
  2. 「プロジェクト名」を入力(例:example)
  3. 「ターゲットランタイム」を「Tomcat9(Java17)」にして完了。

フォルダ

src配下のmainのwebapp

HTMLファイル、CSSファイル、JSPファイルを配置。

WEB-INF以下はユーザは直接アクセスできない。

/src/main/java

Javaプログラムを配置。パッケージを作成して配置する(例:「servlet」など)。

パッケージを右クリックし[新規]-[サーブレット]で「クラス名」を入力(例:「HelloServlet」など)。

HTMLエディタ設定

HTMLファイルを右クリックし、[次で開く]-[その他]から「JSPエディタ」を選び、下にある「すべての'*.html'ファイルで使用する」をチェックしてOKを押す。

オートリロード設定

「サーバ」の「Tomcat9_Java17」をダブルクリックし、「モジュール」のタブを選び、プロジェクトを選択、編集ボタンを押して「自動再ロード使用可能」をチェックしてOKを押す。

スニペット

コードの断片を素早く挿入できる機能。表示されていない場合には[ウィンドウ]-[ビューの表示]の「その他」から「スニペット」を選択し、表示。

サーブレット開発用スニペット

スニペットの設定

  1. Eclipseでスニペットをクリック
  2. 下の空いている場所で右クリックし[カスタマイズ]を選択
  3. インポートボタンを押し、ダウンロードしたXMLファイルを指定する(1回1ファイル)

スニペットの利用

ダブルクリックするとカーソル位置に挿入されます。

パラメータがあるスニペットは値を設定して挿入します。

データの流れ

HTMLのみの場合


プログラムを実行する場合


サーブレット

URL

http://localhost:8080/プロジェクト名/URLパターン

URLパターンはサーブレットクラスの上に付けられているアノテーションで設定

@WebServlet("/index")

メソッド

サーブレット内でのHTML出力

response.setContentType("text/html; charset=UTF-8");
PrintWriter out = response.getWriter();

out.println("<!DOCTYPE html>");
out.println("<html>");
out.println("<head>");
out.println("<meta charset=\"utf-8\">");
out.println("<title>Hello</title>");
out.println("</head>");
out.println("<body>");
out.println("<p>Hello World</p>");
out.println("</body>");
out.println("</html>");

サーブレット内でのDB利用とHTML出力

ShouhinDAO dao = new ShouhinDAO();
Shouhin s = dao.findBySid(1);

response.setContentType("text/html; charset=UTF-8");
PrintWriter out = response.getWriter();

out.println("<html>");
out.println("<head>");
out.println("<title>商品</title>");
out.println("</head>");
out.println("<body>");
out.println("<p>商品名:"+s.getSname()+"</p>");
out.println("</body>");
out.println("</html>");

フォワード

jspファイルを開く(URLは変わらない)

RequestDispatcher rd = request.getRequestDispatcher("WEB-INF/jsp/sample.jsp");
rd.forward(request, response);

リダイレクト

別アドレスに飛ぶ(URLは変わる)

response.sendRedirect("index");

リクエストスコープ設定

JSPにデータを渡す

request.setAttribute("slist", list);

フォーム

フォームタグ

<form method="GETまたはPOST" action="送信先">
	<input type="text" name="名前">
	<input type="submit" value="送信">
</form>

※送信先はURL。例:form

フォームからのデータ受け取り

サーブレット内のdoGetやdoPostでフォームから送られたデータを受け取るには request.getParameter(名前) を行い、戻り値の文字列で受け取る。名前はinputタグのname属性に設定されたもの。

request.setCharacterEncoding("UTF-8");

// 文字列の受け取り
String name = request.getParameter("name");

// 数値の受け取り(文字で受け取るので数値に変換)
int age = Integer.parseInt(request.getParameter("age"));

※その名前が存在しない場合、nullになる。radio、checkboxの場合、未選択時にはnull。

複数の値を配列で受け取る

同じnameを設定した場合、配列で受け取ることができる。

request.setCharacterEncoding("UTF-8");

// 文字列配列の受け取り
String[] names = request.getParameterValues("name");

※数値の場合、まず文字列配列で受け取って1つずつ取り出したときに数値に変換する。

GETとPOSTの違い

GETPOST
データの渡し方URLに付加ユーザには見えない(リクエストボディで送る)
サイズ制限小さい(8KB程度)大きい
ブラウザ履歴入力内容が残る入力内容が残らない
入力の再現・共有URLで可能再入力しない限り不可
サーバデータの書き換え向かない向いている
用途検索、閲覧書き込み、削除、ログイン、アップロード

別ページに値を渡す方法

hiddenを使う

formタグ内にinputタグでtypeをhiddenにすることでユーザには見えずに値を渡すことが出来る。。

<input type="hidden" name="名前" value="送信値">

リンクで渡す

フォームでGET送信するときと同様のアドレスをリンクで指定すればよい。

<a href="shouhin?sid=1">リンク文字列</a>

フォームとデータベースの例

フォームから送信された主キー(sid)でデータベースを検索し表示

request.setCharacterEncoding("UTF-8");

int sid = Integer.parseInt(request.getParameter("sid"));

ShouhinDAO dao = new ShouhinDAO();
Shouhin s = dao.findBySid(sid);

response.setContentType("text/html; charset=UTF-8");
PrintWriter out = response.getWriter();

out.println("<html>");
out.println("<head>");
out.println("<title>商品</title>");
out.println("</head>");
out.println("<body>");
out.println("<p>商品名:"+s.getSname()+"</p>");
out.println("</body>");
out.println("</html>");

JSP

JavaプログラムやEL式をHTML内に書くことが出来る。

<% Javaプログラム %>
<%= 式 %>	式の値の表示
<%-- コメント --%>	コメント

ディレクティブ

<%@ page import="java.util.Date" %>	インポート

<%@ include file="ファイル名" %>	静的インクルード

静的インクルード

共通部分

例:header.jspで以下を作成する

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<header>
<h1>Sampleサイト</h1>
</header>

読み込む側

読み込むところで以下を書く。

<%@ include file="header.jsp" %>

MVC

名前役割クラス
Modelデータ一般的クラス(JavaBeans、DAO/DTOなど)
View表示JSP(+CSS)
Controllerデータと表示の橋渡しサーブレット

Webサーバにアクセスがあった場合、まずサーブレットが実行され、モデルのクラスを利用した後、画面表示のためにJSPを呼び出すようにする。

MVCの流れ

  1. サーブレットが呼び出される。必要があればフォームからの送信データ取得。
  2. モデルクラスの利用(データベースの処理など)
  3. モデルクラス(JavaBeans)をリクエストスコープでJSPに渡す
  4. JSPの呼び出し(フォワード)

// フォームからの送信データ取得
request.setCharacterEncoding("UTF-8");
int sid = Integer.parseInt(request.getParameter("sid"));

// モデルクラスの利用
ShouhinDAO dao = new ShouhinDAO();
Shouhin s = dao.findBySid(sid);

// モデルクラスをリクエストスコープでJSPに渡す
request.setAttribute("shouhin", s);

// JSPの呼び出し(フォワード)
RequestDispatcher rd = request.getRequestDispatcher("WEB-INF/jsp/shouhin.jsp");
rd.forward(request, response);

JavaBeans

JSP呼び出し時にデータを渡すために使うクラス。データの入れ物。
カプセル化されて、setter/getterがあり、引数なしのコンストラクタがあるクラス。

EL式

JavaBeansがJSPに渡されてきたとき、簡単に表示できる。

サーブレット内でリクエストスコープをセット

String name = "田中";
request.setAttribute("name", name);
JSP内での表示
${name}

クラスの場合

サーブレット内で渡すとき
Shouhin s = new Shouhin(1,"りんご",100);
request.setAttribute("shouhin", s);
JSP内での表示(getterとしてgetSname() があるとき)
${shouhin.sname}

EL式内でJavaの計算式を書くことも可能

${shouhin.tanka + 100}

三項演算子(式 ? 式がtrueのとき:式がfalseのとき)を使うとif文的なこともできる。

${shouhin.tanka >= 200 ? "高額":"低額"}

Map

EL式内でHapMapを参照する(mapという名前の場合)。以下のどちらでも良い

${map.キー名}

${map['キー名']}

SELECTで初期値を指定する例

<select name="seibetu">
	<option value="1"  ${shouhin.cid == 1 ? "selected" : ""}>果物</option>
	<option value="2"  ${shouhin.cid == 2 ? "selected" : ""}>野菜</option>
</select>

JSTL

JSP内でforEachなどが書ける

準備

<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>

forEach

リストから変数名に1つずつ値を取り出す

<c:forEach var="変数名" items="${リスト}" >
	// 変数名を使ったEL式
</c:forEach>

例:サーブレット内で、ShouhinクラスのArrayListオブジェクトが list の名前で渡されたとき。

<c:forEach var="s" items="${list}">
	<p>${s.sname}</p>
</c:forEach>

テーブルの例

<table>
<tr><th>商品ID</th><th>商品名</th><th>単価</th></tr>
<c:forEach var="s" items="${list}">
	<tr>
	<td>${s.sid}</td>
	<td>${s.sname}</td>
	<td>${s.tanka}</td>
	</tr>
</c:forEach>
</table>

繰り返し番号を1から数えたい場合、varStatusで指定した名前の.countで出すことができる。

<c:forEach var="s" items="${list}" varStatus="status" >
	${status.count}番 ${s.sname}
</c:forEach>

varStatusにはcountの他にindex(0からの回数)やfirst、last(真偽値)などがある。

begin、endを使うとJavaのfor文と同様に数値を増やしていくことが出来る。

<select name="month">
	<c:forEach begin="1" end="12" var="tuki">
		<option>${tuki}</option>
	</c:forEach>
</select>月

画面表示

c:outタグで、valueに値を指定。XSS対策としてHTMLタグなどがそのまま表示されないように特殊文字に変換(エスケープ)して画面に出力。

<c:out value="${shouhin.sname}" />

値のセット

スコープへの値の設定

リクエストスコープ name に shouhin.sname を設定

<c:set var="name" value="${shouhin.sname}" />

セッションスコープ name に shouhin.sname を設定

<c:set var="name" value="${shouhin.sname}" scope="session" />

条件分岐

c:ifタグのtest属性でtrueのときだけ表示

<c:if test="${shouhin.tanka>=200}" >
	高額
</c:if>

c:ifタグのtest属性でオブジェクトがnullのときのみ表示

<c:if test="${empty shouhin}" >
	// shouhinがnullのときの表示
</c:if>

c:ifタグのtest属性でオブジェクトがnullではないときのみ表示

<c:if test="${not empty shouhin}" >
	// shouhinがあるときの表示
</c:if>

c:chooseタグでtrueのときとfalseの処理で分ける。

c:whenタグのtest属性でtrueのときに表示、c:otherwiseでfalseのときの表示を書く。

<c:choose>
	<c:when test="${shouhin.tanka>=200}" >
		高額
	</c:when>
	<c:otherwise>
		低額
	</c:otherwise>
</c:choose>

日付、数値の書式

JSTL I18N :JSP内でEL式の日付などをフォーマットできる

準備

<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt"%>

日付フォーマット

yyyy
MM
dd
E曜日
HH
mm
ss
<fmt:formatDate value="${u.hi}" pattern="yyyy年MM月dd日" />

数値フォーマット

#数値(0の場合非表示)
0数字(0の場合0で表示)
カンマ(,)桁区切り

HH 時 mm 分 ss 秒

<fmt:formatNumber value="${u.kosu}" pattern="###,###" />

セッション

セッションの設定

HttpSession session = request.getSession();
session.setAttribute("member", member);

セッションの取得

HttpSession session = request.getSession();
Member member = (Member)session.getAttribute("member");

その名前のデータが無い場合、nullになる

セッションの削除

HttpSession session = request.getSession();
session.removeAttribute("member");

その名前のデータが無い場合、nullになる

セッションの破棄

HttpSession session = request.getSession();
session.invalidate();

JSP(JSTL)でのセッション

セッションスコープ name を表示する

${sessionScope.name}

セッションスコープ name を判定する(リクエストスコープと同じ)

<c:if test="${not empty member}" >
	// ログイン時の処理
</c:if>

フラッシュメッセージ

セッションを使って一度だけ表示したいメッセージを実現できる

// サーブレット内
HttpSession session = request.getSession();
session.setAttribute("message", "ログインに失敗しました。");

response.sendRedirect("login");

login.jsp

※c:ifを使うのでtaglibを忘れないこと。

<c:if test="${not empty message}">
  <p>エラー:${message}</p>
  <c:remove var="message" scope="session"/>
</c:if>

情報取得

// 自分のURI
// 例  /example/update
request.getRequestURI()

// 自分のURL
// 例 http://localhost:8080/example/update
request.getRequestURL()

// リクエストパラメータ
// 例 sid=1
request.getQueryString()

// 呼び出し元のURL取得
request.getHeader("REFERER")

// WebContentの実際のパス
getServletContext().getRealPath("相対パス");

ファイルアップロード

※サンプル画像 image.zip

例:FileupServlet(URLは /fileup)でdoGetでは fileup.jsp で画像アップフォームを表示する。フォームは/fileupにPOSTする。リダイレクトで /fileupに戻る。

ファイルアップロード用フォーム

formタグは必ずpostで、enctype="multipart/form-data"を付ける。
ファイルをアップロードする部品はtype="file"
acceptでファイルの種類を指定する(省略時は全てのファイル)

例:fileup.jsp

<form action="fileup" method="post" enctype="multipart/form-data">
写真:<input type="file" name="pict" accept="image/jpeg"><br>
<input type="submit" value="送信">
</form>

※この例ではtype="file"のみだが、他にテキストボックスなどを通常通りに設置しても良い。

サーブレット

サーブレットのクラスに@MultipartConfigを付ける

Partクラスでアップロードされたファイルの情報を取得する。ファイルは既にサーバの規定の場所にあるので、それを自分の望む位置に移動する。

@WebServlet("/fileup")
@MultipartConfig
public class FileupServlet extends HttpServlet {
	// 途中省略
	protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		RequestDispatcher rd = request.getRequestDispatcher("WEB-INF/jsp/fileup.jsp");
		rd.forward(request, response);
	}
	
	protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		
		// Partクラス取得(name属性を指定する)
		Part part=request.getPart("pict");

		if(part.getSize()==0) {
			// ファイルがアップされていないときの処理
			return;
		}

		// ファイルの移動先(この例ではimageフォルダ。あらかじめwebappに作成)
		String path = getServletContext().getRealPath("/image");
        
		// フォルダがなければ作成
	    File upFolder = new File(path);
	    if (!upFolder.exists()) {
	        upFolder.mkdir();
	    }
        // アップするファイル名。
        String fileName = "up.jpg";

		// ファイルの移動
		part.write(path + File.separator + fileName);

		// リダイレクト
		response.sendRedirect("fileup");
	}
}

ファイルの表示

上記の例の場合、画像ファイルは以下で表示される。

<img src="image/up.jpg">

なお、Eclipse上にはアップされた画像は反映されない。動いたサーバ上に反映される(workspace\.metadata\.plugins\org.eclipse.wst.server.core\tmp0\wtpwebapps\プロジェクト名)。

ファイル名について

  • 方法1:アップロードした名前をそのまま使う。これは重複する可能性があるので、アップロード時に既存ファイルが上書きされる可能性がある。
    String fileName = part.getSubmittedFileName();
    
  • 方法2:重複しない名前を付けてその名前をDBに保存。例えば、以下のUUIDを使用する。ただし、100%重複しないとは言えない(限りなくゼロに近い。270京回に0.5回)。
    String fileName = UUID.randomUUID() + ".jpg";
    // ed14621c-a9d9-4eee-b0a4-ffbd0fa99a1f.jpgのような名前になる
    
    
  • 方法3:主キーに関連づけて保存。主キーが1なら、1.jpg など。この場合、DBにファイル名を格納する必要は無い。
  • String fileName = sid + ".jpg";
    

ファイルが無い場合の対策

  1. 商品画像が無い場合にはNoImageの画像を表示する。あるいは商品追加時にNoImageの画像を自動設定する。
  2. 画像が存在しない場合にはimgタグを表示しない。
    ※JavaScriptでの対策例
    <img src="画像パス" onerror="this.onerror = null; this.src='';">