VisualStudioTutor.com Java Tutorial All Lessons

Lesson 25 of 25

Student Course Management System

Combine Java, Swing, JDBC, validation, and SQLite CRUD operations into a complete capstone desktop application.

Capstone objectives

  • Combine the OOP, Swing, JDBC, validation, and collection concepts from earlier lessons.
  • Create a layered desktop project instead of placing all code in one class.
  • Load database records into a JTable.
  • Insert, update, search, and delete student records from the GUI.
  • Keep database logic out of Swing event handlers.
  • Provide user-friendly validation and confirmation messages.

1. Capstone architecture

The application uses four responsibilities. The Student class represents data and business rules. DatabaseHelper opens database connections. StudentRepository performs SQL operations. StudentManagementFrame handles user interaction.

student-course-management/
├── DatabaseHelper.java
├── DatabaseSetup.java
├── Student.java
├── StudentRepository.java
└── StudentManagementFrame.java

2. Database schema

CREATE TABLE IF NOT EXISTS students (
    student_id TEXT PRIMARY KEY,
    name       TEXT NOT NULL,
    course     TEXT NOT NULL,
    mark       INTEGER NOT NULL
        CHECK(mark BETWEEN 0 AND 100)
);

3. Reuse the model and repository

Use the Student, DatabaseHelper, DatabaseSetup, and StudentRepository classes developed in Lesson 24. This is intentional: production applications reuse working components rather than rewriting database code inside every form.

4. Build the Swing form

The interface needs text fields for student ID, name, course, and mark; buttons for Save, Update, Delete, Search, and Clear; plus a table for existing records.

import javax.swing.*;
import javax.swing.table.DefaultTableModel;
import java.awt.*;

public class StudentManagementFrame extends JFrame {

    private final StudentRepository repository =
        new StudentRepository();

    private final JTextField txtStudentId =
        new JTextField();

    private final JTextField txtName =
        new JTextField();

    private final JTextField txtCourse =
        new JTextField();

    private final JTextField txtMark =
        new JTextField();

    private final DefaultTableModel tableModel =
        new DefaultTableModel(
            new String[]{
                "Student ID",
                "Name",
                "Course",
                "Mark",
                "Result"
            },
            0
        ) {
            @Override
            public boolean isCellEditable(
                    int row, int column) {
                return false;
            }
        };

    private final JTable table =
        new JTable(tableModel);

