Java/JDBCによるサークル活動管理アプリケーションの開発

このドキュメントでは、Java Servlet/JSP、JDBC、およびMySQLデータベースを使用して、大学のサークル活動を管理するためのシンプルなWebアプリケーションの構築について解説します。本システムは、活動の追加、更新、削除、検索、および一覧表示(CRUD)の機能を提供します。以下に、主要なコンポーネントとその実装の詳細を示します。

データベーススキーマ

活動情報を格納するためのactivitiesテーブルを想定しています。以下はその構造の例です。

CREATE TABLE activities (
    activity_name VARCHAR(255) PRIMARY KEY,
    purpose VARCHAR(255),
    category VARCHAR(255),
    event_date VARCHAR(255),
    location VARCHAR(255),
    target_audience VARCHAR(255),
    description TEXT,
    schedule_details TEXT
);

モデル (model.Activity.java)

サークル活動のデータを表現するPOJO(Plain Old Java Object)クラスです。データベーステーブルの各カラムに対応するフィールドを持ち、活動の属性をカプセル化します。

package model;

/**
 * サークル活動の情報を表すモデルクラス。
 */
public class Activity {
    private String activityName;
    private String purpose;
    private String category;
    private String eventDate;
    private String location;
    private String targetAudience;
    private String description;
    private String scheduleDetails;

    public Activity(String activityName, String purpose, String category, String eventDate, String location,
                    String targetAudience, String description, String scheduleDetails) {
        this.activityName = activityName;
        this.purpose = purpose;
        this.category = category;
        this.eventDate = eventDate;
        this.location = location;
        this.targetAudience = targetAudience;
        this.description = description;
        this.scheduleDetails = scheduleDetails;
    }

    // Getters
    public String getActivityName() { return activityName; }
    public String getPurpose() { return purpose; }
    public String getCategory() { return category; }
    public String getEventDate() { return eventDate; }
    public String getLocation() { return location; }
    public String getTargetAudience() { return targetAudience; }
    public String getDescription() { return description; }
    public String getScheduleDetails() { return scheduleDetails; }

    // Setters
    public void setActivityName(String activityName) { this.activityName = activityName; }
    public void setPurpose(String purpose) { this.purpose = purpose; }
    public void setCategory(String category) { this.category = category; }
    public void setEventDate(String eventDate) { this.eventDate = eventDate; }
    public void setLocation(String location) { this.location = location; }
    public void setTargetAudience(String targetAudience) { this.targetAudience = targetAudience; }
    public void setDescription(String description) { this.description = description; }
    public void setScheduleDetails(String scheduleDetails) { this.scheduleDetails = scheduleDetails; }

    @Override
    public String toString() {
        return "Activity{" +
               "activityName='" + activityName + '\'' +
               ", purpose='" + purpose + '\'' +
               ", category='" + category + '\'' +
               ", eventDate='" + eventDate + '\'' +
               ", location='" + location + '\'' +
               ", targetAudience='" + targetAudience + '\'' +
               ", description='" + description + '\'' +
               ", scheduleDetails='" + scheduleDetails + '\'' +
               '}';
    }
}

ユーティリティ (util.DatabaseUtil.java)

データベース接続の確立とJDBCリソースのクローズを担当するユーティリティクラスです。

package util;

import java.sql.*;

/**
 * データベース接続とリソース管理のためのユーティリティクラス。
 */
public class DatabaseUtil {

    private static final String DB_URL = "jdbc:mysql://localhost:3306/club_activities?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Tokyo&allowPublicKeyRetrieval=true";
    private static final String DB_USER = "root";
    private static final String DB_PASS = "admin_password"; // 実際のパスワードに置き換えてください

    /**
     * データベース接続を取得します。
     * @return データベース接続オブジェクト
     */
    public static Connection getConnection() {
        Connection connection = null;
        try {
            // JDBCドライバーのロード
            Class.forName("com.mysql.cj.jdbc.Driver");
            connection = DriverManager.getConnection(DB_URL, DB_USER, DB_PASS);
        } catch (ClassNotFoundException e) {
            System.err.println("JDBCドライバーが見つかりません。");
            e.printStackTrace();
        } catch (SQLException e) {
            System.err.println("データベース接続エラーが発生しました。");
            e.printStackTrace();
        }
        return connection;
    }

    /**
     * ステートメントと接続を閉じます。
     * @param statement 閉じるステートメント
     * @param connection 閉じる接続
     */
    public static void closeResources(Statement statement, Connection connection) {
        if (statement != null) {
            try {
                statement.close();
            } catch (SQLException e) {
                System.err.println("Statementのクローズ中にエラーが発生しました: " + e.getMessage());
            }
        }
        if (connection != null) {
            try {
                connection.close();
            } catch (SQLException e) {
                System.err.println("Connectionのクローズ中にエラーが発生しました: " + e.getMessage());
            }
        }
    }

    /**
     * ResultSet、ステートメント、および接続を閉じます。
     * @param resultSet 閉じるResultSet
     * @param statement 閉じるステートメント
     * @param connection 閉じる接続
     */
    public static void closeResources(ResultSet resultSet, Statement statement, Connection connection) {
        if (resultSet != null) {
            try {
                resultSet.close();
            } catch (SQLException e) {
                System.err.println("ResultSetのクローズ中にエラーが発生しました: " + e.getMessage());
            }
        }
        closeResources(statement, connection);
    }
}

リポジトリ (repository.ActivityRepository.java)

