DAO クラスと Model クラスを使ってデータベース操作をしてみる【MVC モデル】 Part 1

Java

この記事では MVC モデルと呼ばれる設計思想に基づいて、 DAO クラス・Model クラスの作り方や運用の仕方を解説します。

DAO クラスはDatabase Access Object、つまりデータベースにアクセスするためのオブジェクトの頭文字を抜き出した略で、文字通りデータベース操作を担当するクラスです。

Model クラスは処理を担当するクラスです。Servlet によって割り振られた仕事を処理し、場合によってはデータベースアクセスの仕事を DAO クラスに指示します。

作業環境

  • Windows 10
  • Eclipse 4.4
  • Tomcat 8.0
  • MySQL 5.7系
スポンサーリンク

MVC モデルという考え方(設計思想)

Web アプリケーションを効率的に開発・運用するにあたって、MVC モデルと呼ばれる設計思想が存在します。

(正確にはここで説明するものは MVC モデル2 と呼ばれる Web 開発に特化した設計思想です。ほかにも原初 MVC とか Cocoa MVC というような、MVC をもとにした設計思想はたくさん存在します)

Java Servlet における MVC モデル

MVC は Model,View,Controller の頭文字をとったものです。

以下のような Servlet,Model(DAO),JSPの使い分けをおこなう設計が MVC モデルの基本形です。

Model処理をおこなうクラス。主となる処理をメソッド化してまとめたもの。
システムの本体に該当する重要な部分といえます。
Viewクライアントが実際に見ることになる画面表示や、クライアントからの入力の受け付けを担当する部分。
Servlet からのフォワードというお願いによって、データの表示をおこないます。
Controllerユーザのリクエストに基づいて、Model や View を制御する Servlet クラス。
入力によっては if 文で分岐して処理やフォワード先を変更したりします。
自分自身はデータの処理や入出力はおこなわず、あくまで Model と View のコントロールに徹します。

MVC でのデータや処理の流れ方

MVC モデルにおける処理の流れはこんな感じ。

  1. ユーザ(クライアント)が入力(リクエスト)
  2. 【Controller -> Model】
    Controller(Servlet) が Model にデータを渡して処理を依頼
  3. 【Model -> DAO】
    Model がデータ整理。DAO に渡してデータベースアクセスを依頼
  4. 【DAO -> データベース 】
    DAO が Model から渡ってきたデータをもとに SQL を発行し、データベースにアクセス、データを取得。
  5. 【DAO -> Model】
    DAO から戻ってきた結果をもとに、Model が処理
  6. 【Model -> Controller】
    Model が処理結果を Controller(Servlet) に渡す
  7. 【Controller -> View】
    Model から返ってきた結果を Controller(Servlet) が View(JSP) に渡す
  8. View が画面を生成し、ユーザ(クライアント)にレスポンス

データベースとファイルの準備

データベースにあるテーブルのデータをすべて表示するという仕組みを構築し、実際に確認してみます。

データの作成

まずはデータベースとそのデータを用意します。

/* データベースに items という名前のテーブルを作成 */
create TABLE items (
id int AUTO_INCREMENT NOT NULL PRIMARY KEY,
name varchar(30),
price int,
quantity int
)
DEFAULT CHARSET=utf8;

/* 作成した items テーブルにテスト用のデータを挿入 */
INSERT INTO
	items (name,price,quantity)
VALUES
	("りんご",120,50),
	("バナナ",80,50),
	("みかん",100,50),
	("もも",200,50)

データベースを作成したら上の SQL 文を実行し、テスト用のデータが入ったテーブルを用意します。

items テーブルには果物と、その果物の値段、在庫の数が格納されている、という状態になりました。

ファイルの作成

ある程度形のある Web アプリケーションになると、プログラムの分担が効率的に開発・運用するカギになってきます。

JSP は画面の表示、Bean クラスはデータの保存と運搬、DAO クラスはデータベース操作、そして Servlet はそれらの統括といった具合です。

よって、今回の記事では以下のファイルを作成します。

  • DAO クラス
  • Bean クラス
  • JSP
  • Model クラス
  • Servlet クラス

