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(コントローラー)から渡されるので、表示するだけの役割です。

登場する専門用語

用語説明
JSPHTMLとJavaが混ざったもの。Webページを作るためのファイル。
JSTLJSPのためのタグライブラリ。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の流れ

  1. ユーザーがWebページでボタンをクリック(名前を入力して送信)。
  2. Controller(サーブレット)がリクエストを受け取る(「この人が名前を送ってきたよ」と受け取る)。
  3. ControllerがModel(DAO)に命令を送る(「名前を使ってデータベースから情報を取ってきて!」)。
  4. DAOがデータベースに問い合わせ、データを取得する(名前に対応する年齢などをデータベースから持ってくる)。
  5. ControllerがView(JSP)を呼び出し、画面を表示する(「この人の名前と年齢を表示してね!」とJSPに命令する)。

まとめ: 専門用語の解説

用語役割/意味イメージ
Modelデータとロジックを管理する部分アプリの頭脳
DAOデータベースへの問い合わせを担当データを取ってくる人
Bean一時的なデータの入れ物データのカバン
View画面の見た目を担当アプリの顔
JSPHTMLのような見た目のファイルHTML+Javaのファイル
Controllerリクエストを受け取る司令塔リクエストの指揮官
ServletControllerのプログラム本体リクエストを受ける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 DBCPHikariCPといった接続プールライブラリを使いましょう。

接続プールのコード例 (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接続を効率化。

良かったらフォローお願いします

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です