Eclipseを使ってTomcat+JSP+Servlet+MySQLでメモアプリ作成

2018.12.16 補足: 本記事では「Tomcatプロジェクト」を使って作成してますが、あとあとを考えると「Mavenプロジェクトで`maven-archetype-webapp`を指定」して作るのがよいです。 (あるいは「動的Webプロジェクト」で)

参考

- [EclipseでサーバサイドJavaを作るときのプロジェクトとwarファイルのエクスポート](https://qiita.com/zaki-lknr/items/a2e76986b6e473964800)
- [[Eclipse/Tomcat] MavenのwebappプロジェクトでServlet+JSP](https://qiita.com/zaki-lknr/items/8137ac40ebd8f5bdb3c5)

---

何番煎じかわからない内容だけど、社内の入門向けJava勉強会で少しかじったのでその復習＆予習に。
(あと、ちょっとした環境構築後にwebアプリ+DBなアプリケーションを動かすときのネタがあればいろいろはかどるかな、と)

- 開発環境はPleiades Java Full Edition
- MySQLは別途構築 -> [MySQL 8.0をWindows10へzipでインストール - Qiita](https://qiita.com/zaki-lknr/items/66990be2548d08a5677b)
- Java-MySQLの接続はMavenを使ってJDBCをインストール

各バージョン

- Pleiades: Eclipse 4.6 Neon 3 Java Full Edition
- Java 1.8
- Tomcat 8.0.x
- MySQL 8.0.13
- MySQL Connector/J: 8.0.13

今どきならいろいろなフレームワークを使って開発するものだけど、プリミティブなところでどんな実装や処理をするのかを見てみたいので、ここでは素のJSP+Servletでやってます。

# Tomcatプロジェクト作成

`Java | Tomcatプロジェクト`を作成

![image.png](https://qiita-image-store.s3.amazonaws.com/0/256859/c7f4556d-4786-68d2-4829-4f007a388d17.png)

プロジェクト名は`memoapp`

![image.png](https://qiita-image-store.s3.amazonaws.com/0/256859/4975af24-2cba-a831-d3a2-f89325e6d7ce.png)

コンテキスト名は(デフォルトの)`/memoapp`
※ コンテキスト名はwebアクセス時のURLに使用される

![image.png](https://qiita-image-store.s3.amazonaws.com/0/256859/b2aec427-77ae-8ad5-745d-04c040cd8d50.png)

これでプロジェクトが作成される

![image.png](https://qiita-image-store.s3.amazonaws.com/0/256859/e91de2f2-a520-1909-9784-214915def5cb.png)

# メイン画面のUIと処理部分を作成する

## JSPファイルの作成

UI部分のJSPファイルを作成する。
Eclipseの新規作成メニューからJSPファイルを選択。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/256859/226c2d7e-b2b6-775c-9b31-4b8419aa67d5.png)

作成フォルダは`WEB-INF/jsp`を選択(なければ手動入力すれば作成される)
ファイル名はわかりやすく。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/256859/579521fa-58bd-02bf-6356-768dd10135c7.png)

テンプレートは「新規JSPファイル(html)」を選択

![image.png](https://qiita-image-store.s3.amazonaws.com/0/256859/81e4945c-0597-797e-1244-c70cc1e8bc6f.png)

これでJSPファイルが作成される

![image.png](https://qiita-image-store.s3.amazonaws.com/0/256859/af332022-4d22-0ff5-9d19-476e4a9c9a85.png)

`<title>`を編集して`<h1>`でページタイトルを追加

```html:index.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
 pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Memo App</title>
</head>
<body>

<h1>memo app</h1>

</body>
</html>
```

## サーブレットの作成

作成したJSPとデータをやり取りする処理を行うサーブレットのクラスを作成する。
※ 新規作成メニューに「サーブレット」があるが、「動的Webプロジェクト」で作成したプロジェクトだけが対象っぽくて使用できないので、普通にJavaクラスの作成で作る。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/256859/65d3a365-80d8-a5d6-4cbf-980d36f565ae.png)

自動生成時のソースはクラス定義のみでコンストラクタとかも何もないけど、以下のように`doGet()`を定義する。(GETリクエストがあった際に呼ばれるメソッドを作成)

```java:MemoAppMain.java
package jp.example.org;

import java.io.IOException;

import javax.servlet.RequestDispatcher;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

@WebServlet("/")
public class MemoAppMain extends HttpServlet {

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp)
            throws ServletException, IOException {

        String view = "/WEB-INF/jsp/index.jsp";
        RequestDispatcher dispatcher = req.getRequestDispatcher(view);
        dispatcher.forward(req, resp);
    }

}
```

`doGet()`では、`/WEB-INF/jsp/index.jsp`にあるJSPファイルを読み込み、フォワード処理を使ってJSPの内容をブラウザで表示させる。

なお、Servletの画面遷移にはフォワードとリダイレクトがあるそうな: [リダイレクトとフォワードの違いを知る：JavaTips ～JSP／サーブレット編 - ＠IT](http://www.atmarkit.co.jp/ait/articles/0407/06/news077.html)

これでTomcatを起動し、「コンテキスト定義を更新」すると、ブラウザからアクセスしたときにJSPの内容が表示されるはず。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/256859/1e86f2c8-b5d9-f36a-1581-6ad09d6af84a.png)


![image.png](https://qiita-image-store.s3.amazonaws.com/0/256859/d32ec827-249d-6fa8-da73-930813a2dbae.png)


# メモの入力処理の作成

## JSPで入力フォーム作成

これは普通にhtmlのformタグで。
ざっくりとこんな感じで。

```html:index.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
 pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Memo App</title>
</head>
<body>

 <h1>memo app</h1>

 <form method="post">
  <input type="text" name="title" size="50"/>
  <br/>
  <textarea rows="5" cols="80" name="memo"></textarea>
  <br/>
  <input type="submit"/>
 </form>

</body>
</html>
```

通常だとformタグには`action`属性で、入力データの送信先を記述するが、今回は自分自身へpostする作りにするため省略

![image.png](https://qiita-image-store.s3.amazonaws.com/0/256859/3676509a-2a1d-c22a-f753-eba6a961a900.png)

表示すると(なんの装飾もないため)こんな感じ。
(Bootstrapとかでなんとかしたいですね…)

## Servletでpostされたデータを受け取る

JSPで記述したformにactionを書かなかったので、post先は自分自身になる。
つまり、すでに作成済みの`MemoAppMain`クラス内に、POSTの受け取り処理を作ればよい。

`MemoAppMain`には作成済みのGETリクエストを処理する`doGet()`があるが、これと同じようにPOSTリクエストを処理する`doPost()`を作成する。

とりあえずこんな感じ

```java:MemoAppMain.java
    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp)
            throws ServletException, IOException {
        req.setCharacterEncoding("UTF-8");
        System.out.println("title: " + req.getParameter("title"));
        System.out.println("memo: " + req.getParameter("memo"));

        // -- ここにDBへ保存処理 --

        resp.sendRedirect(".");
    }
```

このメソッドは、POSTリクエストがあるとコールされる。
POST時のパラメータ(HTTPリクエスト)は、名前の通り`req`オブジェクトにセットされており、`getParameter(String)`でアクセスできる。
この`getParameter(String)`で指定する文字列の"title"と"memo"は、html formのフォーム名の文字列。

今はまだPOSTされたテキストデータにアクセスしているだけなので、これをDBに保存する処理を作成する。
`setCharacterEncoding("UTF-8");`は文字化け対策。
参考： [日本語パラメータの対応(setCharacterEncoding) - リクエスト情報の取得 - サーブレット入門](https://www.javadrive.jp/servlet/request/index4.html)

保存処理が終わったら画面を自分へリダイレクトするために`sendRedirect(".");`をコールして、再読み込みしている。

Todo: Tomcatの起動・終了と、この時点でのアプリの動作を説明してない

# DB接続処理

MySQLを使用します。

フォームに入力されたデータをDB保存するための処理を作成する。
その前に、まずはJavaアプリケーションからDB操作を行うための、JDBCのセットアップを行う。

JDBCの設定(JDBCドライバのアプリケーションへの組み込み)は手動でもできるけど、ここではMavenを使って導入する。

Mavenは…ググってね♡

## TomcatプロジェクトをMavenに変換する

プロジェクトの右クリックメニューから[構成]->[Mavenプロジェクトへ変換]を選択

![image.png](https://qiita-image-store.s3.amazonaws.com/0/256859/5da2e789-454f-7c08-f5a6-375cd5ff4d6d.png)

新規POMの作成ダイアログが表示される。内容はデフォルトのままで[完了]でOK

![image.png](https://qiita-image-store.s3.amazonaws.com/0/256859/4d470ffe-2cf4-5bf9-9ceb-f16003866ba2.png)

すると、pom.xmlファイル(と出力用`target`ディレクトリ)が生成される。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/256859/15e03c2a-0473-ef37-98c2-75edb4921551.png)

```xml:pom.xml
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>
  <groupId>memoapp</groupId>
  <artifactId>memoapp</artifactId>
  <version>0.0.1-SNAPSHOT</version>
  <build>
    <sourceDirectory>WEB-INF/src</sourceDirectory>
    <plugins>
      <plugin>
        <artifactId>maven-compiler-plugin</artifactId>
        <version>3.5.1</version>
        <configuration>
          <source/>
          <target/>
        </configuration>
      </plugin>
    </plugins>
  </build>
</project>
```

また、Mavenプロジェクトへ変換すると、JREのバージョンやビルド先など、いくつかの設定が書き換えられるので修正する

### 出力フォルダの修正

プロジェクトのプロパティの[Javaのビルドパス]の[ソース]タブで、「デフォルト出力フォルダー」が[<projectname>/target/classes]になっているので、[参照]ボタン押下して[<projectname>/WEB-INF/classes]に変更する

![image.png](https://qiita-image-store.s3.amazonaws.com/0/256859/50e67781-b8b6-630c-4d8e-b8c37cd492fe.png)

![image.png](https://qiita-image-store.s3.amazonaws.com/0/256859/de36caa2-931b-9d40-b877-18bf6faed966.png)

デフォルト値を変更したら、[ビルド・パス上のソース・フォルダー]のリストから<projectname>/WEB-INF/srcの出力フォルダーを選択して[編集]を押下

![image.png](https://qiita-image-store.s3.amazonaws.com/0/256859/f92b79e7-918d-e7d6-52d8-f9b4f1ee1ef1.png)

出力フォルダの設定を「プロジェクトのデフォルト出力フォルダー」に設定

![image.png](https://qiita-image-store.s3.amazonaws.com/0/256859/06ecca54-a373-9ce5-59d5-db2a6b60922a.png)

### JREの変更

![image.png](https://qiita-image-store.s3.amazonaws.com/0/256859/20f03aad-cd1a-4c83-0efe-ceb3d33af0b6.png)

![image.png](https://qiita-image-store.s3.amazonaws.com/0/256859/c05e767e-994d-1cb3-1d91-abe753b31cfb.png)



設定を更新すると、変更前のファイルの削除確認がされるので、[はい]で削除する

![image.png](https://qiita-image-store.s3.amazonaws.com/0/256859/4b780489-7b0a-e129-6ce8-c6370af42216.png)

もし`work`以下に見覚えのないファイルが生成された場合は、それも削除してよい

## JDBCの導入

Mavenリポジトリにブラウザでアクセスし、必要なファイルを探す。

[Maven Repository: Search/Browse/Explore](https://mvnrepository.com/)

![image.png](https://qiita-image-store.s3.amazonaws.com/0/256859/92a95695-0c0a-5c2a-0ee4-6e3b53a08d03.png)

今回は`mysql jdbc`で検索。
すると、[MySQL Connector/J]がヒットするので[リンクを開く](https://mvnrepository.com/artifact/mysql/mysql-connector-java)
https://mvnrepository.com/artifact/mysql/mysql-connector-java

![image.png](https://qiita-image-store.s3.amazonaws.com/0/256859/1ef62de6-4eab-c14c-a7b8-fcb05d62eee9.png)

リンクを開くとバージョン一覧が表示されるので、基本的にはMySQLのバージョンと同じものを選択する。今回は、MySQL 8.0.13を使用しているので、同じバージョンを選択。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/256859/ac7743d5-a7b5-e7f3-3ca8-ec8cacba735f.png)

リンク先にソースの記述例が記載されている。この[Maven]タブで表示されている内容を、プロジェクトの`pom.xml`ファイル内の`<dependencies>～</dependencies>`の中へ転記する。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/256859/df60c8b0-e716-3540-7ca3-3b638a25d166.png)

ただし、Mavenプロジェクトへの変換直後など、初期状態では`<dependencies>`タグが存在しないので、(手動で記述するか)依存関係タブからUIで入力する。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/256859/e6129a1b-f5ad-2030-a8a8-7f3ac0c5e657.png)

依存関係の選択ダイアログに以下の通りにMavenのサイトで表示されていた値を入力して[OK]押下する

![image.png](https://qiita-image-store.s3.amazonaws.com/0/256859/f2338987-48cb-25b7-b9e3-8e93ab7eb9ea.png)

と、mysql-connector-javaが追加されるので、ファイルを保存する。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/256859/ed466118-c916-3fa5-8ece-c7578f880aa5.png)

保存すると同時に必要ファイル類のダウンロードとビルドが開始され、ビルド完了するとプロジェクト構成も「Maven依存関係」などのファイルの追加などが行われる

![image.png](https://qiita-image-store.s3.amazonaws.com/0/256859/e583e4e5-4326-1d4a-8c72-b59f54de1205.png)

この時点でpom.xmlの内容はこんな感じ

```xml:pom.xml
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>
  <groupId>memoapp</groupId>
  <artifactId>memoapp</artifactId>
  <version>0.0.1-SNAPSHOT</version>
  <build>
    <sourceDirectory>WEB-INF/src</sourceDirectory>
    <plugins>
      <plugin>
        <artifactId>maven-compiler-plugin</artifactId>
        <version>3.5.1</version>
        <configuration>
          <source/>
          <target/>
        </configuration>
      </plugin>
    </plugins>
  </build>
  <dependencies>
   <dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <version>8.0.13</version>
   </dependency>
  </dependencies>
</project>
```

## データベースとユーザの作成

アプリケーションで使用するDBとユーザを作成する。
これはTomcatやJava関係なく、MySQL上で実施する。

```sql
C:\Users\zaki>mysql -u root -p
Enter password: ********
Welcome to the MySQL monitor.  Commands end with ; or \g.
Your MySQL connection id is 9
Server version: 8.0.13 MySQL Community Server - GPL

Copyright (c) 2000, 2018, Oracle and/or its affiliates. All rights reserved.

Oracle is a registered trademark of Oracle Corporation and/or its
affiliates. Other names may be trademarks of their respective
owners.

Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.

mysql>
mysql> create database memoapp_db;
Query OK, 1 row affected (0.06 sec)

mysql> create user memoapp identified by "memoapp";
Query OK, 0 rows affected (0.13 sec)

mysql> grant all on memoapp_db.* to memoapp;
Query OK, 0 rows affected (0.03 sec)

mysql>
```

## JavaからDB接続

```java
        // -- ここにDBへ保存処理 --
        Connection con = null;
        Statement smt = null;

        String url = "jdbc:mysql://localhost:3306/memoapp_db";
        String user = "memoapp";
        String pass = "memoapp";
        try {
            con = DriverManager.getConnection(url, user, pass);
            smt = con.createStatement();
            System.out.println("smt: " + smt);
        } catch (SQLException e) {
            e.printStackTrace();
        }
        finally {
            try {
                con.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
        // -- ここまでDB処理 --
```

これでformからデータ登録しようとしても例外が発生する

```
java.sql.SQLException: No suitable driver found for jdbc:mysql://localhost:3306/memoapp_db
	at java.sql.DriverManager.getConnection(DriverManager.java:689)
	at java.sql.DriverManager.getConnection(DriverManager.java:247)
	at jp.example.org.MemoAppMain.doPost(MemoAppMain.java:44)
	at javax.servlet.http.HttpServlet.service(HttpServlet.java:648)
	at javax.servlet.http.HttpServlet.service(HttpServlet.java:729)
:
:
```

これは、Eclipse上でのビルドのタイミングではMavenで導入したJDBCのjarファイルを参照できているため問題ないが、Tomcatでの実行時にはJDBCのjarファイルのパスは設定されないため。

プロジェクトのプロパティでTomcatの設定の「開発用クラスローダーのクラスパス」画面で[Use SMART! Maven classpath]にチェックすれば、Mavenのライブラリ類もTomcat実行時に参照してくれる……？

![image.png](https://qiita-image-store.s3.amazonaws.com/0/256859/f8590167-913f-b972-cf6b-0465e5d1f8ad.png)

と思ったんだけど、Tomcatがエラーこいてmemoappを動作してくれない

```
重大: Begin event threw exception [土 12 01 13:38:19 JST 2018]
重大: Parse error in context.xml for /memoapp [土 12 01 13:38:19 JST 2018]
重大: 2行の126列目で発生しました [土 12 01 13:38:19 JST 2018]
重大: 前のエラーのためにこのアプリケーションは利用できないようにマークします [土 12 01 13:38:19 JST 2018]
重大: One or more components marked the context as not correctly configured [土 12 01 13:38:19 JST 2018]
重大: 以前のエラーのためにコンテキストの起動が失敗しました [/memoapp] [土 12 01 13:38:19 JST 2018]
```

ちなみにこの設定を行ってコンテキスト定義を更新するとmemoapp.xmlはこんな感じ

```xml:memoapp.xml
<Context path="/memoapp" reloadable="true" docBase="C:\Users\zaki\src\2018b-java-study\memoapp" workDir="C:\Users\zaki\src\java-study\memoapp\work" >
	<Loader className="org.apache.catalina.loader.DevLoader" reloadable="true" debug="1" useSystemClassLoaderAsParent="false" />
</Context>
```

調べてみると「Tomcatプラグインに含まれる`DevLoader.zip`ファイルをTOMCAT_HOME/server/libへjarにリネームしてコピーする」という情報が散見される

- [[eclipse]DevLoaderの罠: ひなぽの技術メモ](http://hinapotech.cocolog-nifty.com/blog/2008/10/eclipsedevloade.html)
- [Mavenプロジェクトのデバッグ - 鳴兔の飼育日誌](http://d.hatena.ne.jp/Ochotona/20060608/1149732351)
- [EclipseでTomcatが動かない!! - QA@IT](https://qa.atmarkit.co.jp/q/283/)

2018.12時点でのEclipse Tomcat Plugin 9.13では、DevLoader.zipファイルは直接含まれていなくて、`net.sf.eclipse.tomcat_9.1.3.jar`ファイルの中にある(わかりにくい！)
jarファイルは実体はzipなので、拡張子をリネームして展開して、中に入っている`DevloaderTomcat7.jar`を……あれ？Tomcat7しかないの？

このファイル実際に使ってみても、Tomcat8では動かなかった

```
重大: Begin event threw error [土 12 01 14:48:25 JST 2018]
重大: ContainerBase.addChild: start:  [土 12 01 14:48:25 JST 2018]
重大: 設定記述子 C:\pleiades\pleiades\tomcat\8\conf\Catalina\localhost\memoapp.xml を配備中のエラーです [土 12 01 14:48:25 JST 2018]
```

EclipseのTomcatプラグイン内に必要なファイルはなさそうなので、ネットからダウンロードする。

https://sourceforge.net/projects/tomcatplugin/files/additional/
↑
なんかつながらない…
ので、こちらから。
https://github.com/his-eg/tomcatplugin/blob/master/net.sf.eclipse.tomcat/DevLoaderTomcat8.jar

このファイルをTomcatのlibディレクトリへコピーする。(Pleiadesの場合は`pleiades\tomcat\8\lib\DevloaderTomcat7.jar`あたり)

これでTomcatを再起動すれば動作するはず…が、まだ同じエラーが出る

```
java.sql.SQLException: No suitable driver found for jdbc:mysql://localhost:3306/memoapp_db
```

(想像)このエラーは、実行の時点ではDevLoaderがどのクラスをロードするか決定されておらず、jdbcのjarがないと実行できない`DriverManager.getConnection()`に失敗している。
なので直前にjdbcのjarをロードする処理を行えばよい。
そこで`Class.forName("com.mysql.cj.jdbc.Driver");`を行う

```java
        // -- ここにDBへ保存処理 --
        Connection con = null;
        Statement smt = null;

        //String url = "jdbc:mysql://localhost:3306/memoapp_db?useSSL=false&useUnicode=true&serverTimezone=UTC";
        String url = "jdbc:mysql://localhost:3306/memoapp_db";
        String user = "memoapp";
        String pass = "memoapp";
        try {
            Class.forName("com.mysql.cj.jdbc.Driver");
            con = DriverManager.getConnection(url, user, pass);
            smt = con.createStatement();
            System.out.println("smt: " + smt);
        } catch (SQLException | ClassNotFoundException e) {
            e.printStackTrace();
        }
        finally {
            try {
                con.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
        // -- ここまでDB処理 --
```

というわけでDB接続部分のコードはこんな感じ。
サンプルコードによく出てくる`forName()`の引数のクラス名は、現バージョンはFQCNが変更されていて「com.mysql.__cj__.jdbc.Driver」になっているので注意(cjが追加されてる)

それと、開発用クラスローダーのクラスパス、`org.eclipse.m2e.MAVEN2_CLASSPATH_CONTAINER`を追加する必要があった

![image.png](https://qiita-image-store.s3.amazonaws.com/0/256859/fadaa5d1-479a-c568-490a-ee2bb0ebf07a.png)

余談だけどこのDB接続の完了にたどり着くまで1週間かかりますた…

※ スマートじゃないけどTomcatのライブラリ用の`lib`ディレクトリ以下に直接`mysql-connector-java-8.0.13.jar`と`protobuf-java-3.6.1.jar`をコピーするとEclipse側の設定は何もなくても動く。

# DB操作

MySQLの操作のかんたんなまとめはこちらへどうぞ。
[(ゆるりと更新) MySQL / SQLメモ](Qiita https://qiita.com/zaki-lknr/items/4d330b64e1d8cdd9ce59)

## テーブルの設計と作成

サンプルなのでシンプルに

| field         | type     | size | not null | comment | etc            |
|---------------|----------|------|----------|---------|----------------|
| memo_id       | int      | 11   | ✓        | ID      | auto_increment |
| category      | int      | 11   |          | カテゴリ    |                |
| title         | varchar  | 64   |          | タイトル    |                |
| memo          | text     |      |          | メモ      |                |
| create_date   | datetime |      |          | 作成日     |                |
| modified_date | datetime |      |          | 更新日     |                |

テーブルを作成するSQLの作成

```sql
create table if not exists memo_data (
    memo_id INT(11) auto_increment not null comment 'ID',
    category INT(11) comment 'カテゴリ',
    title VARCHAR(64) comment 'タイトル',
    memo TEXT comment 'メモ',
    create_date DATETIME comment '作成日',
    modified_date DATETIME comment '更新日',
    primary key (memo_id)
)
```

テーブルを作成するためのJavaコード(ちょっと適当だけど)

```java
    con = DriverManager.getConnection(url, user, pass);
    smt = con.createStatement();
    System.out.println("smt: " + smt);

    String create_table = "create table if not exists memo_data (" +
        "memo_id INT(11) auto_increment not null comment 'ID'," +
        "category INT(11) comment 'カテゴリ'," +
        "title VARCHAR(64) comment 'タイトル'," +
        "memo TEXT comment 'メモ'," +
        "create_date DATETIME comment '作成日'," +
        "modified_date DATETIME comment '更新日'," +
        "primary key (memo_id)" + ")";
    // create table
    smt.executeUpdate(create_table);
```

これで実行すればテーブルが作成される

```
mysql> show tables;
+----------------------+
| Tables_in_memoapp_db |
+----------------------+
| memo_data            |
+----------------------+
1 row in set (0.01 sec)

mysql> describe memo_data;
+---------------+-------------+------+-----+---------+----------------+
| Field         | Type        | Null | Key | Default | Extra          |
+---------------+-------------+------+-----+---------+----------------+
| memo_id       | int(11)     | NO   | PRI | NULL    | auto_increment |
| category      | int(11)     | YES  |     | NULL    |                |
| title         | varchar(64) | YES  |     | NULL    |                |
| memo          | text        | YES  |     | NULL    |                |
| create_date   | datetime    | YES  |     | NULL    |                |
| modified_date | datetime    | YES  |     | NULL    |                |
+---------------+-------------+------+-----+---------+----------------+
6 rows in set (0.00 sec)

mysql>
```

## メモの保存処理

`doPost()`のメソッドで`req.getParameter("title")`と`req.getParameter("memo")`で参照できるform入力データをテーブルに保存する処理を作成する。

```java
String form_title = req.getParameter("title");
String form_memo = req.getParameter("memo");
```

と変数に取得しておいて、INSERT文は以下のような感じで。

```sql
insert into memo_data (
    category, title, memo, create_date, modified_date
) values (
    0,
    form_title,
    form_memo,
    cast(now() as datetime),
    cast(now() as datetime)
);
```
レコードを保存するJavaコード

```java
    con = DriverManager.getConnection(url, user, pass);
    smt = con.createStatement();

    String form_title = req.getParameter("title");
    String form_memo = req.getParameter("memo");

    System.out.println("title: " + form_title);
    System.out.println("text: " + form_memo);
    String insert_memo = "insert into memo_data (" +
            "category, title, memo, create_date, modified_date" +
        ") values (" +
            "0," +
            "'" + form_title + "'," +
            "'" + form_memo + "'," +
            "cast(now() as datetime)," +
            "cast(now() as datetime) " +
        ");";
    // レコード保存
    smt.executeUpdate(insert_memo);
```

![image.png](https://qiita-image-store.s3.amazonaws.com/0/256859/64dfa930-41cc-1982-5d9a-7190ea61681d.png)

これでブラウザからテキスト入力してsubmitすると、DBに内容が保存される。

(改行が入って見づらいけど)こんな感じ

```
mysql> select * from memo_data;
+---------+----------+--------+----------------------------+---------------------+---------------------+
| memo_id | category | title  | memo                       | create_date         | modified_date       |
+---------+----------+--------+----------------------------+---------------------+---------------------+
|       1 |        0 | 買い物 | - カレー
- やさい
- にく | 2018-12-02 09:52:07 | 2018-12-02 09:52:07 |
+---------+----------+--------+----------------------------+---------------------+---------------------+
1 row in set (0.00 sec)

mysql>
```

## メモの取得と表示処理

最後に保存したメモデータを画面に表示する処理を作成する。
表示時なので、場所は`doGet()`で実行する。

ひとまず表示するカラムは

- タイトル
- メモ
- 更新日

の3つで。

まずはSELECT文

```sql
select title, memo, modified_date from memo_data;
```

Javaのソースは、createやinsertと違って実行結果を受け取る処理も作成する。

```java
    con = DriverManager.getConnection(url, user, pass);
    smt = con.createStatement();

    String select_memo = "select title, memo, modified_date from memo_data;";

    ResultSet result = smt.executeQuery(select_memo);
    while (result.next()) {
        System.out.println("title: " + result.getString("title"));
        System.out.println("memo: " + result.getString("memo"));
        System.out.println("modify: " + result.getString("modified_date"));
    }
```

これでレコード情報が取得できる。
次に、DBから取り出したメモデータを画面(JSP)へ渡す。

# メモの出力処理の作成

今度はServletからJSPへデータを渡す(JSPでServletから受け取った値を参照する)処理を作成する。

## Servlet側

まず`executeQuery()`の結果は、そのままだとConnectionのクローズ後では使用できないので、受け渡し用の変数を作成しその中へレコード情報をコピーする。

```java
    ArrayList<HashMap<String, String>> record_list = new ArrayList<>();

    // 中略

    ResultSet result = smt.executeQuery(select_memo);
    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);
    }
```

そして、JSPへこの`record_list`を渡すために、Servletのリクエスト変数に`setAtribute()`で値を設定する(HashMapのように、キー-値形式のデータ構造を渡すことができる)

```java
    req.setAttribute("record_list", record_list);

    String view = "/WEB-INF/jsp/index.jsp";
    RequestDispatcher dispatcher = req.getRequestDispatcher(view);
    dispatcher.forward(req, resp);
```

これで、JSPで`record_list`というキー名で参照できるようになる

## JSP側

Servletで`setAttribute(KEY)`でセットした値は`request.getAttribute(KEY)`で取り出せる。

```java
     ArrayList<HashMap<String, String>> list = (ArrayList<HashMap<String, String>>) request.getAttribute("record_list");
```

取り出した後は普通のJavaのコーディングと同様に、html形式で出力する。

```java
     Iterator<HashMap<String, String>> i = list.iterator();
     while (i.hasNext()) {
         HashMap map = i.next();
         out.println("<hr/>");
         out.println("<div>" + map.get("title") + "</div>");
         out.println("<div>" + map.get("modified_date") + "</div>");
         out.println("<div>" + ((String) map.get("memo")).replace("\n", "<br/>") + "</div>");
     }
```


あと最初にimport文が必要

```text
<%@page import="java.util.Iterator"%>
<%@page import="java.util.ArrayList"%>
<%@page import="java.util.HashMap"%>
```

全体のソースはこんな感じ

```text
<%@page import="java.util.Iterator"%>
<%@page import="java.util.ArrayList"%>
<%@page import="java.util.HashMap"%>
<%@page language="java" contentType="text/html; charset=UTF-8"
 pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Memo App</title>
</head>
<body>

 <h1>メモアプリ</h1>

 <form method="post">
  <input type="text" name="title" size="50" /> <br />
  <textarea rows="5" cols="80" name="memo"></textarea>
  <br /> <input type="submit" />
 </form>

 <%
     ArrayList<HashMap<String, String>> list = (ArrayList<HashMap<String, String>>) request
             .getAttribute("record_list");

     Iterator<HashMap<String, String>> i = list.iterator();
     while (i.hasNext()) {
         HashMap map = i.next();
         out.println("<hr/>");
         out.println("<div>" + map.get("title") + "</div>");
         out.println("<div>" + map.get("modified_date") + "</div>");
         out.println("<div>" + ((String) map.get("memo")).replace("\n", "<br/>") + "</div>");
     }
 %>

</body>
</html>
```

画面表示

![image.png](https://qiita-image-store.s3.amazonaws.com/0/256859/1bb3c991-9742-65e7-32af-64751e5aa27b.png)


なんとか表示と保存ができるようになりました。

# 今後の課題

- DBサーバのアドレス指定がハードコーディングされているため環境が変わるたびにリビルドが必要
    - [JNDIを使ったデータベース接続設定のXMLファイル定義](https://qiita.com/zaki-lknr/items/2bd955df62d4de0528ac)
- SQLインジェクション対策
    - プレースホルダの使用とか
- ローカル以外の環境で動かすためのwarファイルの作成
    - たぶんEclipseの操作の世界
    - 関連情報: [EclipseでサーバサイドJavaを作るときのプロジェクトとwarファイルのエクスポート](https://qiita.com/zaki-lknr/items/a2e76986b6e473964800)
- 脱Eclipse
    - CLIビルドとか
        - Mavenプロジェクトとして作られたディレクトリ構成をしていれば、例えばDebian系Linuxならaptで`maven`を入れれば、`pom.xml`のあるディレクトリで`mvn package`を実行すればwarファイルができる
        - [GitHubにソースをアップしたので参考](https://github.com/zaki-lknr/javaee-memoapp2/commits/master)
    - あとやっぱIDEAかな
- DBの初期化処理
    - JPA(Java Persistence API)とか
- DB処理をちゃんとクラス化
    - DAOという考え方
        - 平たく言うとデータの入出力操作を別クラスに分離して実装することで、例えばDBがPostgresに変更した場合も呼び出し側の変更を最小にする、みたいな商用コードでよくやるやつ。
        - [DAO側のソース例(GitHub)](https://github.com/zaki-lknr/javaee-memoapp2/tree/master/src/main/java/jp/example/www/dao)
        - [呼び出し側のソースの変更点(GitHub)](https://github.com/zaki-lknr/javaee-memoapp2/commit/9dee5df1857f7c64a7d9b5def76a3e5c7bee505b#diff-9b28da552a85fc2a037e656543ff1624)
- Servlet -> JSPのデータ受け渡しをもう少しきれいに書く
    - JavaBeansを使う: [JavaBeansとJSTLでJSP/Servletの記述を簡易にする](https://qiita.com/zaki-lknr/items/3c01f74e0fe2876442d2)
- ループの表示部分をもう少し綺麗に書けないか
    - JSTL coreタグライブラリのforEachで: [JavaBeansとJSTLでJSP/Servletの記述を簡易にする](https://qiita.com/zaki-lknr/items/3c01f74e0fe2876442d2)
- フレームワーク対応
    - Springとか
- ログインとかセッションとか
- 画面の見た目
    - Bootstrapとか…