データベースアクセスのための DAO クラス

まずは DAO クラスを作成します。

DAO クラスはクラス名末尾に DAO とつけるのがわかりやすくていいと思います。また、今回は items テーブルを操作するための DAO クラスにしようと思うので、テーブル名を名前につけて、ファイル名は ItemsDao にします。

package database;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;

/**
 * items テーブルへアクセスするための DAO クラス
 */
public class ItemsDao {
	private Connection con = null;
	private ResultSet rs = null;
	private PreparedStatement ps = null;

	private String url = "jdbc:mysql://localhost:3306/servlet?characterEncoding=UTF-8&serverTimezone=JST&autoReconnect=true&useSSL=false";
	private String user = "root";
	private String password = "password";

	/**
	 * items テーブルのデータを全て取得します。
	 *
	 * @return rs
	 * @throws SQLException
	 */
	public ResultSet selectAll() throws SQLException {

		try {

			// JDBCドライバのロード
			Class.forName("com.mysql.jdbc.Driver");

			// DB 接続
			con = DriverManager.getConnection(url, user, password);

			// SQL文を生成
			ps = con.prepareStatement("SELECT * FROM items");

			// SQLを実行
			rs = ps.executeQuery();

		} catch (ClassNotFoundException ce) {

			// JDBCドライバが見つからなかったときの処理
			ce.printStackTrace();
		}

		return rs;
	}

	/**
	 * データベース接続を切断
	 */
	public void close() {

		try {

			// データベースとの接続を切断
			if(con != null) {
				con.close();
			}
			if(ps != null) {
				ps.close();
			}
			if(rs != null) {
				rs.close();
			}

		} catch (SQLException se) {

			// データベースからの切断に失敗した場合
			se.printStackTrace();
		}
	}

}

とりあえず selectAll というテーブル内のすべてのデータを取得するメソッドと、データベースとの接続を解除するメソッドのみを作成しました。

(データベース名は servlet となっています)

データを保持するための Bean クラス

次に items テーブルのデータを運ぶための入れ物、 Bean クラスを作成します。

わかりやすいように名前は ItemBean にします。

package database;

import java.io.Serializable;

public class ItemBean implements Serializable {
	private static final long serialVersionUID = 1L;

	// 引数のないコンストラクタ
	public ItemBean() {}

	// 格納したいデータの変数
	private int id;
	private String name;
	private int price;
	private int quantity;

	// 変数に対する getter と setter
	public int getId() {
		return id;
	}
	public void setId(int id) {
		this.id = id;
	}
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public int getPrice() {
		return price;
	}
	public void setPrice(int price) {
		this.price = price;
	}
	public int getQuantity() {
		return quantity;
	}
	public void setQuantity(int quantity) {
		this.quantity = quantity;
	}

}

items テーブルのデータなので、テーブルのカラム名を参考に変数を作った上で、Bean のルールに則ってクラスを作りました。

画面表示のための JSP ファイル

次に View となる JSP を作成します。

ファイル名は itemList.jsp にすることにします。

今回は WebContent/database ディレクトリの中に itemList.jsp を作成します。

<%@ page language="java" contentType="text/html; charset=UTF-8"
	pageEncoding="UTF-8"%>
<%@ page import="java.util.ArrayList"%>
<%@ page import="database.ItemBean"%>
<%
	ArrayList<ItemBean> beanList = (ArrayList<ItemBean>) request.getAttribute("beanList");
%>
<!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">
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css" integrity="sha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T" crossorigin="anonymous">
<title>アイテム一覧</title>
</head>
<body>

	<div class="container">
		<div class="row">
			<h1>アイテム一覧</h1>

			<table class="table">
				<tbody>
					<tr class="bg-dark text-white text-center">
						<th scope="col">アイテムID</th>
						<th scope="col">アイテム名</th>
						<th scope="col">価格</th>
						<th scope="col">在庫数</th>
					</tr>
					<% for(ItemBean bean : beanList){ %>
					<tr class="text-center">
						<td><%= bean.getId() %></td>
						<td><%= bean.getName() %></td>
						<td><%= bean.getPrice() %> 円</td>
						<td><%= bean.getQuantity() %> コ</td>
					</tr>
					<% } // endfor %>
				</tbody>
			</table>

		</div>
	</div>

