Lesson 9

JavaでMVCモデルを実装する

Lesson 9 Chapter 1
MVCモデルとは

前レッスンでは、JavaプログラムからDBに接続し、操作を行う方法を学びました。本レッスンでは、ここまでの知識を用いて簡単なWebアプリケーションを作って行きます。今までのプログラムは、コンソールに結果を出力するだけでしたが、ブラウザ上で画面を表示するものを作っていきます。

本レッスンで作成するWebアプリケーションは、MVCモデルという設計手法を用います。まずは、MVCモデルについて学んでいきましょう。

MVCモデルの概念

MVCは、Model-View-Controllerの略で、GUIアプリケーションの開発における設計手法のひとつです。MVCは、アプリケーションの構造をModel、View、Controllerの3つのコンポーネントに分けて、それぞれの部分を独立で開発できるようにします。

Java-9_1

コンポーネントは、部品や要素を意味します。では、それぞれのコンポーネントの役割を見ていきましょう。

Model

モデル(Model)は、アプリケーションの主な処理や、データを管理する役割を持っています。DBからデータを取得、変更、保存を行います。

View

ビュー(View)は、ユーザーが操作するためのインターフェースを管理する役割を持っています。アプリケーションの見た目や振る舞いを定義するために使用されます。具体的には、ユーザーがアプリケーション上で行う操作(例えば、ボタンをクリックするなど)を受け付け、それをコントローラーに伝えます。そして、コントローラーがその操作に応じて処理を行い、再びビューに結果を返します。この結果をもとに、ビューはユーザーに必要な情報や画面表示を行う役割を担っています。

Controller

コントローラ(Controller)は、ViewとModelの間で仲介するコンポーネントで、ユーザーの操作を管理します。ビューからの入力を受け取り、適切な処理をモデルに依頼し、結果の表示をビューに依頼します。

ショッピングサイト

MVCモデルの概念を理解したところで、実際にMVCモデルを使ってショッピングサイトを構築するとどのようなものになるのかを見てみましょう。

1. Model

オンラインショッピングサイトのModelは、顧客情報や製品情報、在庫情報などのデータを管理します。これらの情報は、データベースに保存されます。Modelは、このデータを更新、保存、取得する責任を持っています。例えば、新しい製品が追加されると、Modelはそれをデータベースに追加し、在庫情報を更新します。

2. View

オンラインショッピングサイトのViewは、ユーザーが操作するためのインターフェースを管理します。Viewは、HTML、CSS、JavaScriptなどのWebテクノロジーを使用して構築されます。ユーザーは、Viewから製品のリストを参照し、商品を選択して購入することができます。

3. Controller

オンラインショッピングサイトのControllerは、ユーザーの操作を管理し、ModelとViewの間で仲介します。Controllerは、Viewからのユーザーのリクエストを受け取り、Modelから必要なデータを取得します。そして、Viewに必要なデータを渡して、表示する商品のリストを更新します。また、ユーザーが商品を選択して購入を完了すると、Controllerは、Modelを更新して在庫情報を更新し、Viewに支払い完了の通知を送信します。

このように、オンラインショッピングサイトは、MVCモデルを使用して構築することができます。

MVCモデルの利点

MVCモデルはWebアプリケーションの設計パターンとして広く使用されています。この設計パターンが人気がある理由は、多くの利点があるからです。

まず、MVCモデルはコンポーネントの再利用性が高く、同じModelを使用して複数のViewを作成したり、同じViewを使用して複数のControllerを作成することができます。また、各コンポーネントが分離されているため、開発の並列化が可能です。これにより、複数の開発者が同時に開発を行うことができ、開発の効率化が図れます。

さらに、各コンポーネントの責務が明確に分離されているため、保守性が高くなります。たとえば、Viewの変更が必要な場合でも、ModelやControllerに影響を与えることなく、Viewの変更だけを行うことができます。また、新しい機能やコンポーネントを追加することが容易であり、テスト性も高いため、開発の拡張性と柔軟性に優れています。

これらの利点により、MVCモデルはWebアプリケーションの設計において広く使用されている設計パターンの一つとなっています。

Lesson 9 Chapter 2
サーブレット(Java Servlet)

前チャプターでは、MVCモデルについて学びました。本レッスンを通して、MVCモデルを用いた簡単なWebアプリケーションを作っていきます。JavaにおいてMVCモデルを実現する上で、サーブレットとJSPという技術について知る必要があります。まず、本チャプターでは、サーブレット(Java Servlet)という技術に触れていきます。

サーブレットとは

サーブレットは、Webアプリケーションの開発で使用されるサーバーサイドの技術の一つです。サーブレットを使用することで、Webサイトの動的なコンテンツの生成や処理を実現することができます。MVCモデルのControllerを担います。

サーブレットクラス

Javaのプログラムにおいて、サーブレットは、サーブレットクラスと呼ばれるJavaプログラムで構成されます。これらのサーブレットクラスは、HTTPリクエストを処理するために使用されます。

サーブレットクラスは、javax.servlet.http.HttpServletクラスを拡張する必要があります。また、サーブレットクラスは、HTTPリクエストを処理するためのサーブレットメソッド(doGet、doPostなど)を実装する必要があります。これらのサーブレットメソッドは、HTTPリクエストを受信し、HTTPレスポンスを生成するために使用されます。

HTTPリクエスト、HTTPレスポンス

サーブレットは、Webブラウザから送信されるHTTPリクエストに応答して、動的なWebコンテンツを生成するプログラムです。HTTPリクエストは、WebブラウザからWebサーバーに対して送信されるメッセージで、特定のWebページを要求するために使用されます。以下は、HTTPリクエストの基本的な要素です。

  • URL(Uniform Resource Locator):リクエストするページのアドレスです。URLは、プロトコル(httpやhttpsなど)とドメイン名(www.example.comなど)の組み合わせで構成されます。

  • HTTPメソッド:HTTPリクエストで実行されるアクションを示すメソッドです。HTTPメソッドには、GET、POST、PUT、DELETEなどの種類があります。GETは、リソースの取得を要求するために使用され、POSTは、Webサーバーにデータを送信するために使用されます。

  • ヘッダー:HTTPリクエストに関する追加の情報を提供するために使用されます。ヘッダーには、ブラウザの種類、言語設定、Cookie、キャッシュ設定などが含まれます。

  • ボディ:HTTPリクエストで送信されるデータが含まれる部分です。ボディは、HTTPメソッドによって異なります。例えば、GETメソッドはボディを送信しないことが一般的ですが、POSTメソッドはボディにフォームデータを含むことができます。

HTTPリクエストは、Webブラウザによって自動的に生成されます。ユーザーは、Webページ上のリンクをクリックすることでHTTPリクエストを生成し、Webサイトにアクセスできます。Web開発者は、HTTPリクエストを受信し、必要な情報を取得してHTTPレスポンスを返すために、Webサーバーサイドでプログラミングを行います。このHTTPリクエストを受信し適切なHTTPレスポンスを返すのがサーブレットの役割です。(HTTPリクエストはチャプター8で実際に実装しながら再度解説します)

HTTPプロトコル

HTTP (HyperText Transfer Protocol) プロトコルは、Web上でクライアントとサーバー間で文書を送受信するための標準化されたプロトコルです。HTTPを使用すると、WebブラウザがWebサイトからHTMLページ、画像、スタイルシートなどを取得できます。HTTPは、WebアプリケーションやAPIなど、様々なWebベースのシステムで広く使用されています。

サーブレットコンテナ

サーブレットクラスは、サーブレットと同じく、HTTPリクエストに応答して動的なWebコンテンツを生成するプログラムです。しかし、サーブレットは、単独で動ける訳ではありません。Javaプログラムが実行するのにJREが必要であったように、サーブレットが動くには、サーブレットコンテナ(Servlet Container)というサーブレットの実行環境を提供するソフトウェアが必要です。サーブレットコンテナはWebサーバー上で実行され、HTTPリクエストを受け取って、適切なサーブレットに処理を委譲します。サーブレットコンテの例として、Apache、Tomcat、Jettyなどがあります。(ApacheとTomcatについては、後述します。)

ライフサイクル管理

サーブレットコンテナは、サーブレットのライフサイクルを管理します。サーブレットの初期化、サービスの実行、破棄など、すべてのフェーズにおいてサーブレットコンテナが処理を担当します。

