Just JDBC

By implementing the Student Management System using JDBC, you will see the manual effort required in handling connections, queries, and transactions, which makes you realize Hibernate’s advantages.


πŸš€ Step-by-Step Guide: Implementing Student Management with JDBC (H2 Database)

🎯 What Will Students Learn?

βœ… How to connect to an H2 database using JDBC
βœ… How to perform CRUD operations manually
βœ… Understand the limitations of JDBC before transitioning to Hibernate


πŸ”Ή Step 1: Set Up JDBC Configuration

πŸ“Œ Add H2 Database Dependency (For Maven Projects)

<dependency>
    <groupId>com.h2database</groupId>
    <artifactId>h2</artifactId>
    <scope>runtime</scope>
</dependency>

πŸ“Œ Configure Database Connection in db.properties

jdbc.url=jdbc:h2:mem:testdb
jdbc.username=sa
jdbc.password=
jdbc.driverClassName=org.h2.Driver

πŸ”Ή Step 2: Create JDBCUtil for Database Connection

package com.example.util;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.util.Properties;
import java.io.InputStream;

public class JDBCUtil {
    private static String url;
    private static String username;
    private static String password;

    static {
        try (InputStream input = JDBCUtil.class.getClassLoader().getResourceAsStream("db.properties")) {
            Properties prop = new Properties();
            prop.load(input);
            url = prop.getProperty("jdbc.url");
            username = prop.getProperty("jdbc.username");
            password = prop.getProperty("jdbc.password");
            Class.forName(prop.getProperty("jdbc.driverClassName"));
        } catch (Exception e) {
            e.printStackTrace();
            throw new RuntimeException("Failed to load database properties");
        }
    }

    public static Connection getConnection() throws SQLException {
        return DriverManager.getConnection(url, username, password);
    }
}

βœ… Why? Manages database connection setup, making it reusable across the application.


πŸ”Ή Step 3: Create Student Model Class

package com.example.model;

public class Student {
    private int id;
    private String firstName;
    private String lastName;
    private String email;

    public Student() {}

    public Student(String firstName, String lastName, String email) {
        this.firstName = firstName;
        this.lastName = lastName;
        this.email = email;
    }

    public int getId() { return id; }
    public void setId(int id) { this.id = id; }
    public String getFirstName() { return firstName; }
    public void setFirstName(String firstName) { this.firstName = firstName; }
    public String getLastName() { return lastName; }
    public void setLastName(String lastName) { this.lastName = lastName; }
    public String getEmail() { return email; }
    public void setEmail(String email) { this.email = email; }
}

βœ… Why? Represents the Student entity for our system.


πŸ”Ή Step 4: Create StudentDAO for CRUD Operations

package com.example.dao;

import com.example.model.Student;
import com.example.util.JDBCUtil;

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

public class StudentDAO {

    // Save Student
    public void saveStudent(Student student) {
        String sql = "INSERT INTO Student (first_name, last_name, email) VALUES (?, ?, ?)";
        try (Connection conn = JDBCUtil.getConnection();
             PreparedStatement stmt = conn.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS)) {
            stmt.setString(1, student.getFirstName());
            stmt.setString(2, student.getLastName());
            stmt.setString(3, student.getEmail());
            stmt.executeUpdate();
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }

    // Fetch All Students
    public List<Student> getAllStudents() {
        List<Student> students = new ArrayList<>();
        String sql = "SELECT * FROM Student";
        try (Connection conn = JDBCUtil.getConnection();
             PreparedStatement stmt = conn.prepareStatement(sql);
             ResultSet rs = stmt.executeQuery()) {
            while (rs.next()) {
                Student student = new Student();
                student.setId(rs.getInt("id"));
                student.setFirstName(rs.getString("first_name"));
                student.setLastName(rs.getString("last_name"));
                student.setEmail(rs.getString("email"));
                students.add(student);
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }
        return students;
    }

    // Update Student
    public void updateStudent(int id, String newEmail) {
        String sql = "UPDATE Student SET email = ? WHERE id = ?";
        try (Connection conn = JDBCUtil.getConnection();
             PreparedStatement stmt = conn.prepareStatement(sql)) {
            stmt.setString(1, newEmail);
            stmt.setInt(2, id);
            stmt.executeUpdate();
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }

    // Delete Student
    public void deleteStudent(int id) {
        String sql = "DELETE FROM Student WHERE id = ?";
        try (Connection conn = JDBCUtil.getConnection();
             PreparedStatement stmt = conn.prepareStatement(sql)) {
            stmt.setInt(1, id);
            stmt.executeUpdate();
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }
}

βœ… Why? Manages CRUD operations manually using raw SQL.


πŸ”Ή Step 5: Create Main.java to Test JDBC Implementation

package com.example;

import com.example.dao.StudentDAO;
import com.example.model.Student;

import java.util.List;

public class Main {
    public static void main(String[] args) {
        StudentDAO studentDAO = new StudentDAO();

        // Save Student
        System.out.println("Saving Student...");
        studentDAO.saveStudent(new Student("John", "Doe", "john.doe@example.com"));

        // Fetch All Students
        System.out.println("Fetching Students...");
        List<Student> students = studentDAO.getAllStudents();
        students.forEach(s -> System.out.println(s.getId() + " " + s.getFirstName() + " " + s.getEmail()));

        // Update Student
        System.out.println("Updating Student Email...");
        studentDAO.updateStudent(1, "john.updated@example.com");

        // Delete Student
        System.out.println("Deleting Student...");
        studentDAO.deleteStudent(1);
    }
}

βœ… Expected Output

Saving Student...
Fetching Students...
1 John john.doe@example.com
Updating Student Email...
Deleting Student...

πŸ“Œ Why Is This Approach Limiting?

JDBC IssuesHibernate/JPA Solution
Boilerplate Code: Requires manually handling connections, statements, and result sets.Hibernate eliminates boilerplate code using Session and EntityManager.
No Caching: Every query runs directly on the DB, increasing latency.Hibernate has first-level and second-level caching for better performance.
No Automatic Mapping: Need to manually set object properties from ResultSet.Hibernate automatically maps objects using annotations.
Difficult Transactions: Requires manual commit/rollback handling.Hibernate automates transactions with @Transactional.
Vendor-Specific Code: SQL queries may not be portable across databases.JPA supports database-independent queries using JPQL.

πŸš€ Next Step: Transition to JPA

Now that we’ve seen the limitations of JDBC, let’s switch to JPA, where the framework handles transactions, mappings, and queries automatically.

πŸ‘‰ Are you ready to move forward with JPA? πŸ˜ƒ