このドキュメントでは、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>