サーブレットのライフサイクル管理には、次のメソッドが定義されます。

  • init(): サーブレットが初期化されたときに呼び出されます。サーブレットが初めてリクエストを受け取る前に、一度だけ呼び出されます。このメソッドでは、サーブレットが必要とする任意のリソースを初期化することができます。

  • service(): リクエストがサーブレットに送信されるたびに呼び出されます。このメソッドでは、HTTPリクエストに対して適切なレスポンスを生成するためのロジックを実装します。

  • destroy(): サーブレットが破棄されるときに呼び出されます。Webアプリケーションが終了するときに一度だけ呼び出されます。このメソッドでは、リソースを解放したり、開いたコネクションを閉じたりするなど、クリーンアップを行うためのロジックを実装することができます。

  • init(ServletConfig config): サーブレットが初期化されたときに呼び出されます。init()メソッドとは異なり、このメソッドにはServletConfigオブジェクトが渡されます。このオブジェクトには、サーブレットが利用できる初期化パラメータやサーブレットコンテキスト情報が含まれています。

  • getServletConfig(): ServletConfigオブジェクトを返します。このオブジェクトには、サーブレットが利用できる初期化パラメータやサーブレットコンテキスト情報が含まれています。

  • getServletInfo(): サーブレットのバージョン情報を返します。

これらのメソッドは、サーブレットコンテナによって制御され、サーブレットのライフサイクルを管理します。サーブレットは、Webアプリケーションが開始されるときに初期化され、HTTPリクエストが送信されるたびにservice()メソッドが呼び出されます。Webアプリケーションが終了するときには、destroy()メソッドが呼び出され、サーブレットが破棄されます。

マルチスレッド対応

サーブレットはマルチスレッド環境下で動作することができるため、複数のリクエストを同時に処理することができます。

マルチスレッドとは、複数のスレッド(処理の流れ)を持つプログラムのことです。1つのプログラム内で複数の処理を同時に行い、より効率的に処理を行うことができます。マルチスレッドを利用することで、プログラムの応答性を向上させたり、処理速度を高速化することができます。

サーブレットは、サーブレットコンテナによって生成され、各リクエストに対して別々のスレッドで処理されます。つまり、複数のリクエストが同時にサーブレットに到着する場合、それぞれのリクエストに対して別々のスレッドが割り当てられます。

サーブレットは、スレッドセーフである必要があります。つまり、複数のスレッドが同時にサーブレットにアクセスしても、互いに干渉しないようにする必要があります。これは、サーブレットがスレッドセーフであることを確認することで実現できます。

プラットフォーム非依存

サーブレットは、Javaプラットフォーム上で動作するため、プラットフォームに依存しません。これは、Java仮想マシン(JVM)上で動作するためです。Javaの特徴の1つに、JVM上で動作することで、異なるオペレーティングシステムで実行されるJavaアプリケーションは同じプログラムコードで書くことができ、プラットフォームに依存しないということが挙げられます。

サーブレットがプラットフォーム非依存であるため、Webアプリケーションを開発する際に、開発者はサーブレットを利用して、異なるプラットフォーム上で動作するWebアプリケーションを開発することができます。また、Java仮想マシンは、マルチプラットフォームで動作するため、同じJavaアプリケーションを異なるプラットフォーム上で実行することも可能です。

これにより、サーブレットを利用して、異なるプラットフォームに対応したWebアプリケーションを開発することができ、開発効率や移植性を高めることができます。

サーブレットに関する説明は以上です。チャプター6以降では、サーブレットクラスを実際に作成し、コードを書いていきます。その前に、次のチャプターではJSPについて説明していきます。

Lesson 9 Chapter 3
JSP

前チャプターでは、サーブレットについて触れてきました。サーブレットは、MVCモデルにおけるControllerを担うコンポーネントでした。本チャプターでは、Viewを担うコンポーネントである、JSPについて学んでいきます。

JSPとは

JSP(Java Server Pages)は、Javaプラットフォーム上で動作するWebアプリケーションの開発に用いられる技術の1つです。JSPを用いることで、Webページを動的に生成することができます。

JSPは、HTMLのように見えるテンプレートファイルにJavaコードを埋め込むことができます。JSPコンテナは、JSPをHTMLに変換し、Webブラウザにレスポンスを返す際に動的に生成された結果を含めます。つまり、JSPはJavaのコードを埋め込むことで、動的なWebページを生成することができるのです。

JSPの利点の1つは、HTMLとJavaコードを組み合わせることができるということです。これにより、Javaの力を借りて、Webページを動的に生成することができます。また、JSPはJavaプラットフォーム上で動作するため、Javaの機能やライブラリを利用することができ、開発効率を高めることができます。

サーブレットとの関係

JSPとサーブレットは、Java EEにおけるWebアプリケーションの開発に使用される技術であり、密接に関連しています。

JSPはJavaプラットフォーム上で動作するWebアプリケーションの開発に使用されるテクノロジーの1つであり、HTMLにJavaコードを埋め込むことができます。JSPをブラウザからのリクエストに応じて動的に処理するためには、JSPをサーブレットに変換する必要があります。

サーブレットは、Webサーバー上で動作するJavaプログラムであり、HTTPリクエストを受信して、HTTPレスポンスを生成する処理を行います。JSPは、サーブレットに変換され、サーブレットのライフサイクルに従って動的に処理されます。JSPのコードはサーブレットのdoGet()、doPost()などのメソッド内に挿入され、サーブレットのメソッドからJSPを呼び出すこともできます。

JSPとサーブレットの組み合わせにより、Webアプリケーションの開発が容易になります。JSPを使用することで、HTMLコードとJavaコードを1つのファイルにまとめることができます。また、サーブレットを使用することで、Webアプリケーションの動的な処理を行うことができます。

JSPとサーブレットはどちらもJava EEにおけるWebアプリケーションの開発に欠かせない技術であり、相互に補完しあう関係にあります。

Lesson 9 Chapter 4
Tomcat

前チャプターでは、サーブレットとJSPの関係性について見てきましたが、本チャプターでは、サーブレットコンテナを提供するTomcatについて学んでいきます。

WebサーバーとWebコンテナ

Tomcatについて理解するには、まずWebサーバーとWebコンテナについて理解する必要があります。これらは、Webアプリケーションを提供するための役割を持つサーバーソフトウェアです。

Webサーバーは、HTTPプロトコルを使用してWebリクエストを受信し、静的なWebコンテンツ(HTML、CSS、画像ファイルなど)を返すことができます。Webサーバーは、通常、Apache HTTP Server、Nginxなどのソフトウェアで実行されます。

Webコンテナは、Webアプリケーションを実行するための環境を提供するソフトウェアです。Webコンテナは、サーブレット APIやJSP APIなどのJava EE APIを使用して、Webアプリケーションを処理します。Webコンテナは、サーブレットコンテナ、JavaServer Pages(JSP)コンテナ、JavaServer Faces(JSF)コンテナなどがあります。Webコンテナは、Webサーバーと協調して、動的なWebコンテンツを提供することができます。

一般的に、Webサーバーは静的なWebコンテンツを提供し、Webコンテナは動的なWebコンテンツを提供します。ただし、WebサーバーとWebコンテナは同じサーバー上にインストールされ、同じHTTPポートを共有することができるため、単一のサーバーで両方の機能を提供することもできます。

Tomcatとは

Tomcatは、Apacheソフトウェア財団が開発・管理する、オープンソースのWebアプリケーションサーバーです。サーブレット仕様に準拠して設計されるWebサーバーであり、サーブレットコンテナ、つまりWebコンテナの一種でもあります。つまり、ブラウザからのアクセスを受けて適切なコンテンツを返すというWebサーバーの機能と、サーブレットをサーバー上で実行するサーブレットコンテナの機能を持っているわけです。

Tomcatは、JVM上で実行されるので、プラットフォーム非依存であり、Windows、Linux、macOSなど、様々なオペレーティングシステムで動作します。

サーブレットコンテナとJakarta EE