</body>
</html>

今回は Bootstrap という CSS に詳しくないバックエンドエンジニアでも手軽に見栄えを整えることができる CSS フレームワークを使用しています。

(とはいってもBootstrapのクラスを覚えないといけないけど)

<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css" integrity="sha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T" crossorigin="anonymous">

この link タグによって Bootstrap を読み込んでいます。

これが head タグ内に記述してあると、Bootstrap のサーバーから取得した CSS を使用できるようになり、決まった class 名をつけることで見た目を変更できます。

処理をするためのクラスである Model クラス

次に Model クラスを作成します。 ManagementModel という名前にしました。

package database;

import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;

/**
 *  処理を担当するクラス
 *
 */
public class ManagementModel {

	/**
	 * アイテム一覧を取得して返却する
	 *
	 * @return beanList
	 */
	public ArrayList<ItemBean> getItemsList() {

		// ResultSet を初期化
		ResultSet rs = null;

		// bean を入れるためのリスト
		ArrayList<ItemBean> beanList = new ArrayList<ItemBean>();

		// データベース接続をするために DAO をインスタンス化
		ItemsDao dao = new ItemsDao();

		try {
			// データを取得
			rs = dao.selectAll();

			// 取得したデータを beanList に入れるループ
			while (rs.next()) {
				ItemBean bean = new ItemBean();

				// アイテムID アイテム名 価格 在庫数 を bean にセット
				bean.setId(rs.getInt("id"));
				bean.setName(rs.getString("name"));
				bean.setPrice(rs.getInt("price"));
				bean.setQuantity(rs.getInt("quantity"));

				// データをセットした bean を beanList に追加
				beanList.add(bean);
			}

		} catch (SQLException e) {
			// 例外処理
			e.printStackTrace();
		} finally {
			// 処理終了時に各接続を解除
			dao.close();
		}


		return beanList;
	}
}

とりあえずデータベースのデータをすべて取り出して、それを beanList にして返す、という処理を作りました。

データベースからデータを取り出している処理を書いているにも関わらず、SQL 文を一切書いていません。

DAO クラスや Model クラスを作る利点のひとつはここにあります。

もし DAO クラスがエラーを吐いていることがわかったら SQL に問題があるのではないか、とある程度見当をつけることができるなど、問題の調査範囲を限定できます。

また、チーム開発をするときもこのようなファイルの分割は重要です。

たとえ SQL に疎いメンバーがいても「itemsDao クラスの selectAll を使えばデータがすべて取り出せる」といったことだけ伝えておけば、データベースを操作することができます。

クライアントからリクエストに基づいて、Model と JSP を制御する Servlet

次は Controller となる Servlet です。

ManagementServlet という名前の Servlet を作成します。

protected void doGet(HttpServletRequest request,
		HttpServletResponse response) throws ServletException, IOException {

	// 処理クラスをインスタンス化
	ManagementModel model = new ManagementModel();

	// すべてのデータを beanList として取得
	ArrayList<ItemBean> beanList = model.getItemsList();

	// フォワードの準備
	request.setAttribute("beanList", beanList);

	// アイテム一覧画面に移動
	RequestDispatcher rd = request.getRequestDispatcher("./database/itemList.jsp");
	rd.forward(request, response);
}

doGet メソッド以外は省略しています。

これでようやく形になりました。

Model と DAO もふくめると、データベース接続してデータベースからデータを取得、取得したデータをもとにBeanList 作成、リクエストに乗せてフォワード、という処理をしています。

これだけ複雑な処理をしていても、Servlet はすっきりしていますね。

Model と DAO に然るべき処理を分業することで、Servlet は Controller としておこなうべきことだけに集中でき、スパゲッティコードが生まれにくくなります。これも Model と DAO を作成する利点といえるでしょう。

ManagementServlet を実行すると、このような画面になります。

Select 文によって取得された全データが画面に表示されています。

続きます。

コメント

タイトルとURLをコピーしました