データベースとの直接的なやり取りを抽象化するDAO(Data Access Object)層です。CRUD操作に対応するメソッドを提供し、プリペアドステートメントを使用してSQLインジェクションのリスクを軽減しています。

package repository;

import model.Activity;
import util.DatabaseUtil;

import java.sql.*;
import java.util.ArrayList;
import java.util.List;

/**
 * サークル活動データのデータベース操作を管理するリポジトリクラス。
 */
public class ActivityRepository {

    /**
     * 新しい活動を追加します。
     *
     * @param activity 追加する活動オブジェクト
     * @return 成功した場合は1、失敗した場合は0
     */
    public int addActivity(Activity activity) {
        String sql = "INSERT INTO activities (activity_name, purpose, category, event_date, location, target_audience, description, schedule_details) VALUES (?, ?, ?, ?, ?, ?, ?, ?)";
        Connection conn = null;
        PreparedStatement pstmt = null;
        int rowsAffected = 0;
        try {
            conn = DatabaseUtil.getConnection();
            pstmt = conn.prepareStatement(sql);
            pstmt.setString(1, activity.getActivityName());
            pstmt.setString(2, activity.getPurpose());
            pstmt.setString(3, activity.getCategory());
            pstmt.setString(4, activity.getEventDate());
            pstmt.setString(5, activity.getLocation());
            pstmt.setString(6, activity.getTargetAudience());
            pstmt.setString(7, activity.getDescription());
            pstmt.setString(8, activity.getScheduleDetails());

            rowsAffected = pstmt.executeUpdate();
        } catch (SQLException e) {
            System.err.println("活動の追加中にデータベースエラーが発生しました: " + e.getMessage());
            e.printStackTrace();
        } finally {
            DatabaseUtil.closeResources(pstmt, conn);
        }
        return rowsAffected;
    }

    /**
     * 指定された活動名を持つ活動を削除します。
     *
     * @param activityName 削除する活動の名前
     * @return 成功した場合は1、失敗した場合は0
     */
    public int deleteActivityByName(String activityName) {
        String sql = "DELETE FROM activities WHERE activity_name = ?";
        Connection conn = null;
        PreparedStatement pstmt = null;
        int rowsAffected = 0;
        try {
            conn = DatabaseUtil.getConnection();
            pstmt = conn.prepareStatement(sql);
            pstmt.setString(1, activityName);
            rowsAffected = pstmt.executeUpdate();
        } catch (SQLException e) {
            System.err.println("活動の削除中にデータベースエラーが発生しました: " + e.getMessage());
            e.printStackTrace();
        } finally {
            DatabaseUtil.closeResources(pstmt, conn);
        }
        return rowsAffected;
    }

    /**
     * 活動情報を更新します。
     *
     * @param activity 更新する活動オブジェクト
     * @return 成功した場合は1、失敗した場合は0
     */
    public int updateActivity(Activity activity) {
        String sql = "UPDATE activities SET purpose=?, category=?, event_date=?, location=?, target_audience=?, description=?, schedule_details=? WHERE activity_name=?";
        Connection conn = null;
        PreparedStatement pstmt = null;
        int rowsAffected = 0;
        try {
            conn = DatabaseUtil.getConnection();
            pstmt = conn.prepareStatement(sql);
            pstmt.setString(1, activity.getPurpose());
            pstmt.setString(2, activity.getCategory());
            pstmt.setString(3, activity.getEventDate());
            pstmt.setString(4, activity.getLocation());
            pstmt.setString(5, activity.getTargetAudience());
            pstmt.setString(6, activity.getDescription());
            pstmt.setString(7, activity.getScheduleDetails());
            pstmt.setString(8, activity.getActivityName()); // WHERE句

            rowsAffected = pstmt.executeUpdate();
        } catch (SQLException e) {
            System.err.println("活動の更新中にデータベースエラーが発生しました: " + e.getMessage());
            e.printStackTrace();
        } finally {
            DatabaseUtil.closeResources(pstmt, conn);
        }
        return rowsAffected;
    }

    /**
     * 複数の条件に基づいて活動を検索します。
     * 指定された条件が空文字列の場合は、その条件は無視されます。
     *
     * @param activityName 活動名 (部分一致)
     * @param eventDate 活動日時 (部分一致)
     * @param category 活動カテゴリ (部分一致)
     * @param targetAudience 対象 (部分一致)
     * @return 検索条件に一致する活動のリスト
     */
    public List<Activity> findActivities(String activityName, String eventDate, String category, String targetAudience) {
        List<Activity> activities = new ArrayList<>();
        StringBuilder sqlBuilder = new StringBuilder("SELECT * FROM activities WHERE 1=1");
        List<String> params = new ArrayList<>();

        if (activityName != null && !activityName.trim().isEmpty()) {
            sqlBuilder.append(" AND activity_name LIKE ?");
            params.add("%" + activityName + "%");
        }
        if (eventDate != null && !eventDate.trim().isEmpty()) {
            sqlBuilder.append(" AND event_date LIKE ?");
            params.add("%" + eventDate + "%");
        }
        if (category != null && !category.trim().isEmpty()) {
            sqlBuilder.append(" AND category LIKE ?");
            params.add("%" + category + "%");
        }
        if (targetAudience != null && !targetAudience.trim().isEmpty()) {
            sqlBuilder.append(" AND target_audience LIKE ?");
            params.add("%" + targetAudience + "%");
        }

        Connection conn = null;
        PreparedStatement pstmt = null;
        ResultSet rs = null;

        try {
            conn = DatabaseUtil.getConnection();
            pstmt = conn.prepareStatement(sqlBuilder.toString());

            for (int i = 0; i < params.size(); i++) {
                pstmt.setString(i + 1, params.get(i));
            }

            rs = pstmt.executeQuery();
            while (rs.next()) {
                activities.add(new Activity(
                        rs.getString("activity_name"),
                        rs.getString("purpose"),
                        rs.getString("category"),
                        rs.getString("event_date"),
                        rs.getString("location"),
                        rs.getString("target_audience"),
                        rs.getString("description"),
                        rs.getString("schedule_details")
                ));
            }
        } catch (SQLException e) {
            System.err.println("活動の検索中にデータベースエラーが発生しました: " + e.getMessage());
            e.printStackTrace();
        } finally {
            DatabaseUtil.closeResources(rs, pstmt, conn);
        }
        return activities;
    }