TomcatがWebサーバとサーブレットコンテナを兼ねていることが分かりました。そもそもサーブレットは、Jakarta EEの仕様の一部で、Jakarta EEでは、サーブレットを扱うサーブレットコンテナの仕様も定義しています。そのサーブレットコンテナを実装しているのがTomcatです。Tomcatは、無償で提供されているので機能は限定的ですが、Jakarta EEで定義されている他の機能を用いると、開発者はより高度なWebアプリケーションを構築することができます。

サーブレットとTomcat

サーブレットとTomcatの関係性について見ていきましょう。まずは、Webサーバーとしての役割です。TomcatはWebサーバーとして機能し、HTTPリクエストを受け取って、サーブレットやJSPを使ってWebアプリケーションを処理し、HTTPレスポンスを返します。

Tomcatは、サーブレットやJSPを処理するためにサーブレットコンテナとしても機能します。サーブレットコンテナは、サーブレットを実行し、サーブレットライフサイクルを管理します。Tomcatは、JSPエンジンとしても機能し、JSPをサーブレットに変換し、サーブレットと同様に処理されるようにします。

以上のことから、Tomcatがサーブレット/JSPを用いた開発において、サーブレットコンテナ、そしてWebサーバーとして使うことができることが分かりました。

Lesson 9 Chapter 5
Apache

前チャプターでは、Tomcatについて学びました。Tomcatは、サーブレットコンテナとしての役割とWebサーバーの役割を併せ持つソフトウェアでした。しかし、併せ持つと言っても、Tomcatのメインの役割はサーブレットコンテナです。Webサーバーの機能は簡易的なものです。このことから、一般的にTomcatを使う場合は、Tomcatと一緒にApacheというWebサーバを使います。本チャプターではこのApacheについて学んでいきます。

Apacheとは

Apacheは、Webサーバーソフトウェアで、世界的に最も広く使用されているWebサーバーのひとつです。Apacheは、クロスプラットフォームで動作するため、Windows、Linux、Mac OS Xなど、ほとんどのオペレーティングシステム上で実行できます。Apacheは、拡張性が高く、多くのプラグインが利用可能であるため、さまざまな目的に合わせたカスタマイズが可能です。また、安定性に優れており、大量のトラフィックにも耐えることができます。

Tomcatとの違い

本チャプター冒頭でも説明した通り、TomcatとApacheはそれぞれWebサーバーソフトウェアとしての役割を持っていますが、TomcatのWebサーバーとしての機能は限定的です。それは、Tomcatがサーブレットコンテナとして、動的なコンテンツを処理するために特化しているからです。一方Apacheは、Webサーバーとして動作し、静的なコンテンツを処理することに特化しています。つまり、ApacheとTomcatは両方ともWebサーバーですが、Apacheは静的なコンテンツの配信に特化しており、Tomcatは動的なコンテンツの処理に特化しています。しかし、両方を組み合わせて使用することで、静的なコンテンツと動的なコンテンツの両方を配信できます。

ApacheとTomcatの併用

ここまでの説明を踏まえて、TomcatとApacheを併用してサーブレット/JSPで開発を行うと以下の図のようになります。

Java-9_2

動的なコンテンツを提供する場合、まずユーザーからのHTTPリクエストをWebサーバーであるApacheが受け、Tomcatに対してリクエストの転送を行います。Tomcatは、リクエストを受け、JSPやサーブレットを処理し、レスポンスをApacheに返します。ApacheはTomcatのレスポンスからHTTPレスポンスを生成し、ユーザーに返します。このように、WebサーバーとしてのApacheと、サーブレットコンテナとしてのTomcatは連携しています。

Lesson 9 Chapter 6
サーブレット/JSP の開発環境を構築する

ここまで、サーブレット/JSP、そしてサーブレットコンテナであるTomcatとWebサーバーであるApacheについて学んできました。本チャプターからは、いよいよ実際に手を動かしながら、Webアプリケーションを作っていきます。

プロジェクトの作成

まずは、プロジェクトを作成していきます。Eclipseを開いて、"ファイル(F)"→"新規(N)"→"その他(O)"を選択します。

ウィザードを選択という画面に遷移するので、"動的Webプロジェクト"を選択して"完了(F)"を押下します。

Java-9_3

プロジェクト名は「memoApp」としましょう。今回作成するプロジェクトは、本レッスンの後半で作成するアプリケーションの土台になります。名前を入力したら、"完了(F)"を押下します。

Java-9_4

動的Webプロジェクトが作成されました。

サーブレットファイル、JSPファイルの作成

memoAppプロジェクトフォルダ内のsrcフォルダを右クリックし、"新規(N)"→"その他(O)"の順番で押下します。ウイザードで、「サーブレット」を選択し、"次へ(N)"を押下します。

Java-9_5

Java パッケージ(K)に"servlet"と入力します。次にクラス名(M)は、"MemoHome"と設定しましょう。"完了(F)"を押下します。

Java-9_6

サーブレットファイルが生成されました。

JSPファイルは、src/main/webapp/ に作成します。webappフォルダを右クリックし、"新規(N)"→"その他(O)"の順番で押下します。ウイザードで、「JSPファイル」を選択し、"次へ(N)"を押下します。

Java-9_7

ファイル名は、"index.jsp"と入力しましょう。"完了(F)"を押下します。

以上で、サーブレットファイルとJSPファイルの作成が完了しました。次のチャプターでは、ApacheとTomcatの連携を行っていきます。

Lesson 9 Chapter 7
ApacheとTomcatの連携

本チャプターでは、前チャプターで作成したmemoAppプロジェクトにApacheとTomcatを連携させていきます。

ターゲット・ランタイムの設定

まずは、Eclipse側からTomcatをインストールしていきましょう。memoAppプロジェクトを右クリックし、"プロパティ"を選択し、"ターゲット・ランタイム"を検索します。ターゲット・ランタイムの設定画面に遷移したら"新規"を押下します。

Java-9_8

ターゲット・ランタイムとは、プロジェクトが実行されるアプリケーションサーバーとそのバージョンなどの実効環境のことを指します。Apacheフォルダ内の"Apache Tomcat v10.0"を選択し、"次へ(N)"を押下します。

Java-9_9

"ダウンロードしてインストール..."を押下します。ライセンスに関する画面が表示されるので、同意のチェックを入れ、"完了(F)"を押下します。

Java-9_10

フォルダー選択の画面になるので、"C:\pleiades\2022-09\tomcat"に"10"というフォルダを作成してその中をインストール先とし、"選択"をクリックします。少し待つとインストールが完了するので、"完了(F)"を押下しましょう。

Java-9_11

ターゲット・ランタイムの画面に戻るので、作成した"Apache Tomcat v10.0"にチェックを入れ"適用して閉じる"を押下します。これで、ターゲット・ランタイムの設定は完了です。

Java-9_12

サーバーの設定

次にサーバーの設定を行っていきます。ここでいうサーバーとは、開発環境で実行する時に用いるサーバーのことです。まずは、"ウィンドウ(W)"→"ビューの表示(V)"→"その他(O)"を選択します。

Java-9_13

"サーバー"を選択し、"開く(O)"を押下します。

Java-9_14

サーバーのビューが画面の下側に表示されるので、右クリックし、"新規(W)"→"サーバー"と選択します。

Java-9_15

ターゲット・ランタイムでも選択した、"Tomcat v10.0サーバー"を選択し、"次へ(N)"を押下しましょう。

Java-9_16

今回は、"memoApp"プロジェクトを"Tomcat v10.0サーバー"上で構成したいので、"memoApp"を選択し、"追加(D)"を押下します。"memoApp"が右側に移動したら、"完了(F)"を押下しましょう。

Java-9_17

これで、プロジェクトに対して、ApacheとTomcatの連携をさせることができました。では実際にJSPを実行して動作確認を行いましょう。

JSPでの動作確認

では、memoAppを実行していきます。その前に少しJSPファイルに手を加えましょう。JSPファイルを以下のように変更します。

index.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
<p>テストテストテスト</p>
</body>
</html>

bodyタグの中にpタグを追加し、「テストテストテスト」という文字列を追加しました。これで、このJSPが表示されると、「テストテストテスト」という文字列が表示されるようになります。

では編集したJSPファイルを実行していきます。"実行"ボタンを押下します。"サーバーで実行"という画面が表示されるので、先ほど追加した、Tomcat 10を選択し、"完了(F)"を押下します。

Java-9_18