    public StudentManagementFrame() {
        setTitle("Student Course Management System");
        setDefaultCloseOperation(EXIT_ON_CLOSE);

        buildInterface();

        DatabaseSetup.initialize();
        loadStudents();

        setSize(850, 500);
        setLocationRelativeTo(null);
    }

5. Create the form layout and buttons

private void buildInterface() {
    JPanel form =
        new JPanel(new GridLayout(4, 2, 8, 8));

    form.add(new JLabel("Student ID:"));
    form.add(txtStudentId);

    form.add(new JLabel("Name:"));
    form.add(txtName);

    form.add(new JLabel("Course:"));
    form.add(txtCourse);

    form.add(new JLabel("Mark:"));
    form.add(txtMark);

    JButton btnSave = new JButton("Save");
    JButton btnUpdate = new JButton("Update");
    JButton btnDelete = new JButton("Delete");
    JButton btnSearch = new JButton("Search");
    JButton btnClear = new JButton("Clear");

    JPanel buttons = new JPanel(new FlowLayout());
    buttons.add(btnSave);
    buttons.add(btnUpdate);
    buttons.add(btnDelete);
    buttons.add(btnSearch);
    buttons.add(btnClear);

    JPanel top = new JPanel(new BorderLayout(8, 8));
    top.add(form, BorderLayout.CENTER);
    top.add(buttons, BorderLayout.SOUTH);

    add(top, BorderLayout.NORTH);
    add(
        new JScrollPane(table),
        BorderLayout.CENTER
    );

    btnSave.addActionListener(
        event -> saveStudent()
    );

    btnUpdate.addActionListener(
        event -> updateStudent()
    );

    btnDelete.addActionListener(
        event -> deleteStudent()
    );

    btnSearch.addActionListener(
        event -> searchStudent()
    );

    btnClear.addActionListener(
        event -> clearFields()
    );

    table.getSelectionModel()
        .addListSelectionListener(event -> {
            if (!event.getValueIsAdjusting()) {
                fillFieldsFromSelectedRow();
            }
        });
}

6. Convert form input into a Student object

Centralizing validation avoids repeating the same checks in every button handler.

private Student readStudentFromForm() {
    String id =
        txtStudentId.getText().trim();

    String name =
        txtName.getText().trim();

    String course =
        txtCourse.getText().trim();

    if (id.isBlank()
            || name.isBlank()
            || course.isBlank()) {

        throw new IllegalArgumentException(
            "Student ID, name, and course are required."
        );
    }

    int mark;

    try {
        mark = Integer.parseInt(
            txtMark.getText().trim()
        );
    } catch (NumberFormatException e) {
        throw new IllegalArgumentException(
            "Mark must be a whole number."
        );
    }

    return new Student(
        id, name, course, mark
    );
}

7. Save a student

private void saveStudent() {
    try {
        Student student =
            readStudentFromForm();

        if (repository.add(student)) {
            JOptionPane.showMessageDialog(
                this,
                "Student saved successfully."
            );

            loadStudents();
            clearFields();
        }

    } catch (RuntimeException e) {
        showError(e);
    }
}

8. Update a student

private void updateStudent() {
    try {
        Student student =
            readStudentFromForm();

        if (repository.update(student)) {
            JOptionPane.showMessageDialog(
                this,
                "Student updated successfully."
            );

            loadStudents();
            clearFields();
        } else {
            JOptionPane.showMessageDialog(
                this,
                "Student ID was not found."
            );
        }

    } catch (RuntimeException e) {
        showError(e);
    }
}

9. Search by student ID

private void searchStudent() {
    String id =
        txtStudentId.getText().trim();

    if (id.isBlank()) {
        JOptionPane.showMessageDialog(
            this,
            "Enter a Student ID to search."
        );
        return;
    }

    repository.findById(id)
        .ifPresentOrElse(
            student -> {
                txtName.setText(
                    student.getName()
                );
                txtCourse.setText(
                    student.getCourse()
                );
                txtMark.setText(
                    String.valueOf(
                        student.getMark()
                    )
                );
            },
            () ->
                JOptionPane.showMessageDialog(
                    this,
                    "Student not found."
                )
        );
}

10. Delete with confirmation

private void deleteStudent() {
    String id =
        txtStudentId.getText().trim();

    if (id.isBlank()) {
        JOptionPane.showMessageDialog(
            this,
            "Select or enter a Student ID."
        );
        return;
    }

    int choice = JOptionPane.showConfirmDialog(
        this,
        "Delete student " + id + "?",
        "Confirm Delete",
        JOptionPane.YES_NO_OPTION,
        JOptionPane.WARNING_MESSAGE
    );

    if (choice != JOptionPane.YES_OPTION) {
        return;
    }

    try {
        if (repository.delete(id)) {
            JOptionPane.showMessageDialog(
                this,
                "Student deleted."
            );

            loadStudents();
            clearFields();
        } else {
            JOptionPane.showMessageDialog(
                this,
                "Student ID was not found."
            );
        }

    } catch (RuntimeException e) {
        showError(e);
    }
}

11. Reload the JTable

private void loadStudents() {
    tableModel.setRowCount(0);

    for (Student student :
            repository.findAll()) {

        tableModel.addRow(
            new Object[]{
                student.getStudentId(),
                student.getName(),
                student.getCourse(),
                student.getMark(),
                student.getResult()
            }
        );
    }
}

12. Select a row and edit it

private void fillFieldsFromSelectedRow() {
    int row = table.getSelectedRow();

    if (row < 0) {
        return;
    }

    txtStudentId.setText(
        tableModel.getValueAt(row, 0).toString()
    );

    txtName.setText(
        tableModel.getValueAt(row, 1).toString()
    );

    txtCourse.setText(
        tableModel.getValueAt(row, 2).toString()
    );

    txtMark.setText(
        tableModel.getValueAt(row, 3).toString()
    );
}

private void clearFields() {
    txtStudentId.setText("");
    txtName.setText("");
    txtCourse.setText("");
    txtMark.setText("");
    table.clearSelection();
    txtStudentId.requestFocus();
}

private void showError(RuntimeException e) {
    String message =
        e.getCause() != null
            ? e.getCause().getMessage()
            : e.getMessage();

    JOptionPane.showMessageDialog(
        this,
        message,
        "Error",
        JOptionPane.ERROR_MESSAGE
    );
}

13. Start the application

public static void main(String[] args) {
        SwingUtilities.invokeLater(() -> {
            new StudentManagementFrame()
                .setVisible(true);
        });
    }
}

14. What this capstone demonstrates

ConceptWhere it appears
OOPStudent model and encapsulated validation
Interfaces between layersGUI calls repository methods instead of embedding SQL
SwingForms, buttons, event handlers, JTable, dialogs
JDBCPrepared statements and result mapping
SQLitePersistent local database
ValidationRequired fields, numeric mark, mark range
CRUDSave, load/search, update, delete

Production improvements to try next

  • Replace the free-text course field with a JComboBox populated from a separate courses table.
  • Add a second table for courses and a foreign key from students to courses.
  • Add search by name or course.
  • Move database work off the Swing Event Dispatch Thread using SwingWorker for larger datasets.
  • Add unit tests for Student validation and repository integration tests against a temporary database.
  • Add export to CSV and a printable student report.

Common mistakes

ProblemWhy it happensFix
All SQL is placed inside button handlersThe GUI becomes difficult to maintain and test.Keep SQL inside StudentRepository.
The JTable is not refreshed after a changeThe database changed but the displayed model did not.Call loadStudents() after successful save/update/delete.
Delete happens immediatelyUsers can remove data accidentally.Ask for confirmation before deleting.
Database operations freeze a large GUISlow I/O is executing on the EDT.Use a background worker for long-running database work.

Capstone challenge

Extend the system into a true Student Course Management System by adding a courses table with course_id, course_name, and lecturer. Create a Course model and repository, load courses into a combo box, and save the selected course_id with each student. This introduces a relational design rather than storing course names repeatedly as free text.

Course summary

You have now progressed from Java fundamentals through object-oriented programming, collections, lambdas, date/time, Swing, JDBC, SQLite, CRUD, and a complete desktop database application. The most important next step is to keep building: add features, refactor duplicated code, test each layer, and place the finished source code under version control.