    /**
     * すべての活動を取得します。
     * @return すべての活動のリスト
     */
    public List<Activity> findAllActivities() {
        List<Activity> activities = new ArrayList<>();
        String sql = "SELECT * FROM activities";
        Connection conn = null;
        PreparedStatement pstmt = null;
        ResultSet rs = null;

        try {
            conn = DatabaseUtil.getConnection();
            pstmt = conn.prepareStatement(sql);
            rs = pstmt.executeQuery();

            while (rs.next()) {
                activities.add(new Activity(
                        rs.getString("activity_name"),
                        rs.getString("purpose"),
                        rs.getString("category"),
                        rs.getString("event_date"),
                        rs.getString("location"),
                        rs.getString("target_audience"),
                        rs.getString("description"),
                        rs.getString("schedule_details")
                ));
            }
        } catch (SQLException e) {
            System.err.println("全活動の取得中にデータベースエラーが発生しました: " + e.getMessage());
            e.printStackTrace();
        } finally {
            DatabaseUtil.closeResources(rs, pstmt, conn);
        }
        return activities;
    }
}

サービス (service.ActivityService.java)

ビジネスロジックを実装するサービスクラスです。入力値の検証を行い、リポジトリ層のメソッドを呼び出してデータベース操作を実行します。

package service;

import model.Activity;
import repository.ActivityRepository;

import java.util.List;

/**
 * サークル活動のビジネスロジックを提供するサービスクラス。
 * 主にデータの検証とリポジトリへの委譲を行います。
 */
public class ActivityService {
    private ActivityRepository activityRepository;

    public ActivityService() {
        this.activityRepository = new ActivityRepository();
    }

    /**
     * 新しい活動を追加します。入力値の検証も行います。
     *
     * @param activity 追加する活動オブジェクト
     * @return 1: 成功, 0: データベース操作失敗, 2-6: 入力検証エラー (詳細: 2:活動名長すぎ, 3:目的長すぎ, 4:内容長すぎ, 5:日程長すぎ, 6:活動名が空)
     */
    public int registerActivity(Activity activity) {
        if (activity.getActivityName() == null || activity.getActivityName().trim().isEmpty()) {
            return 6; // 活動名が空
        }
        if (activity.getActivityName().length() > 20) {
            return 2; // 活動名が長すぎ
        }
        if (activity.getPurpose().length() > 50) {
            return 3; // 目的が長すぎ
        }
        if (activity.getDescription().length() > 500) {
            return 4; // 内容が長すぎ
        }
        if (activity.getScheduleDetails().length() > 500) {
            return 5; // 日程が長すぎ
        }

        if (activityRepository.addActivity(activity) > 0) {
            return 1; // 成功
        } else {
            return 0; // データベース操作失敗
        }
    }

    /**
     * 指定された活動名の活動を削除します。
     *
     * @param activityName 削除する活動の名前
     * @return 1: 成功, 0: 失敗
     */
    public int removeActivity(String activityName) {
        if (activityRepository.deleteActivityByName(activityName) > 0) {
            return 1;
        } else {
            return 0;
        }
    }

    /**
     * 活動情報を更新します。
     *
     * @param activity 更新する活動オブジェクト
     * @return 1: 成功, 0: 失敗
     */
    public int updateActivityDetails(Activity activity) {
        // 更新時にも簡単な検証を行うことができます。
        if (activity.getActivityName() == null || activity.getActivityName().trim().isEmpty()) {
            return 0; // 活動名が空では更新できない
        }
        if (activityRepository.updateActivity(activity) > 0) {
            return 1;
        } else {
            return 0;
        }
    }

    /**
     * 検索条件に基づいて活動を検索します。
     *
     * @param activityName 活動名 (部分一致)
     * @param eventDate 活動日時 (部分一致)
     * @param category 活動カテゴリ (部分一致)
     * @param targetAudience 対象 (部分一致)
     * @return 検索結果の活動リスト
     */
    public List<Activity> searchActivities(String activityName, String eventDate, String category, String targetAudience) {
        return activityRepository.findActivities(activityName, eventDate, category, targetAudience);
    }

    /**
     * すべての活動を取得します。
     * @return すべての活動のリスト
     */
    public List<Activity> getAllActivities() {
        return activityRepository.findAllActivities();
    }
}

コントローラー (controller.ActivityServlet.java)

クライアントからのHTTPリクエストを受け取り、適切なサービス層のメソッドを呼び出してビジネスロジックを実行し、結果をJSPビューにフォワードします。

package controller;

import jakarta.servlet.ServletException;
import jakarta.servlet.annotation.WebServlet;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import model.Activity;
import service.ActivityService;

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

/**
 * サークル活動管理システムのコントローラーサーブレット。
 * 各種活動操作(追加、削除、更新、検索、閲覧)を処理します。
 */