ブラウザが立ち上がり、以下のような画面が表示されれば、成功です。

Java-9_19

ApacheとTomcatをプロジェクトに連携させることができました。次のチャプターからは、実際にサーブレットとJSPを触りながら、MVCモデルをいかに表現していくかを学んでいきます。

Lesson 9 Chapter 8
Javaで表現するMVCモデル

本チャプターでは、MVCモデルがJavaにおいてどう表現されるのかをより具体的に見ていきます。

Model

前チャプターでは、Controllerを担うサーブレットと、Viewを担うJSPのファイルを作成しましたが、ModelはJavaにおいてどのように表現されているのでしょうか。データの処理や、データベースアクセスを担当するModelは一般的なJavaクラスが担当します。

Modelが担う役割は様々ですが、以下の3つにまとめることができます。

  • DAO(Data Access Object):データベース、ファイル、APIなどからデータを取得し、アプリケーション内で利用できる形(JavaBeansやDTO)に変換します。また、データの追加、更新、削除などを行います。

  • Service:ビジネスロジックを実行します。ビジネスロジックは、データの処理方法、バリデーション、エラー処理、セキュリティなどを含みます。

  • POJO, DTO:アプリケーションにおけるデータの保持と転送に関する役割を担っています。(POJOについては次チャプターで扱います)

DAOの例として以下のコードを見てみましょう。

MemoDAO.java
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;

public class MemoDAO {
    private String url = "jdbc:mysql://localhost:3306/mydb?useSSL=false";
    private String user = "root";
    private String password = "password";

    public List getMemoList() throws SQLException {
        List memoList = new ArrayList();

        try (Connection con = DriverManager.getConnection(url, user, password);
             PreparedStatement ps = con.prepareStatement("SELECT * FROM memo ORDER BY id ASC");
             ResultSet rs = ps.executeQuery()) {

            while (rs.next()) {
                int id = rs.getInt("id");
                String title = rs.getString("title");
                String content = rs.getString("content");
                Memo memo = new Memo(id, title, content);
                memoList.add(memo);
            }
        }
        return memoList;
    }

    public void addMemo(String title, String content) throws SQLException {
        try (Connection con = DriverManager.getConnection(url, user, password);
             PreparedStatement ps = con.prepareStatement("INSERT INTO memo (title, content) VALUES (?, ?)")) {

            ps.setString(1, title);
            ps.setString(2, content);
            ps.executeUpdate();
        }
    }

    public void deleteMemo(int id) throws SQLException {
        try (Connection con = DriverManager.getConnection(url, user, password);
             PreparedStatement ps = con.prepareStatement("DELETE FROM memo WHERE id = ?")) {

            ps.setInt(1, id);
            ps.executeUpdate();
        }
    }
}

この例は、メモアプリにおけるModelの一部です。Memoオブジェクトをデータベースとやりとりするためのメソッドを定義しています。getMemoListメソッドは、memoテーブルから全てのメモを取得し、Memoオブジェクトのリストを返します。addMemoメソッドは、新しいメモをデータベースに追加します。deleteMemoメソッドは、指定されたIDのメモを削除します。このようにDAOでは、データベースとのやり取りを定義します。

View(JSP)

Viewは、ユーザーが操作するためのインターフェースを管理する役割を持っています。アプリケーションの外観や振る舞いを定義するために使用されます。JSPでは、HTMLコードの中にJavaコードを埋め込んで、動的なコンテンツを生成します。以下の例を見てみましょう。

index.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Memo List</title>
</head>
<body>
<h1>Memo List</h1>
<table>
  <tr>
    <th>Id</th>
    <th>Title</th>
    <th>Content</th>
  </tr>
  <c:forEach items="${memoList}" var="memo">
    <tr>
      <td>${memo.id}</td>
      <td>${memo.title}</td>
      <td>${memo.content}</td>
    </tr>
  </c:forEach>
</table>
</body>
</html>

このJSPファイルは、Memo Listを表示するViewの部分を担当しています。JSPファイルの先頭には、ページのエンコーディング形式やJSTLライブラリのインポートが記述されています。その後、Memo Listを表示するためのHTML要素が含まれています。<c:forEach>はJSTLライブラリを使って、memoListオブジェクトの各要素について繰り返し処理を行います。また、JSPファイル内には、変数の値を表示するために${}の形式を使って、memoオブジェクトのプロパティ値を取得しています。

JSTLライブラリとは

JSTL(JavaServer Pages Tag Library)は、JavaサーブレットやJSPで使われるライブラリで、JSP内でHTMLのような記述をすることができるタグを提供します。これにより、Javaコードの埋め込みを減らし、JSPページをよりシンプルで可読性の高いものにすることができます。JSTLには、制御フロー、ループ、条件、フォームの処理などに使用するためのタグが含まれています。JSTLライブラリは、JSPコンテナーに含まれている場合がありますが、必要に応じて独自にダウンロードして使用することもできます。さきほどの例では、独自にダウンロードを行っています。

Controller

Controllerは、ViewとModelの間で仲介するコンポーネントです。サーブレットは、HTTPリクエストに対応します。HTTPリクエストには、GETリクエストとPOSTリクエストがあります。

GETリクエストは、データをサーバーに要求するためのリクエストです。具体的には、リクエストされたURLに対応するリソースの取得を目的とします。GETリクエストは、ブラウザのアドレスバーからリンク先に遷移する場合や、フォームのデータを送信しない場合などに使用されます。

POSTリクエストは、サーバーにデータを送信するためのリクエストです。具体的には、フォームのデータを送信する場合などに使用されます。POSTリクエストは、リクエストボディにデータを含めるため、GETリクエストよりも多くのデータを送信できます。

サーブレットでは、doGetメソッドを使ってGETリクエストを処理し、doPostメソッドを使ってPOSTリクエストを処理します。リクエストがGETであればdoGetメソッドが呼び出され、POSTであればdoPostメソッドが呼び出されます。また、サーブレットは両方のリクエストを処理できるため、どちらのメソッドも実装する必要があります。以上を踏まえて、以下のコードを見てみましょう。

Servlet.java
import java.io.IOException;
import java.util.List;

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 MemoListServlet extends HttpServlet {
    private static final long serialVersionUID = 1L;

    private MemoDao memoDao;

    public void init() {
        memoDao = new MemoDao();
    }

    protected void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        List<Memo> memoList = memoDao.getAllMemos();
        request.setAttribute("memoList", memoList);
        request.getRequestDispatcher("memoList.jsp").forward(request, response);
    }

    protected void doPost(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        String title = request.getParameter("title");
        String content = request.getParameter("content");
        Memo memo = new Memo(title, content);
        memoDao.addMemo(memo);
        response.sendRedirect(request.getContextPath() + "/");
    }
}

このコードは、HTTP GETとPOSTリクエストを処理するMemoアプリのサーブレットです。doGetメソッドでは、MemoDaoオブジェクトを使用してすべてのメモを取得し、JSPファイルに渡すためにリクエスト属性に保存します。doPostメソッドでは、フォームから送信されたタイトルと内容のパラメータを取得し、Memoオブジェクトを作成してMemoDaoに渡します。MemoDaoは、データベースに新しいメモを追加します。最後に、レスポンスをトップページにリダイレクトします。Controllerの仲介役的な役割はこのように実装されます。

フォワード

フォワード(forward)とは、リクエストを他のサーブレットクラスや、JSPファイルに転送するための仕組みです。先ほどのサーブレットクラスからフォワードを行っている箇所を抜粋したコードが以下です。

Servlet.java
request.getRequestDispatcher("memoList.jsp").forward(request, response);

上記のコードでは、処理を"memoList.jsp"に転送しています。これによって、ブラウザにはフォワードされたJSPファイルの出力が表示されます。

リダイレクト

処理を別のリソースに転送する仕組みはフォワード以外にリダイレクト(redirect)があります。リダイレクトは、リクエストを他のURLに転送することです。以下のような状況で使用されます。

  • ユーザーに別のページに移動してもらいたい場合

  • POSTリクエストをGETリクエストに変換する場合(PRGパターン)

PRGパターンとは、"Post Redirect Get"の略で、POSTメソッドでリクエストを受け、Redirectし、GETメソッドから画面を返すパターンのことです。フォームの送信後にブラウザの更新によって生じる多重投稿や、戻るボタンによる不正なページ遷移を防止するために利用されます。先ほどのサーブレットクラスのPOSTメソッドでは、PRGパターンが実装されています。該当箇所を見てみましょう。

