MVC(Model-View-Controller)とは?【Java, JSP, Servlet, OracleDB, Tomcat環境】
目次
MVCとは?
MVCは、アプリケーションを3つの役割に分ける設計パターンです。
この3つの役割は、Model(モデル)、View(ビュー)、**Controller(コントローラー)**です。
この分け方をすることで、見た目の部分(画面)と処理の部分(ロジック)を分離でき、メンテナンスがしやすくなります。
1. Model(モデル)とは?
データの管理や、アプリケーションの「中身のロジック」を担当する部分です。
簡単なイメージ
- **「アプリの頭脳」**のような部分。
- データベースとやり取りをして、アプリに必要な情報の取得や計算を行います。
- ユーザーが名前を送ったら、データベースから情報を探して返す人のイメージです。
具体的な動き
- データを保存する(データベースへのINSERT)
- データを読み出す(データベースからSELECT)
- データを更新する(データベースへのUPDATE)
- データを削除する(データベースへのDELETE)
登場する専門用語
| 用語 | 説明 |
|---|---|
| DAO | データベースの操作をする人(仲介役)。SQL文を書く場所。 |
| Bean | データの入れ物。名前や年齢などのデータを一時的に保持するためのクラス。 |
ポイント
- DAOはデータベース操作を担当するクラス。
- Beanはデータの塊を一時的に持つオブジェクト。
- 「ユーザー情報(名前、年齢)」などのデータは一時的にBeanに入れて、DAOがデータベースからその情報を取得します。
2. View(ビュー)とは?
ユーザーが見る「画面の見た目」を担当する部分です。
簡単なイメージ
- **「アプリの顔」**のような部分。
- 画面に何を表示するかが決まる場所です。
- 例えば、**「このボタンは青にする」や「この名前を表示する」**といった指示を担当します。
具体的な動き
- HTMLを使って画面のレイアウトを作ります。
- JSPを使うことで、データを表示できるようになります。
- データはServlet(コントローラー)から渡されるので、表示するだけの役割です。
登場する専門用語
| 用語 | 説明 |
|---|---|
| JSP | HTMLとJavaが混ざったもの。Webページを作るためのファイル。 |
| JSTL | JSPのためのタグライブラリ。if文やループを使うための道具。 |
| EL | 式言語(Expression Language)。${user.name}のように簡単にデータを表示する仕組み。 |
ポイント
- Viewは「見た目の担当」なので、計算や処理をしてはいけません。
- JSPファイルがViewの担当です。
- ViewはHTMLの中にJSTLやELを使って、データを表示するイメージです。
3. Controller(コントローラー)とは?
ユーザーのリクエストを受け取り、アプリの動きを指示する部分です。
簡単なイメージ
- **「アプリの司令塔」**のような存在です。
- View(見た目)とModel(頭脳)の橋渡し役。
- ユーザーが送ったリクエスト(名前を送る、ボタンを押す)に対して、「どの画面を見せるか?」や「データを取る必要があるか?」を判断します。
具体的な動き
- ユーザーがボタンを押す(リクエストが来る)
- Controller(サーブレット)が動き出す
- DAOにデータベースへの命令を送る
- データをJSPに渡して画面を表示する
登場する専門用語
| 用語 | 説明 |
|---|---|
| Servlet | リクエストを受け取り、処理の流れを指示するプログラム。 |
| HttpServletRequest | リクエストの情報(フォームの入力値など)を取り出す道具。 |
| HttpServletResponse | レスポンスの情報(JSPの画面やJSONデータ)を返す道具。 |
| request.setAttribute() | JSPにデータを渡すメソッド。 |
| request.getRequestDispatcher().forward() | 画面を指定して、JSPに遷移する方法。 |
ポイント
- Controllerは司令塔の役割を果たす。
- Servletがリクエストを受けて、どのJSPに進むかを決める。
- View(JSP)に表示するためのデータをrequestオブジェクトに詰めて送る。
まとめ: MVCの流れ
- ユーザーがWebページでボタンをクリック(名前を入力して送信)。
- Controller(サーブレット)がリクエストを受け取る(「この人が名前を送ってきたよ」と受け取る)。
- ControllerがModel(DAO)に命令を送る(「名前を使ってデータベースから情報を取ってきて!」)。
- DAOがデータベースに問い合わせ、データを取得する(名前に対応する年齢などをデータベースから持ってくる)。
- ControllerがView(JSP)を呼び出し、画面を表示する(「この人の名前と年齢を表示してね!」とJSPに命令する)。
まとめ: 専門用語の解説
| 用語 | 役割/意味 | イメージ |
|---|---|---|
| Model | データとロジックを管理する部分 | アプリの頭脳 |
| DAO | データベースへの問い合わせを担当 | データを取ってくる人 |
| Bean | 一時的なデータの入れ物 | データのカバン |
| View | 画面の見た目を担当 | アプリの顔 |
| JSP | HTMLのような見た目のファイル | HTML+Javaのファイル |
| Controller | リクエストを受け取る司令塔 | リクエストの指揮官 |
| Servlet | Controllerのプログラム本体 | リクエストを受けるJavaクラス |
| request | リクエスト情報を管理 | ユーザーからのメッセージ |
| response | レスポンス情報を管理 | サーバーからの返信 |
まとめ
- Modelはデータを管理する人(DAOやBeanがこれを担当)
- Viewは画面の見た目を作る人(JSPがこれを担当)
- Controllerはアプリ全体の動きを指示する司令塔(Servletがこれを担当)
この分け方をすることで、アプリの修正が楽になり、見通しが良くなります。
この仕組みは、大規模なアプリでも使われている超有名なパターンなので、しっかり理解しておくと良いでしょう!
基本的なコード例
ファイル構成
project/
├── src/
│ ├── controller/
│ │ └── UserController.java // Controller
│ ├── model/
│ │ └── User.java // Model (Bean)
│ │ └── UserDAO.java // Model (DAO)
│ └── view/
│ └── user.jsp // View
└── web.xml // web.xml (サーブレットのマッピング)
Model (User.java - JavaBean)
package model;
public class User {
private String name;
private int age;
public User() {}
public User(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
Model (UserDAO.java - DAO)
package model;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
public class UserDAO {
private final String URL = "jdbc:oracle:thin:@localhost:1521:xe";
private final String USERNAME = "system";
private final String PASSWORD = "oracle";
public User getUserByName(String name) {
User user = null;
try (Connection conn = DriverManager.getConnection(URL, USERNAME, PASSWORD)) {
String sql = "SELECT * FROM users WHERE name = ?";
PreparedStatement stmt = conn.prepareStatement(sql);
stmt.setString(1, name);
ResultSet rs = stmt.executeQuery();
if (rs.next()) {
user = new User(rs.getString("name"), rs.getInt("age"));
}
} catch (Exception e) {
e.printStackTrace();
}
return user;
}
}
Controller (UserController.java - サーブレット)
package controller;
import model.User;
import model.UserDAO;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
@WebServlet("/user")
public class UserController extends HttpServlet {
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String name = request.getParameter("name");
UserDAO userDAO = new UserDAO();
User user = userDAO.getUserByName(name);
request.setAttribute("user", user);
request.getRequestDispatcher("/view/user.jsp").forward(request, response);
}
}
View (user.jsp - JSP)
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<title>ユーザー情報</title>
</head>
<body>
<h1>ユーザー情報</h1>
<%
model.User user = (model.User) request.getAttribute("user");
if (user != null) {
%>
<p>名前: <%= user.getName() %></p>
<p>年齢: <%= user.getAge() %></p>
<%
} else {
%>
<p>ユーザーが見つかりませんでした。</p>
<%
}
%>
</body>
</html>
web.xml (サーブレットの設定)
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee" version="3.1">
<display-name>MyApp</display-name>
<servlet>
<servlet-name>UserController</servlet-name>
<servlet-class>controller.UserController</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>UserController</servlet-name>
<url-pattern>/user</url-pattern>
</servlet-mapping>
</web-app>
注意点
1. JSPにビジネスロジックを入れない
なぜダメなのか?
- JSPはView(見た目)専用のファイルであり、データの処理や計算を行う場ではありません。
- JSPにビジネスロジック(計算や条件分岐)を入れると、HTMLとJavaコードが混ざり、可読性が低下します。
- もし後からデザイナーがHTMLを修正したい場合、Javaの処理も理解しなければならなくなり、保守性が悪くなります。
どうすればいいのか?
- 処理は必ずServletやDAOに記述して、JSPは単に「表示するだけ」にしてください。
- JSPでは、**JSTL(JSP標準タグライブラリ)やEL(式言語)**を活用して、表示だけを担当させましょう。
悪い例 (NG)
<%
String userName = request.getParameter("name");
if (userName != null) {
out.print("こんにちは、" + userName + "さん!");
}
%>
良い例 (OK)
// Controller (Servlet)でデータを処理する
request.setAttribute("userName", request.getParameter("name"));
request.getRequestDispatcher("/view/user.jsp").forward(request, response);
JSP側
<h1>こんにちは、${userName}さん!</h1><br></code>ポイント
- ビジネスロジック(名前の取得と条件分岐)はController (Servlet) に記述。
- JSPは変数の表示だけに徹する。JSTLやELを使えば、HTMLの中にJavaコードを書く必要がありません。
2. SQLインジェクションに注意
なぜ気をつけるのか?
- SQLインジェクションとは、悪意のあるSQLをフォーム経由で実行されてしまう攻撃です。
- 例えば、ログインフォームで
admin' --と入力すると、パスワードがなくてもログインできてしまう可能性があります。
どうすればいいのか?
- PreparedStatementを使うことで、SQL文の中のパラメータを自動的にエスケープしてくれます。
- Stringの連結によるSQL文生成は絶対に避ける。
悪い例 (NG)
String sql = "SELECT * FROM users WHERE name = '" + userName + "'";
Statement stmt = conn.createStatement();
ResultSet rs = stmt.executeQuery(sql);
userNameに "admin' --" が入力されると、次のSQLが実行されてしまいます。sqlコードをコピーするSELECT * FROM users WHERE name = 'admin' --';
良い例 (OK)
String sql = "SELECT * FROM users WHERE name = ?";
PreparedStatement stmt = conn.prepareStatement(sql);
stmt.setString(1, userName);
ResultSet rs = stmt.executeQuery();
- PreparedStatementは、SQLの**?(プレースホルダー)を使って入力値を安全に埋め込む**仕組みです。
ポイント
- SQLの中に文字列の連結をしないこと!
- PreparedStatementは必ず使う。パスワードの照合、検索クエリ、データの登録など、すべてに使います。
3. 接続プール (Connection Pool) の活用
なぜ接続プールが必要なのか?
- データベース接続は重い処理のため、毎回接続と切断を行うとパフォーマンスが低下します。
- 1回接続して再利用する仕組み(接続プール)を使えば、効率が大幅に上がります。
どうすればいいのか?
- Apache DBCPやHikariCPといった接続プールライブラリを使いましょう。
接続プールのコード例 (DBCP)
<Resource
name="jdbc/mydb"
auth="Container"
type="javax.sql.DataSource"
driverClassName="oracle.jdbc.OracleDriver"
url="jdbc:oracle:thin:@localhost:1521:xe"
username="system"
password="oracle"
maxActive="20"
maxIdle="10"
maxWait="10000"/>
DBの接続方法 (DAO)
Context initContext = new InitialContext();
DataSource ds = (DataSource) initContext.lookup("java:comp/env/jdbc/mydb");
Connection conn = ds.getConnection();
ポイント
- Tomcatの
context.xmlに接続設定を書く。 - DB接続は1回だけ行い、使いまわす。
- 接続プールを使うことで、パフォーマンスが劇的に向上します。
4. スレッドセーフの注意
なぜ気をつけるのか?
- サーブレットは1つのインスタンスを複数のリクエストが共有します。
- もしサーブレット内にインスタンス変数を使うと、複数のリクエストが同時に変数を上書きしてしまいます。
どうすればいいのか?
- インスタンス変数を使わないこと。
- リクエスト単位のデータは**リクエストスコープの変数(HttpServletRequest)**に格納しましょう。
悪い例 (NG)
javaコードをコピーする<code>public class UserController extends HttpServlet {
private String userName;
protected void doGet(HttpServletRequest request, HttpServletResponse response) {
userName = request.getParameter("name"); // 危険!!
response.getWriter().write("Hello, " + userName);
}
}良い例 (OK)
protected void doGet(HttpServletRequest request, HttpServletResponse response) {
String userName = request.getParameter("name"); // ローカル変数ならOK
response.getWriter().write("Hello, " + userName);
}ポイント
- インスタンス変数は共有されるため、ローカル変数を使いましょう。
5. セキュリティ
なぜ気をつけるのか?
- Webアプリには不正なアクセスを防ぐ仕組みが必要です。
- ログインページへのアクセス制限を行い、セッションIDの乗っ取りを防ぐ必要があります。
どうすればいいのか?
- セッションの有無をチェックする。
- 重要なページはログインが必要にし、未ログインのユーザーはログインページにリダイレクトしましょう。
良い例 (OK)
HttpSession session = request.getSession();
String user = (String) session.getAttribute("user");
if (user == null) {
response.sendRedirect("/login.jsp");
return;
}
ログイン処理のコード
String username = request.getParameter("username");
String password = request.getParameter("password");
UserDAO userDAO = new UserDAO();
User user = userDAO.authenticate(username, password);
if (user != null) {
HttpSession session = request.getSession();
session.setAttribute("user", user);
response.sendRedirect("/home.jsp");
} else {
response.sendRedirect("/login.jsp?error=true");
}
注意点まとめ
- 未ログインのユーザーはリダイレクトする。
- セッション管理はHttpSessionを使う。
- セッションの有効期限を設定しましょう。
- JSPはViewだけ、ビジネスロジックはServletへ。
- セッションとSQLインジェクションには注意。
- 接続プールを使ってDB接続を効率化。

2009年那覇でホームレスになるも沖縄の方々に助けられ、2010年からNPOで地域密着で困窮支援。2016-2024年まで株式会社FM那覇代表取締役。沖縄の支援団体情報ポータルサイト「カケハシオキナワ」設立運営。防災士。コンサル・エンジニア。