@WebServlet("/ActivityServlet")
public class ActivityServlet extends HttpServlet {
    private static final long serialVersionUID = 1L;
    private ActivityService activityService;

    public ActivityServlet() {
        super();
        this.activityService = new ActivityService();
    }

    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        request.setCharacterEncoding("UTF-8");
        String method = request.getParameter("method");

        if ("add".equals(method)) {
            addActivity(request, response);
        } else if ("delete".equals(method)) {
            deleteActivity(request, response);
        } else if ("update".equals(method)) {
            updateActivity(request, response);
        } else if ("search".equals(method)) {
            searchActivities(request, response);
        } else if ("browse".equals(method)) { // 全活動閲覧のためのメソッド
            browseAllActivities(request, response);
        } else {
            // 不明なメソッドの場合、エラーページまたはメインページにリダイレクト
            response.sendRedirect("index.jsp");
        }
    }

    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        doPost(request, response); // GETリクエストもPOSTで処理
    }

    /**
     * 活動を追加します。
     * @param request HTTPリクエスト
     * @param response HTTPレスポンス
     * @throws ServletException
     * @throws IOException
     */
    private void addActivity(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        String activityName = request.getParameter("activityName");
        String purpose = request.getParameter("purpose");
        String category = request.getParameter("category");
        String eventDate = request.getParameter("eventDate");
        String location = request.getParameter("location");

        // チェックボックスの値を処理
        StringBuilder targetAudienceBuilder = new StringBuilder();
        String memberCheckbox = request.getParameter("targetMember");
        String studentTeacherCheckbox = request.getParameter("targetStudentTeacher");
        if (memberCheckbox != null) {
            targetAudienceBuilder.append(memberCheckbox).append(" ");
        }
        if (studentTeacherCheckbox != null) {
            targetAudienceBuilder.append(studentTeacherCheckbox).append(" ");
        }
        String targetAudience = targetAudienceBuilder.toString().trim();

        String description = request.getParameter("description");
        String scheduleDetails = request.getParameter("scheduleDetails");

        Activity newActivity = new Activity(activityName, purpose, category, eventDate, location, targetAudience, description, scheduleDetails);
        int result = activityService.registerActivity(newActivity);

        String message;
        String forwardPage = "addActivity.jsp";
        switch (result) {
            case 1:
                message = "活動情報が正常に追加されました。";
                break;
            case 0:
                message = "活動の追加に失敗しました。データベースエラーを確認してください。";
                break;
            case 2:
                message = "活動名は20文字を超過できません。";
                break;
            case 3:
                message = "活動目的は50文字を超過できません。";
                break;
            case 4:
                message = "活動内容は500文字を超過できません。";
                break;
            case 5:
                message = "活動日程は500文字を超過できません。";
                break;
            case 6:
                message = "活動名が空です。";
                break;
            default:
                message = "不明なエラーが発生しました。";
        }
        request.setAttribute("message", message);
        request.getRequestDispatcher(forwardPage).forward(request, response);
    }

    /**
     * 活動を削除します。
     * @param request HTTPリクエスト
     * @param response HTTPレスポンス
     * @throws ServletException
     * @throws IOException
     */
    private void deleteActivity(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        String activityName = request.getParameter("activityName");
        int result = activityService.removeActivity(activityName);

        String message;
        if (result > 0) {
            message = "活動情報が正常に削除されました。";
        } else {
            message = "活動の削除に失敗しました。指定された活動名が見つからないか、データベースエラーが発生しました。";
        }
        request.setAttribute("message", message);
        request.getRequestDispatcher("deleteActivity.jsp").forward(request, response);
    }

    /**
     * 活動情報を更新します。
     * @param request HTTPリクエスト
     * @param response HTTPレスポンス
     * @throws ServletException
     * @throws IOException
     */
    private void updateActivity(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        String activityName = request.getParameter("activityName");
        String purpose = request.getParameter("purpose");
        String category = request.getParameter("category");
        String eventDate = request.getParameter("eventDate");
        String location = request.getParameter("location");

        StringBuilder targetAudienceBuilder = new StringBuilder();
        String memberCheckbox = request.getParameter("targetMember");
        String studentTeacherCheckbox = request.getParameter("targetStudentTeacher");
        if (memberCheckbox != null) {
            targetAudienceBuilder.append(memberCheckbox).append(" ");
        }
        if (studentTeacherCheckbox != null) {
            targetAudienceBuilder.append(studentTeacherCheckbox).append(" ");
        }
        String targetAudience = targetAudienceBuilder.toString().trim();

        String description = request.getParameter("description");
        String scheduleDetails = request.getParameter("scheduleDetails");

        Activity updatedActivity = new Activity(activityName, purpose, category, eventDate, location, targetAudience, description, scheduleDetails);
        int result = activityService.updateActivityDetails(updatedActivity);

        String message;
        if (result == 1) {
            message = "活動情報が正常に更新されました。";
        } else {
            message = "活動の更新に失敗しました。活動名を確認するか、データベースエラーを確認してください。";
        }
        request.setAttribute("message", message);
        request.getRequestDispatcher("updateActivity.jsp").forward(request, response);
    }

    /**
     * 活動を検索します。
     * @param request HTTPリクエスト
     * @param response HTTPレスポンス
     * @throws ServletException
     * @throws IOException
     */
    private void searchActivities(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        String activityName = request.getParameter("activityName");
        String eventDate = request.getParameter("eventDate");
        String category = request.getParameter("category");
        String targetAudience = request.getParameter("targetAudience");

        List<Activity> results = activityService.searchActivities(activityName, eventDate, category, targetAudience);

        if (results.isEmpty()) {
            request.setAttribute("message", "検索条件に一致する活動は見つかりませんでした。");
            request.getRequestDispatcher("searchActivity.jsp").forward(request, response);
        } else {
            request.setAttribute("activities", results); // 複数結果を考慮
            request.getRequestDispatcher("searchResults.jsp").forward(request, response);
        }
    }

    /**
     * すべての活動を閲覧します。
     * @param request HTTPリクエスト
     * @param response HTTPレスポンス
     * @throws ServletException
     * @throws IOException
     */
    private void browseAllActivities(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        List<Activity> allActivities = activityService.getAllActivities();
        request.setAttribute("activities", allActivities);
        request.getRequestDispatcher("browseActivities.jsp").forward(request, response);
    }
}