Servlet.java
 protected void doPost(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
  String title = request.getParameter("title");
  String content = request.getParameter("content");
  Memo memo = new Memo(title, content);
  memoDao.addMemo(memo);
  response.sendRedirect(request.getContextPath() + "/");
}

上記のコードでは、新しいメモを作成する処理を行った後に、"response.sendRedirect(request.getContextPath() + "/")"の行で、Servlet.javaに対してリダイレクトするようにブラウザに指示を行います。ブラウザは指定されたURLにリクエストを送信します。このURLは、getContextPath()メソッドによって取得されており、このメソッドは、コンテキストルート(アプリケーションのURLの先頭部分)を返します。ここでのコンテキストルートは、Servlet.javaを指すので、リダイレクトによって、Servlet.javaのGETメソッドへと処理が移ります。PRGパターンはこのように実装されます。

ここまで、Model、View、Controllerのプログラムを見ながら、Javaクラス、サーブレットクラス、JSPファイルがどのようにMVCモデルを表現しているのかを見てきました。Modelで定義されたメソッドをControllerが呼び出すことによってデータを処理し、フォーワードやリダイレクトを用いることで、ControllerとJSPの連携がされていました。しかし、本チャプターでは処理の流れの移動については触れましたが、インスタンスの流れについてはあまり触れていません。ControllerとJSPの間でインスタンスを渡すにはどうすればいいでしょうか。次のチャプターはJavaBeansという仕組みを中心にインスタンスの受け渡しについて学んでいきます。

Lesson 9 Chapter 9
JavaBeansを理解する

前チャプターでは、MVCモデルにおいてどのように処理が流れるのかについて学んできました。本チャプターでは、インスタンスの受け渡しについて学んでいきます。

スコープ

インスタンスの受け渡しには、スコープを利用します。スコープとはインスタンスを保存できる領域を指し、その領域の有効範囲によって4種類に分けられます。

  • リクエストスコープ(Request Scope):リクエストスコープは、同じリクエストで送信された複数の要求間でオブジェクトを共有するために使用されます。リクエストスコープ内のオブジェクトは、リクエストされたJSPページまたはサーブレット内でのみ使用できます。

  • セッションスコープ(Session Scope):セッションスコープは、ユーザーセッション内でオブジェクトを共有するために使用されます。セッションスコープ内のオブジェクトは、ユーザーがサイトにアクセスしている限り、複数のリクエスト間で有効です。

  • アプリケーションスコープ(Application Scope):アプリケーションスコープは、アプリケーション全体でオブジェクトを共有するために使用されます。アプリケーションスコープ内のオブジェクトは、アプリケーションが実行されている限り、すべてのユーザーに対して共有されます。

  • ページスコープ(Page Scope):ページスコープは、JSPページ内でのみオブジェクトを使用できるようにします。ページスコープ内のオブジェクトは、同じページの複数のリクエスト間で共有できます。

スコープは、オブジェクトの有効期間を制御するための重要な仕組みであり、適切に使用されると、Webアプリケーションのパフォーマンスを向上させ、データの整合性を確保することができます。このスコープには、JavaBeansと呼ばれるクラスのインスタンスを保存します。

JavaBeansとは

JavaBeansはJavaクラスの独立性を高め、再利用可能性を高めるためのルールおよびそれを満たしているクラスを指します。

JavaBeansは、以下のルールに従って実装されたJavaクラスです。

  • クラスが public である

  • デフォルトコンストラクタを持つ

  • フィールドはカプセル化する

  • フィールドは、getter/setterメソッドを持つ

  • java.io.Serializable を実装している

JavaBeansのクラス

メモアプリを例にとって、Memoオブジェクトを作成するJavaBeansを以下にお見せします。

Memo.java
public class Memo {
    private int id;
    private String title;
    private String content;

    public Memo() {}

    public Memo(String title, String content) {
        this.title = title;
        this.content = content;
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getTitle() {
        return title;
    }

    public void setTitle(String title) {
        this.title = title;
    }

    public String getContent() {
        return content;
    }

    public void setContent(String content) {
        this.content = content;
    }
}

Memoクラスには、メモのID、タイトル、内容が含まれます。これらのフィールドに対するgetterおよびsetterメソッドが含まれています。また、Memoクラスには引数を持たないデフォルトコンストラクタと、タイトルと内容を引数とするコンストラクタが含まれています。

JSPでの使用方法

それでは、サーブレット側でリクエストスコープにインスタンスを保存し、JSPで表示してみましょう。リクエストスコープにインスタンスを保存する構文は以下になります。

Servlet.java
Memo memo = new Memo();
memo.setId(1);
memo.setTitle("タイトル");
memo.setContent("内容")
request.setAttribute("memoList", memoList);

先ほどのMemoクラスのインスタンスを生成し、適当な内容を設定しました。HttpServletRequestのsetAttribute()メソッドを用いて、インスタンスを保存しています。第1引数は、保存するインスタンスを管理する名前を指定しており、「属性名」といいます。Mapにおけるキーのような役割を果たします。第2引数には、保存するインスタンスを指定します。

次にJSP側の処理です。まずはコードを確認しましょう。

jsp
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>

<% //取得するインスタンスのMemoクラスをインポート %>
<%@ page import= "memoApp.Memo" %>

<% //リクエストスコープからインスタンスを取得
Memo memo = (Memo) request.getAttribute("memo");%>

    
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>メモアプリ</title>
</head>
<body>
<p><%= memo.getId() %></p>
<p><%= memo.getTitle() %></p>
<p><%= memo.getContent() %></p>
</body>
</html>
                        

上記のコードでは、まずスコープから取得するMemoクラスをインポートしています。そして、getAttribute()メソッドを使って、リクエストスコープからインスタンスを取り出しています。取り出したインスタンスに対して、getterメソッドを呼び出すことで、各プロパティの値を出力します。このJSPを実行した画面が以下になります。

Java-9_20

サーブレット側で作成されたインスタンスがJSPに渡り、表示されているのが分かります。基本的なJavaBeansの受け渡しは、このように実現されます。

EL式

JSPでスコープに保存されたJavaBeansインスタンスのプロパティの出力は、EL式を使うことで、よりシンプルに記述することが出来ます。EL式とはExpression Languageの略で、構文は以下のようになります。

${属性名} :スコープに保存されているインスタンスを取得。

${属性名.プロパティ} : スコープに保存されているインスタンスのプロパティを取得。

先ほどのmemoインスタンスの出力をEL式を用いて行うと以下のようになります。

jsp
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>メモアプリ</title>
</head>
<body>
<p>${memo.id}</p>
<p>${memo.title};</p>
<p>${memo.content}</p>
</body>
</html> 

記述が簡略化されたのが分かります。

本チャプターを通じて、JavaBeanをスコープに保存し、取り出す方法を学びました。次チャプターからは、いよいよメモアプリの作成に取り掛かっていきます。

Lesson 9 Chapter 10
メモアプリを作成する

ここまでのレッスンを通して、MVCモデルを学んできました。ここからは、実際に簡易なメモアプリを作っていきます。

必要な機能を考える

作り始める前に、まずメモアプリに必要な機能を決める必要があります。今回は、簡易なものを想定しているので、以下の機能に絞ります。

  • メモの作成

  • メモの表示(一覧)

このふたつの機能を備えたメモアプリを作成していきましょう。まずは、JSPから書いていきます。

JSPでUIを実装する

今回のメモアプリは、一つの画面数で作っていきます。まずは、「メモの作成」のためのフォームを作っていきます。フォームを使うことで、ユーザーの入力をControllerに送ることができます。フォームは以下のように実装します。

フォーム
<form action="/memoAppSample1/Sample" method="post">
  タイトル:<input type="text" name="title"><br>
  内容:<input type="text" name="content"><br>
  <input type="submit" value="追加">
</form>

まずは、フォームタグの中の設定から見ていきます。

  • action属性: フォームが送信されたときに、データが送信される先のURLを指定します。"/memoApp/Home"を指定しているので、Home.javaのサーブレットにリクエストが送られることになります。

  • method属性: データの送信方法を指定します。"post"を指定しているため、サーブレット側では、POSTメソッドで受け取る処理をすることになります。

フォームの中には、タイトルと内容のテキスト入力欄と送信用のボタンを実装しました。これで、フォームの実装は完了です。見た目を確認するために、一度実行してみましょう。現状のJSPコードの全体は、以下のようになっています。

index.jsp
<%@ page language="java" contentType="text/html;charset=UTF-8"
    
<!DOCTYPE html>

<html>

<head>
<meta charset="UTF-8">
<title>メモアプリ</title>

</head>
<body>

<form action="/memoApp/Home" method="post">
タイトル:<input type="text" name="title"><br>
内容:<input type="text" name="content"><br>
<input type="submit" value="追加">
</form>
</body>
</html>

では、実行してみましょう。index.jspが以下のように「webappフォルダ」の直下にあることを確認します。

Java-9_21

index.jspを右クリックし、"実行(R)"→"サーバーで実行"を押下します。チャプター7で追加した、Tomcat 10を選択し、"完了(F)"を押下しましょう。

Java-9_22

ブラウザが立ち上がり、以下のような画面が表示されます。

Java-9_23

無事、フォームが実装されていることが分かります。

では次に、メモの一覧表示をする部分を作っていきます。tableタグを使って、メモの一覧表示を行っていきます。表示には、前チャプターで学んだEL式を使います。コードは以下に示します。

一覧表示
<table>
  <thead>
    <tr>
      <th>Title</th>
      <th>Content</th>
   </tr>
  </thead>
  <tbody>

    <%
      List<Memo> memoList = (List) request.getAttribute("memoList");
      for (Memo memo : memoList) { 		
   %>
        <tr>
            <td><%= memo.getTitle() %></td>
            <td><%= memo.getContent() %></td>
            
        </tr>
    <%} %>

  </tbody>
</table>

少し複雑ですが、順を追って解説していきます。まずtableタグを構成する各種タグについて見ていきましょう。

  • thead: 表の見出し行を示すタグ

  • tbody: 表の本体を示すタグ

  • th: 表の見出しを表すタグ

  • tr: 行を表すタグ

  • td: 表のセルを表すタグ

tableタグ内では、Javaのfor文を使用しています。スコープからmemolistを取り出し、memolist内の各リストに対して処理を行っています。

では、先ほどのメモ作成用フォームと、メモ一覧表示用のテーブルを合わせて表示していきましょう。JSPのコード全体は、以下のようになります。

index.jsp
<%@ page language="java" contentType="text/html;charset=UTF-8"
<%@ page import="model.Memo" %>
<%@ page import="java.util.List" %>
    
<!DOCTYPE html>

<html>

<head>
<meta charset="UTF-8">
<title>メモアプリ</title>

</head>
<body>

<form action="/memoApp/Home" method="post">
  タイトル:<input type="text" name="title"><br>
  内容:<input type="text" name="content"><br>
  <input type="submit" value="追加">
</form>


<table>
  <thead>
    <tr>
      <th>タイトル</th>
      <th>コンテンツ</th>
   </tr>
  </thead>
  <tbody>

    <%
      List<Memo> memoList = (List) request.getAttribute("memoList");
      for (Memo memo : memoList) { 		
   %>
        <tr>
            <td><%= memo.getTitle() %></td>
            <td><%= memo.getContent() %></td>
            
        </tr>
    <%} %>

  </tbody>
</table>

</body>
</html>

実行すると以下のようになります。

Java-9_24

これで、JSPの実装は完了です。次は、サーブレットの実装を行っていきます。

サーブレットにJSPファイルの読み込み、フォワード処理を実装する

サーブレットの実装を始めていきます。チャプター6で作成した、MemoHome.javaというサーブレットクラスを編集していきます。ではまず、最初にURLに対してアクセスがあった際に、先ほど編集したindex.jspにフォワードする実装を行っていきます。GETリクエストに対応するため、GETメソッドを以下のように編集しましょう。

MemoHome.java
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
  
  RequestDispatcher dispatcher = request.getRequestDispatcher("/WEB-INF/index.jsp");
  dispatcher.forward(request, response);
}

フォワード処理を実装しました。では、実際に動くか試してみましょう。index.jspを"webappフォルダ"直下から、"WEB-INFフォルダ"内に移します。ディレクトリ構成は以下のようになっています。

Java-9_25

memoAppを右クリックし、"実行(R)"→"サーバーで実行"を押下します。Tomcat 10を選択し、"完了(F)"を押下しましょう。

ブラウザが立ち上がるので、"localhost:8080/memoApp/MemoHome"とURLを入力します。先ほど作ったJSPのページが表示されれば、無事フォワードが実装できています。

次に、JSPで設定したフォームが送られて来た場合の処理を書いていきます。

POSTリクエスト時の処理を実装する

サーブレット側の処理を書く前に、受け取ったメモに関する情報をまとめるMemoクラスのJavaBeansを作成します。"model"というパッケージを作成し、その中にMemoクラスを作成しましょう。

Java-9_26

Memoクラスの内容は以下のようになります。

Memo.java
package model;

import java.io.Serializable;

public class Memo implements Serializable {
    private int id;
    private String title;
    private String content;

    public Memo() {
    }

    public Memo(int id, String title, String content) {
    	this.id = id;
    	this.title = title;
    	this.content = content;
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getTitle() {
        return title;
    }

    public void setTitle(String title) {
        this.title = title;
    }

    public String getContent() {
        return content;
    }

    public void setContent(String content) {
        this.content = content;
    }
}

では、作成したMemoクラスを用いてMemoHomeのPOSTメソッドを書いていきます。フォームで受け取った値をMemoクラスのインスタンスを生成して代入していきます。コードは以下のようになります。

MemoHome.java
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		 
  String title = request.getParameter("title");
    String content = request.getParameter("content");
    
    Memo memo = new Memo();
    memo.setTitle(title);
    memo.setContent(content);
    
    //DBへの追加処理
    
    response.sendRedirect(request.getContextPath() + "/MemoHome");

}

まず、getParameter()メソッドを用いて、フォームから値を取り出します。そして、新しく生成したインスタンスに代入していきます。このmemoをDBに追加して、リダイレクトするPRGパターンです。※DBに関する処理は次チャプターで書いていきます。

それでは、実行してみましょう。MemoHomeのコード全体は以下のようになっています。

MemoHome.java
package servlet;

import java.io.IOException;

import jakarta.servlet.RequestDispatcher;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import model.Memo;


public class MemoHome extends HttpServlet {
	private static final long serialVersionUID = 1L;
       
    
    public MemoHome() {
        super();
    }

	
	protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		RequestDispatcher dispatcher = request.getRequestDispatcher("/WEB-INF/index.jsp");
		dispatcher.forward(request, response);
	}

	protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		 
		String title = request.getParameter("title");
	    String content = request.getParameter("content");
	    
	    Memo memo = new Memo();
	    memo.setTitle(title);
	    memo.setContent(content);
	    
	    //DBへの追加処理
	    
	    response.sendRedirect(request.getContextPath() + "/MemoHome");
 
	}

}

memoAppを右クリックし、"実行(R)"→"サーバーで実行"を押下します。Tomcat 10を選択し、"完了(F)"を押下しましょう。

ブラウザが立ち上がるので、"localhost:8080/memoApp/MemoHome" とURLを入力します。先ほど作ったJSPのページが表示されるので、"追加"ボタンを押下しましょう。エラーにならず、画面が変わらなければ、リダイレクトができています。

本チャプターでは、JSPとサーブレットの実装を行いました。次チャプターでは、DBとアプリケーションを連携させ、作成したメモがDBに格納されるようにしていきます。

Lesson 9 Chapter 11
DBとの連携

本チャプターでは、作成中のmemoAppとDBを連携していきます。DBはレッスン8で使用した、MySQLを使います。

プロジェクトにJDBCドライバを追加

MySQLに接続するためのJDBCドライバをプロジェクトに追加します。"memoApp"プロジェクトを右クリックし、"プロパティー(R)"を押下します。

左側のリストから、"Javaのビルド・パス"を選択し、"ライブラリー(L)"タブからクラスパスを選択して、右側にある"外部JARの追加"ボタンを押下します。

Java-9_27