ビュー (JSPファイル)

ユーザーインターフェースを提供するJSP(JavaServer Pages)ファイルです。JSTL(JSP Standard Tag Library)とEL(Expression Language)を使用して、JavaコードとHTMLを分離し、可読性と保守性を向上させています。

index.jsp (メインメニュー)

システムの入り口となるメインメニューページです。

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>サークル活動管理システム</title>
<style>
    body { font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; margin: 0; padding: 20px; background-color: #f4f7f6; color: #333; }
    .container { max-width: 600px; margin: 40px auto; padding: 30px; background-color: #fff; border-radius: 8px; box-shadow: 0 4px 8px rgba(0,0,0,0.1); text-align: center; }
    h1 { color: #2c3e50; margin-bottom: 30px; font-size: 2em; }
    .menu-item { margin-bottom: 15px; }
    .menu-item a {
        display: inline-block;
        padding: 12px 25px;
        background-color: #3498db;
        color: white;
        text-decoration: none;
        border-radius: 5px;
        font-size: 1.1em;
        transition: background-color 0.3s ease;
        min-width: 200px;
    }
    .menu-item a:hover { background-color: #2980b9; }
</style>
</head>
<body>
<div class="container">
    <h1>サークル活動管理システムへようこそ</h1>
    <div class="menu-item">
        <a href="addActivity.jsp">活動情報を登録</a>
    </div>
    <div class="menu-item">
        <a href="updateActivity.jsp">活動情報を更新</a>
    </div>
    <div class="menu-item">
        <a href="deleteActivity.jsp">活動情報を削除</a>
    </div>
    <div class="menu-item">
        <a href="searchActivity.jsp">活動情報を検索</a>
    </div>
    <div class="menu-item">
        <form action="ActivityServlet" method="post" style="display: inline;">
            <input type="hidden" name="method" value="browse">
            <button type="submit" style="
                padding: 12px 25px;
                background-color: #3498db;
                color: white;
                border: none;
                border-radius: 5px;
                font-size: 1.1em;
                cursor: pointer;
                transition: background-color 0.3s ease;
                min-width: 200px;
            ">全活動情報を閲覧</button>
        </form>
    </div>
</div>
</body>
</html>

addActivity.jsp (活動登録フォーム)

新しいサークル活動情報を入力・登録するためのフォームページです。

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>活動情報登録</title>
<style>
    body { font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; margin: 0; padding: 20px; background-color: #f4f7f6; color: #333; }
    .container { max-width: 700px; margin: 40px auto; padding: 30px; background-color: #fff; border-radius: 8px; box-shadow: 0 4px 8px rgba(0,0,0,0.1); }
    h1 { color: #2c3e50; text-align: center; margin-bottom: 30px; }
    .form-group { margin-bottom: 15px; display: flex; align-items: center; }
    .form-group label { flex: 0 0 150px; font-weight: bold; margin-right: 15px; }
    .form-group input[type="text"], .form-group textarea { flex: 1; padding: 10px; border: 1px solid #ddd; border-radius: 4px; box-sizing: border-box; }
    .form-group input[type="radio"], .form-group input[type="checkbox"] { margin-right: 5px; }
    .form-group .radio-group, .form-group .checkbox-group { flex: 1; display: flex; flex-wrap: wrap; gap: 10px; align-items: center; }
    .form-group .radio-group label, .form-group .checkbox-group label { flex: unset; font-weight: normal; margin-right: 15px; }
    .actions { text-align: center; margin-top: 30px; }
    .actions input[type="submit"], .actions input[type="reset"], .actions .back-link {
        padding: 10px 20px;
        border: none;
        border-radius: 5px;
        cursor: pointer;
        font-size: 1em;
        margin: 0 10px;
        transition: background-color 0.3s ease;
    }
    .actions input[type="submit"] { background-color: #28a745; color: white; }
    .actions input[type="submit"]:hover { background-color: #218838; }
    .actions input[type="reset"] { background-color: #6c757d; color: white; }
    .actions input[type="reset"]:hover { background-color: #5a6268; }
    .actions .back-link { background-color: #007bff; color: white; text-decoration: none; display: inline-block; }
    .actions .back-link:hover { background-color: #0056b3; }
</style>
</head>
<body>
<%
Object message = request.getAttribute("message");
if(message!=null && !"".equals(message)){
%>
<script type="text/javascript">
alert("<%=request.getAttribute("message")%>");
</script>
<%
} %>
<div class="container">
    <h1>活動情報登録</h1>
    <div class="actions" style="text-align: left; margin-bottom: 20px;">
        <a href="index.jsp" class="back-link">メインページに戻る</a>
    </div>
    <form action="ActivityServlet?method=add" method="post">
        <div class="form-group">
            <label for="activityName">活動名:</label>
            <input type="text" id="activityName" name="activityName" required />
        </div>
        <div class="form-group">
            <label for="purpose">目的:</label>
            <input type="text" id="purpose" name="purpose" />
        </div>
        <div class="form-group">
            <label>カテゴリ:</label>
            <div class="radio-group">
                <label><input type="radio" name="category" value="サークル競技会" checked>サークル競技会</label>
                <label><input type="radio" name="category" value="野外活動" >野外活動</label>
                <label><input type="radio" name="category" value="学内集会" >学内集会</label>
                <label><input type="radio" name="category" value="新入生勧誘" >新入生勧誘</label>
            </div>
        </div>
        <div class="form-group">
            <label for="eventDate">日時:</label>
            <input type="text" id="eventDate" name="eventDate" placeholder="例: 2023-10-26 14:00" />
        </div>
        <div class="form-group">
            <label for="location">場所:</label>
            <input type="text" id="location" name="location" />
        </div>
        <div class="form-group">
            <label>対象:</label>
            <div class="checkbox-group">
                <label><input type="checkbox" name="targetMember" value="サークルメンバー" />サークルメンバー</label>
                <label><input type="checkbox" name="targetStudentTeacher" value="全学生・教職員" checked/>全学生・教職員</label>
            </div>
        </div>
        <div class="form-group">
            <label for="description">内容:</label>
            <textarea id="description" name="description" rows="3"></textarea>
        </div>
        <div class="form-group">
            <label for="scheduleDetails">日程詳細:</label>
            <textarea id="scheduleDetails" name="scheduleDetails" rows="3"></textarea>
        </div>
        <div class="actions">
            <input type="submit" value="登録">
            <input type="reset" value="リセット">
        </div>
    </form>
</div>
</body>
</html>

deleteActivity.jsp (活動削除フォーム)

指定した活動名を基に活動情報を削除するためのフォームページです。

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>活動情報削除</title>
<style>
    body { font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; margin: 0; padding: 20px; background-color: #f4f7f6; color: #333; }
    .container { max-width: 500px; margin: 40px auto; padding: 30px; background-color: #fff; border-radius: 8px; box-shadow: 0 4px 8px rgba(0,0,0,0.1); text-align: center; }
    h1 { color: #2c3e50; margin-bottom: 30px; }
    .form-group { margin-bottom: 20px; }
    .form-group label { display: block; margin-bottom: 8px; font-weight: bold; }
    .form-group input[type="text"] { width: calc(100% - 22px); padding: 10px; border: 1px solid #ddd; border-radius: 4px; box-sizing: border-box; }
    .actions { margin-top: 30px; }
    .actions input[type="submit"], .actions .back-link {
        padding: 10px 20px;
        border: none;
        border-radius: 5px;
        cursor: pointer;
        font-size: 1em;
        margin: 0 10px;
        transition: background-color 0.3s ease;
    }
    .actions input[type="submit"] { background-color: #dc3545; color: white; }
    .actions input[type="submit"]:hover { background-color: #c82333; }
    .actions .back-link { background-color: #007bff; color: white; text-decoration: none; display: inline-block; }
    .actions .back-link:hover { background-color: #0056b3; }
</style>
</head>
<body>
<div class="container">
    <h1>活動情報削除</h1>
    <div class="actions" style="text-align: left; margin-bottom: 20px;">
        <a href="index.jsp" class="back-link">メインページに戻る</a>
    </div>
    <%
    Object message = request.getAttribute("message");
    if(message!=null && !"".equals(message)){
    %>
    <script type="text/javascript">
    alert("<%=request.getAttribute("message")%>");
    </script>
    <%} %>
    <form action="ActivityServlet?method=delete" method="post">
        <div class="form-group">
            <label for="activityName">削除する活動名を入力してください:</label>
            <input type="text" id="activityName" name="activityName" required />
        </div>
        <div class="actions">
            <input type="submit" value="削除" />
        </div>
    </form>
</div>
</body>
</html>

updateActivity.jsp (活動更新フォーム)

既存の活動情報を更新するためのフォームページです。

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>活動情報更新</title>
<style>
    body { font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; margin: 0; padding: 20px; background-color: #f4f7f6; color: #333; }
    .container { max-width: 700px; margin: 40px auto; padding: 30px; background-color: #fff; border-radius: 8px; box-shadow: 0 4px 8px rgba(0,0,0,0.1); }
    h1 { color: #2c3e50; text-align: center; margin-bottom: 30px; }
    .form-group { margin-bottom: 15px; display: flex; align-items: center; }
    .form-group label { flex: 0 0 150px; font-weight: bold; margin-right: 15px; }
    .form-group input[type="text"], .form-group textarea { flex: 1; padding: 10px; border: 1px solid #ddd; border-radius: 4px; box-sizing: border-box; }
    .form-group input[type="radio"], .form-group input[type="checkbox"] { margin-right: 5px; }
    .form-group .radio-group, .form-group .checkbox-group { flex: 1; display: flex; flex-wrap: wrap; gap: 10px; align-items: center; }
    .form-group .radio-group label, .form-group .checkbox-group label { flex: unset; font-weight: normal; margin-right: 15px; }
    .actions { text-align: center; margin-top: 30px; }
    .actions input[type="submit"], .actions input[type="reset"], .actions .back-link {
        padding: 10px 20px;
        border: none;
        border-radius: 5px;
        cursor: pointer;
        font-size: 1em;
        margin: 0 10px;
        transition: background-color 0.3s ease;
    }
    .actions input[type="submit"] { background-color: #ffc107; color: #333; }
    .actions input[type="submit"]:hover { background-color: #e0a800; }
    .actions input[type="reset"] { background-color: #6c757d; color: white; }
    .actions input[type="reset"]:hover { background-color: #5a6268; }
    .actions .back-link { background-color: #007bff; color: white; text-decoration: none; display: inline-block; }
    .actions .back-link:hover { background-color: #0056b3; }
</style>
</head>
<body>
<%
Object message = request.getAttribute("message");
if(message!=null && !"".equals(message)){
%>
<script type="text/javascript">
alert("<%=request.getAttribute("message")%>");
</script>
<%
} %>
<div class="container">
    <h1>活動情報更新</h1>
    <div class="actions" style="text-align: left; margin-bottom: 20px;">
        <a href="index.jsp" class="back-link">メインページに戻る</a>
    </div>
    <form action="ActivityServlet?method=update" method="post">
        <p>※更新したい活動の「活動名」を正確に入力してください。</p>
        <div class="form-group">
            <label for="activityName">活動名:</label>
            <input type="text" id="activityName" name="activityName" required />
        </div>
        <div class="form-group">
            <label for="purpose">目的:</label>
            <input type="text" id="purpose" name="purpose" />
        </div>
        <div class="form-group">
            <label>カテゴリ:</label>
            <div class="radio-group">
                <label><input type="radio" name="category" value="サークル競技会">サークル競技会</label>
                <label><input type="radio" name="category" value="野外活動">野外活動</label>
                <label><input type="radio" name="category" value="学内集会">学内集会</label>
                <label><input type="radio" name="category" value="新入生勧誘">新入生勧誘</label>
            </div>
        </div>
        <div class="form-group">
            <label for="eventDate">日時:</label>
            <input type="text" id="eventDate" name="eventDate" placeholder="例: 2023-10-26 14:00" />
        </div>
        <div class="form-group">
            <label for="location">場所:</label>
            <input type="text" id="location" name="location" />
        </div>
        <div class="form-group">
            <label>対象:</label>
            <div class="checkbox-group">
                <label><input type="checkbox" name="targetMember" value="サークルメンバー" />サークルメンバー</label>
                <label><input type="checkbox" name="targetStudentTeacher" value="全学生・教職員" />全学生・教職員</label>
            </div>
        </div>>
        <div class="form-group">
            <label for="description">内容:</label>
            <textarea id="description" name="description" rows="3"></textarea>
        </div>
        <div class="form-group">
            <label for="scheduleDetails">日程詳細:</label>
            <textarea id="scheduleDetails" name="scheduleDetails" rows="3"></textarea>
        </div>
        <div class="actions">
            <input type="submit" value="更新" />
            <input type="reset" value="リセット" />
        </div>
    </form>
</div>
</body>
</html>

searchActivity.jsp (活動検索フォーム)

様々な条件で活動情報を検索するためのフォームページです。

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>活動情報検索</title>
<style>
    body { font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; margin: 0; padding: 20px; background-color: #f4f7f6; color: #333; }
    .container { max-width: 600px; margin: 40px auto; padding: 30px; background-color: #fff; border-radius: 8px; box-shadow: 0 4px 8px rgba(0,0,0,0.1); }
    h1 { color: #2c3e50; text-align: center; margin-bottom: 30px; }
    table { width: 100%; border-collapse: collapse; margin-bottom: 20px; }
    table th, table td { padding: 12px; border: 1px solid #ddd; text-align: left; }
    table th { background-color: #e9ecef; font-weight: bold; }
    table input[type="text"] { width: calc(100% - 20px); padding: 8px; border: 1px solid #ccc; border-radius: 3px; box-sizing: border-box; }
    .actions { text-align: center; margin-top: 30px; }
    .actions input[type="submit"], .actions input[type="reset"], .actions .back-link {
        padding: 10px 20px;
        border: none;
        border-radius: 5px;
        cursor: pointer;
        font-size: 1em;
        margin: 0 10px;
        transition: background-color 0.3s ease;
    }
    .actions input[type="submit"] { background-color: #17a2b8; color: white; }
    .actions input[type="submit"]:hover { background-color: #138496; }
    .actions input[type="reset"] { background-color: #6c757d; color: white; }
    .actions input[type="reset"]:hover { background-color: #5a6268; }
    .actions .back-link { background-color: #007bff; color: white; text-decoration: none; display: inline-block; }
    .actions .back-link:hover { background-color: #0056b3; }
</style>
</head>
<body>
<div class="container">
    <h1>活動情報検索</h1>
    <div class="actions" style="text-align: left; margin-bottom: 20px;">
        <a href="index.jsp" class="back-link">メインページに戻る</a>
    </div>
    <%
    Object message = request.getAttribute("message");
    if(message!=null && !"".equals(message)){
    %>
    <script type="text/javascript">
    alert("<%=request.getAttribute("message")%>");
    </script>
    <%} %>
    <form action="ActivityServlet?method=search" method="post">
        <table>
            <tr>
                <td>活動名: </td>
                <td>
                    <input type="text" name="activityName" />
                </td>
            </tr>
            <tr>
                <td>日時: </td>
                <td>
                    <input type="text" name="eventDate" />
                </td>
            </tr>
            <tr>
                <td>カテゴリ: </td>
                <td>
                    <input type="text" name="category" />
                </td>
            </tr>
            <tr>
                <td>対象: </td>
                <td>
                    <input type="text" name="targetAudience" />
                </td>
            </tr>
        </table>
        <div class="actions">
            <input type="submit" value="検索" />
            <input type="reset" value="リセット" />
        </div>
    </form>
</div>
</body>
</html>

searchResults.jsp (検索結果表示ページ)

検索クエリの結果を表示するページです。複数の活動情報をテーブル形式で一覧表示します。

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>検索結果</title>
<style>
    body { font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; margin: 0; padding: 20px; background-color: #f4f7f6; color: #333; }
    .container { max-width: 800px; margin: 40px auto; padding: 30px; background-color: #fff; border-radius: 8px; box-shadow: 0 4px 8px rgba(0,0,0,0.1); }
    h1 { color: #2c3e50; text-align: center; margin-bottom: 30px; }
    .message { color: #dc3545; text-align: center; margin-bottom: 20px; font-weight: bold; }
    table { width: 100%; border-collapse: collapse; margin-bottom: 20px; }
    table th, table td { padding: 12px; border: 1px solid #ddd; text-align: left; }
    table th { background-color: #e9ecef; font-weight: bold; }
    .actions { text-align: center; margin-top: 30px; }
    .actions .button-link {
        display: inline-block;
        padding: 10px 20px;
        border: none;
        border-radius: 5px;
        cursor: pointer;
        font-size: 1em;
        margin: 0 10px;
        text-decoration: none;
        color: white;
        transition: background-color 0.3s ease;
    }
    .actions .back-button { background-color: #007bff; }
    .actions .back-button:hover { background-color: #0056b3; }
</style>
</head>
<body>
<div class="container">
    <h1>活動情報検索結果</h1>

    <c:if test="${not empty requestScope.message}">
        <p class="message">${requestScope.message}</p>
    </c:if>

    <c:if test="${empty requestScope.activities}">
        <p>検索条件に一致する活動は見つかりませんでした。</p>
    </c:if>

    <c:if test="${not empty requestScope.activities}">
        <table>
            <thead>
                <tr>
                    <th>活動名</th>
                    <th>目的</th>
                    <th>カテゴリ</th>
                    <th>日時</th>
                    <th>場所</th>
                    <th>対象</th>
                    <th>内容</th>
                    <th>日程詳細</th>
                </tr>
            </thead>
            <tbody>
                <c:forEach var="activity" items="${requestScope.activities}">
                    <tr>
                        <td>${activity.activityName}</td>
                        <td>${activity.purpose}</td>
                        <td>${activity.category}</td>
                        <td>${activity.eventDate}</td>
                        <td>${activity.location}</td>
                        <td>${activity.targetAudience}</td>
                        <td>${activity.description}</td>
                        <td>${activity.scheduleDetails}</td>
                    </tr>
                </c:forEach>
            </tbody>
        </table>
    </c:if>

    <div class="actions">
        <a href="searchActivity.jsp" class="button-link back-button">新しい検索を実行</a>
        <a href="index.jsp" class="button-link back-button">メインページに戻る</a>
    </div>
</div>
</body>
</html>

browseActivities.jsp (全活動一覧表示ページ)

データベースに登録されているすべての活動情報を一覧表示するページです。

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>活動情報一覧</title>
<style>
    body { font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; margin: 0; padding: 20px; background-color: #f4f7f6; color: #333; }
    .container { max-width: 1000px; margin: 40px auto; padding: 30px; background-color: #fff; border-radius: 8px; box-shadow: 0 4px 8px rgba(0,0,0,0.1); }
    h1 { color: #2c3e50; text-align: center; margin-bottom: 30px; }
    table { width: 100%; border-collapse: collapse; margin-bottom: 20px; }
    table th, table td { padding: 12px; border: 1px solid #ddd; text-align: left; word-break: break-all; }
    table th { background-color: #e9ecef; font-weight: bold; }
    .actions { text-align: center; margin-top: 30px; }
    .actions .back-link {
        display: inline-block;
        padding: 10px 20px;
        background-color: #007bff;
        color: white;
        text-decoration: none;
        border-radius: 5px;
        font-size: 1em;
        transition: background-color 0.3s ease;
    }
    .actions .back-link:hover { background-color: #0056b3; }
</style>
</head>
<body>
<div class="container">
    <h1>活動情報一覧</h1>
    <div class="actions" style="text-align: left; margin-bottom: 20px;">
        <a href="index.jsp" class="back-link">メインページに戻る</a>
    </div>
    
    <c:if test="${empty requestScope.activities}">
        <p>登録されている活動情報はありません。</p>
    </c:if>

    <c:if test="${not empty requestScope.activities}">
        <table>
            <thead>
                <tr>
                    <th>活動名</th>
                    <th>目的</th>
                    <th>カテゴリ</th>
                    <th>日時</th>
                    <th>場所</th>
                    <th>対象</th>
                    <th>内容</th>
                    <th>日程詳細</th>
                </tr>
            </thead>
            <tbody>
                <c:forEach var="activity" items="${requestScope.activities}">
                    <tr>
                        <td>${activity.activityName}</td>
                        <td>${activity.purpose}</td>
                        <td>${activity.category}</td>
                        <td>${activity.eventDate}</td>
                        <td>${activity.location}</td>
                        <td>${activity.targetAudience}</td>
                        <td>${activity.description}</td>
                        <td>${activity.scheduleDetails}</td>
                    </tr>
                </c:forEach>
            </tbody>
        </table>
    </c:if>
</div>
</body>
</html>

タグ: Java JDBC Servlet JSP MySQL

11月1日 12:43 投稿