JARファイルを選択するウィンドウが開くので、MySQLと一緒にダウンロードした、Connector JのJARファイルを選択します。基本的には、"ローカルディスク(C:)/Program Files/MySQL/Connector J"の中に格納されています。

JARファイルの選択が完了したら、右下にある"適用して閉じる"ボタンを押下しましょう。

Java-9_28

これで、MySQL用のJDBCドライバをプロジェクトに追加できました。

基本的なテーブル設計

JDBCドライバを追加したので、memoAppで使うテーブル設計を行っていきます。

Memoクラスで、定義した、id、title、contentが基本的な要素になってきます。

id列はINT型で、主キーとして指定しましょう。title列とcontent列はそれぞれ文字列で格納できるようにします。

DDLの実行

データベースを作成するために、MySQL Shellを起動します。Windowsの検索窓からMySQL Shellを検索し、実行します。

Java-9_29

以下のコマンドを入力して、MySQL Serverへ接続します。パスワードが要求された場合は、MySQLインストール時に設定したパスワードを入力します。

MySQL Shell
 \connect root@localhost:3306
Java-9_30

以下のコマンドを入力して、SQL言語へ切り替えます。

MySQL Shell
 \sql

つづけて以下のSQL文で、メモアプリ用のデータベースを作成します。

MySQL Shell
CREATE DATABASE IF NOT EXISTS memoAppDb; 

つづけてUSE文で、"memoAppDb"に操作を行うことを伝えます。

MySQL Shell
USE memoAppDb;

では、"memoAppDb"の中にテーブルを作成していきましょう。以下のSQLを打ち込んで"memos"テーブルを作ります。

MySQL Shell
CREATE TABLE memos (
    id INT AUTO_INCREMENT PRIMARY KEY,
    title VARCHAR(50),
    content VARCHAR(255)
);

以上のSQL文では、memosという名前のテーブルを作成しています。テーブルは、id、title、contentの3つのカラムで構成されています。id列はINT型で、PRIMARY KEYとして指定され、AUTO_INCREMENTというオプションが設定されているため、1ずつ増分する値が自動的に挿入されます。title列とcontent列はそれぞれVARCHAR(50)、VARCHAR(255)の文字列型です。

VARCHARとは

VARCHAR(n)は、文字列を格納するためのデータ型の1つで、最大長がn文字であることを示します。nは1以上255以下の整数値で指定できます。例えば、VARCHAR(50)は、最大で50文字の文字列を格納することができます。

これでDB側の準備は整いました。それでは、メモ作成時にデータを保存する処理を実装していきます。

データ保存処理の実装

まず、"modelフォルダ"に"MemoDAOクラス"を作成します。まずは、DB接続情報を持つ定数と、リソースを格納するための変数を用意します。コードは以下になります。

MemoDAO.java
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;

public class MemoDAO {
	  private static final String MYSQL_DRIVER = "com.mysql.cj.jdbc.Driver";  
	  private static final String JDBC_CONNECTION = "jdbc:mysql://localhost:3306/memoAppDb";
	  private static final String USER = "root";
	  private static final String PASS = "*****";
	  
	  private static Connection con = null;
	  private static PreparedStatement ps = null;
	  private static ResultSet rs = null;
}

"*****"の箇所には、ご自分のパスワードを入力してください。次に、先ほど作った、memosテーブルにレコードを追加するaddMemo()メソッドを定義していきます。コードは以下のようになります。

MemoDAO.java
public void addMemo(Memo memo) {
  try {
    
    Class.forName(MYSQL_DRIVER);

    con = DriverManager.getConnection(JDBC_CONNECTION, USER, PASS);
    ps = con.prepareStatement("INSERT INTO memos (title, content) VALUES (?, ?)");
      ps.setString(1, memo.getTitle());
      ps.setString(2, memo.getContent());
      ps.executeUpdate();
    } catch (ClassNotFoundException | SQLException e) {
      e.printStackTrace();
    } finally {
      try {
        if (ps != null) ps.close();
        if (con != null) con.close();
      } catch (SQLException e) {
        e.printStackTrace();
      }
  }
}

このメソッドでは、Memoオブジェクトを引数として受け取り、それをmemosテーブルに挿入します。

まず、MySQLドライバをロードしてJDBC接続を確立し、SQL文を準備します。SQL文には、"INSERT INTO memos (title, content) VALUES (?, ?)"という挿入用のSQL文が含まれており、2つのパラメータを持っています。

PreparedStatementのsetString()メソッドを使って、Memoオブジェクトからタイトルと内容を取得し、それらをSQL文のパラメータにバインドします。

最後に、更新用のexecuteUpdate()メソッドを呼び出し、変更をデータベースに反映します。

try-catch-finallyブロックを使って、例外が発生した場合に適切に処理を行います。finallyブロックでは、PreparedStatementとConnectionをクローズして、リソースの解放を行います。

データ取得処理の実装

次に、作成したメモデータを一覧表示するために取得する処理を実装していきます。MemoDAOクラス内に新たにgetMemoList()メソッドを定義していきます。コードは以下に示します。

MemoDAO.java
public List<Memo> getMemoList() {

  List<Memo> memoList = new ArrayList<Memo>(); 

  try {
    
    Class.forName(MYSQL_DRIVER);
    
    con = DriverManager.getConnection(JDBC_CONNECTION, USER, PASS);
    ps = con.prepareStatement("SELECT * FROM memos");
    rs = ps.executeQuery();

      while (rs.next()) {
          int id = rs.getInt("id");
          String title = rs.getString("title");
          String content = rs.getString("content");
          Memo memo = new Memo(id, title, content);
          memoList.add(memo);
      }
      
  } catch (ClassNotFoundException | SQLException e) {
      e.printStackTrace();
    } finally {
      try {
        if (rs != null) rs.close();
        if (ps != null) ps.close();
        if (con != null) con.close();
      } catch (SQLException e) {
        e.printStackTrace();
      }
  }
  return memoList;
}

このコードは、memosテーブルから全てのメモを取得してMemoオブジェクトのリストとして返すためのメソッドです。JDBCドライバをロードし、データベースに接続します。次に、SQL文 "SELECT * FROM memos" を実行して、memosテーブルからすべてのレコードを取得します。ResultSetオブジェクトを使って取得したレコードを1つずつ処理し、Memoオブジェクトを生成してリストに追加します。最後に、リソースを解放してからメモのリストを返します。

これで、MemoDAOの実装は以上になります。コードの全体像は以下のようになります。

MemoDAO.java
package model;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;

public class MemoDAO {
  private static final String MYSQL_DRIVER = "com.mysql.cj.jdbc.Driver";  
  private static final String JDBC_CONNECTION = "jdbc:mysql://localhost:3306/memoAppDb";
  private static final String USER = "root";
  private static final String PASS = "sting1028";
  
  private static Connection con = null;
  private static PreparedStatement ps = null;
  private static ResultSet rs = null;

  public List<Memo> getMemoList() {

    List<Memo> memoList = new ArrayList<Memo>(); 
    
      try {
        
        Class.forName(MYSQL_DRIVER);
        
        con = DriverManager.getConnection(JDBC_CONNECTION, USER, PASS);
        ps = con.prepareStatement("SELECT * FROM memos");
        rs = ps.executeQuery();

          while (rs.next()) {
              int id = rs.getInt("id");
              String title = rs.getString("title");
              String content = rs.getString("content");
              Memo memo = new Memo(id, title, content);
              memoList.add(memo);
          }
          
      } catch (ClassNotFoundException | SQLException e) {
          e.printStackTrace();
        } finally {
          try {
            if (rs != null) rs.close();
            if (ps != null) ps.close();
            if (con != null) con.close();
          } catch (SQLException e) {
            e.printStackTrace();
          }
      }
      return memoList;
  }

  
  
  public void addMemo(Memo memo) {
    try {
      
      Class.forName(MYSQL_DRIVER);

      con = DriverManager.getConnection(JDBC_CONNECTION, USER, PASS);
      ps = con.prepareStatement("INSERT INTO memos (title, content) VALUES (?, ?)");
      
        ps.setString(1, memo.getTitle());
        ps.setString(2, memo.getContent());
        ps.executeUpdate();
      } catch (ClassNotFoundException | SQLException e) {
        e.printStackTrace();
      } finally {
        try {
          if (ps != null) ps.close();
          if (con != null) con.close();
        } catch (SQLException e) {
          e.printStackTrace();
        }
    }
  }
}

DBへのメモの追加と、DBからのメモの取得のメソッドを定義したので、サーブレット内でメソッドを呼び出す実装を行っていきます。

サーブレット側の実装

まずは、doGetメソッドから編集していきます。GETリクエストに対しては、リクエストされた時点でのDBにあるメモ一覧を取得してリクエストスコープに格納し、jspへフォワードします。実装は以下のようになります。

MemoHome.java
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
  
  // MemoDAOのgetMemoList()メソッドの呼び出し
  request.setAttribute("memoList", memoDao.getMemoList());
  
  RequestDispatcher dispatcher = request.getRequestDispatcher("/WEB-INF/index.jsp");
  dispatcher.forward(request, response);
}

このメソッドは、リクエストを受け取り、MemoDAOを使用してデータベースからメモリストを取得し、取得したメモリストを"memoList"という名前のリクエストスコープに設定しています。

次にdoPostメソッドを編集していきます。このメソッドでは、新しいメモを作成するフォームを受け取った際に、フォームからメモ情報を取り出し、MemoDAOのaddMemo()メソッドを呼び出して、DBに保存する処理を行っていきます。コードは以下のようになります。

MemoHome.java
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		 
  String title = request.getParameter("title");
    String content = request.getParameter("content");
    
    Memo memo = new Memo();
    memo.setTitle(title);
    memo.setContent(content);
    
    
    //DBへ追加する処理
    
    memoDao.addMemo(memo);

    
    response.sendRedirect(request.getContextPath() + "/MemoHome");

}

前チャプターでは、フォームから値を取り出し、Memoオブジェクトに代入するところまで実装していました。今回は、MemoDAOのaddMemo()メソッドを呼び出して、データベースにメモを追加しています。そして最後にMemoHomeにリダイレクトすることで、追加されたメモをリスト表示する画面に遷移します。

以上で、サーブレット側の実装も完了しました。全体像は以下のようになっています。

MemoHome.java
package servlet;

import java.io.IOException;

import jakarta.servlet.RequestDispatcher;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import model.Memo;
import model.MemoDAO;


public class MemoHome extends HttpServlet {
	private static final long serialVersionUID = 1L;
       
    
    public MemoHome() {
        super();
    }
    
    private MemoDAO memoDao;
    
    public void init() {
    	memoDao = new MemoDAO();
    }

	
	protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		
		request.setAttribute("memoList", memoDao.getMemoList());
		
		
		RequestDispatcher dispatcher = request.getRequestDispatcher("/WEB-INF/index.jsp");
		dispatcher.forward(request, response);
	}

	protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		 
		String title = request.getParameter("title");
	    String content = request.getParameter("content");
	    
	    Memo memo = new Memo();
	    memo.setTitle(title);
	    memo.setContent(content);
	    
	    
	    //DBへ追加する処理
	   
	    memoDao.addMemo(memo);
		
	    
	    response.sendRedirect(request.getContextPath() + "/MemoHome");
 
	}

}

では、実際にDBとの連携ができているのか、実行してみましょう。

memoAppを右クリックし、"実行(R)"→"サーバーで実行"を押下します。Tomcat 10を選択し、"完了(F)"を押下しましょう。

ブラウザが立ち上がるので、"localhost:8080/memoApp/MemoHome" とURLを入力します。先ほど作ったJSPのページが表示されるので、任意の内容を入力し、"追加"ボタンを押下しましょう。

Java-9_31

以下のように表示されれば、DB連携ができています。

Java-9_32

これで、追加機能と表示機能をもったメモアプリが完成しました。次のチャプターでは、エラーログの出力について見ていきましょう。

Lesson 9 Chapter 12
エラーログの出力

前チャプターで作成したメモアプリにエラーログの出力を実装するのが本チャプターのねらいです。エラーログの出力処理にはSLF4J(Simple Logging Facade for Java)というライブラリを用います。SLF4Jは、ログシステム用のファサードです。

Facadeとは

Facade(ファサード)は「表の立つ壁」を意味する英単語です。ソフトウェア開発において、Facadeパターンは、複雑な処理を抱えるシステムに対して、シンプルなインタフェースを提供するデザインパターンの一つです。Facadeパターンは、システム内部の複雑な構造を、外部から隠蔽することで、システムの利用者がより簡単にシステムを利用できるようにすることを目的としています。

Facadeパターンを実装する場合、複雑なシステムの内部にあるクラスやメソッドを、シンプルなインタフェースを持つファサード(Facade)クラスにまとめます。ファサードクラスは、利用者からのリクエストを受け取り、内部のシステムを適切に呼び出して処理を行います。このとき、利用者はファサードクラスを介してシステムにアクセスすることになるため、システム内部の複雑な構造を知る必要がありません。

logファサードライブラリとlogライブラリの関係

一般的に、アプリケーションのログ出力にはlogライブラリが使用されます。logライブラリには、Log4jやjava.util.loggingなどの複数の種類があります。これらのライブラリは、ログ出力の機能を提供します。

しかし、logライブラリは、そのまま使うと設定が煩雑であり、アプリケーションのログ出力処理に直接組み込むと依存度が高くなります。この問題を解決するために、logファサードライブラリが開発されました。

logファサードライブラリは、logライブラリをラッピングし、簡単に設定を行い、アプリケーションコードからログ出力処理を呼び出せるようにすることで、アプリケーションとlogライブラリの間に抽象的な層を提供します。これにより、アプリケーションはlogライブラリに依存することなく、柔軟かつ効率的にログ出力処理を実行できます。

SLF4Jは、logファサードライブラリの一種で、LogbackやLog4jなどの複数のlogライブラリをラッピングしています。SLF4Jを使用することで、アプリケーションコードからログ出力処理を呼び出すことができます。また、設定ファイルを変更することで、実際に使用するlogライブラリを切り替えることもできます。

SLF4Jを使用してログ出力処理を実装する

それでは、実際にメモアプリに対して、SLF4Jを使用して、ログ出力処理を行ってみましょう。

まず、SLF4Jを使用するためには、SLF4JのAPIと実装ライブラリをプロジェクトに導入する必要があります。まずは以下のリンクへアクセスします。

https://www.slf4j.org/download.html

"Maven central"を押下します。

Java-9_33

遷移したら"org/slf4j/slf4j-api/2.0.6.jar"と、"org/slf4j/slf4j-simple/2.0.6.jar"をダウンロードします。

ダウンロードしたふたつのjarファイルを"WEB-INFフォルダ"下にある"libフォルダ"の中に追加します。

SLF4Jでは、ログ出力にLoggerというクラスを使用します。Loggerクラスのインスタンスを取得するには、LoggerFactory.getLogger()メソッドを使用します。このメソッドには、Loggerの名前としてクラス名を渡します。MemoHome.javaに実装してみましょう。

MemoHome.java
package servlet;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class MemoHome extends HttpServlet {
	private static final Logger logger = LoggerFactory.getLogger("MemoHome");

  ----

}

Loggerクラスは、ログを出力するためのいくつかのメソッドを提供しています。ログの出力レベルには、TRACE、DEBUG、INFO、WARN、ERROR、FATALの6つがあります。ログ出力のメソッドには、それぞれtrace()、debug()、info()、warn()、error()、fatal()があります。以下は、Loggerを使用してログを出力する例です。

MemoHome.java
package servlet;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class MemoHome extends HttpServlet {
	private static final Logger logger = LoggerFactory.getLogger("MemoHome");

  ----

  protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		
    logger.info("info message");
    logger.warn("warn message");
    logger.error("error message");
		
    
    ------

    
	}

}

実行すると、コンソールに以下のようなログが出力されます。

Java-9_34

getLogger()メソッドで設定したクラス名と、出力レベル、メッセージ内容が表示されているのが分かります。

以上が、SLF4Jを使用してログ出力処理を実装するための基本的な手順になります。

SLF4Jは、複数のログライブラリを同時に使用することを可能にし、さまざまなライブラリに依存しない方法でアプリケーションでのログ出力を管理することができます。このため、SLF4Jは、Javaアプリケーションの開発において必須のツールの一つとなっています。