1. What is JDBC?
JDBC stands for Java Database Connectivity. It is a Java API that lets your Java program talk to a database (MySQL, Oracle, PostgreSQL, etc.) — send SQL queries, get results back, and manipulate data.
1. You pick up the phone (load the driver)
2. You dial the number (create a connection)
3. You talk (send SQL queries)
4. You listen (read results from ResultSet)
5. You hang up (close the connection)
The phone system itself is JDBC — it connects you (Java) to the other person (database).
Why Do We Need JDBC?
Your Java application stores data in variables and objects — but when the app closes, all that data is lost. Databases store data permanently. JDBC is the bridge that lets Java save and retrieve data from a database.
- Java app needs to store user registrations → needs a database
- Java app needs to show a list of products → reads from a database
- Without JDBC, Java has no way to talk to MySQL, Oracle, etc.
2. JDBC Architecture
JDBC sits between your Java code and the database. Here's the flow:
┌─────────────────┐ │ Java Application │ └────────┬────────┘ │ calls methods like executeQuery() ┌────────▼────────┐ │ JDBC API │ ← java.sql package (Connection, Statement, ResultSet) └────────┬────────┘ │ translates to database-specific protocol ┌────────▼────────┐ │ JDBC Driver │ ← e.g., MySQL Connector/J, Oracle JDBC driver └────────┬────────┘ │ sends actual SQL over the network ┌────────▼────────┐ │ Database │ ← MySQL, Oracle, PostgreSQL └─────────────────┘
3. Types of JDBC Drivers
| Type | Name | How It Works | Used? |
|---|---|---|---|
| Type 1 | JDBC-ODBC Bridge | Uses ODBC driver as middleman. Very slow. | Removed in Java 8. Never use. |
| Type 2 | Native-API | Uses database-specific native libraries (C/C++). | Platform-dependent. Rarely used. |
| Type 3 | Network Protocol | Talks to a middleware server that then talks to DB. | Complex setup. Rare. |
| Type 4 | Thin / Pure Java | Directly talks to database using Java sockets. No middleman. | Most used. This is what you use. |
4. JDBC Setup
Step 1: Get the MySQL Connector/J JAR
Download mysql-connector-j-8.x.x.jar from the MySQL website and add it to your project's classpath (or lib folder).
Step 2: Load the Driver
Class.forName("com.mysql.cj.jdbc.Driver");
Class.forName() loads the driver class into JVM memory. When the driver class loads, its static block automatically registers itself with DriverManager. Without this, Java doesn't know which driver to use.Note: In JDBC 4.0+ (Java 6+), the driver auto-loads if the JAR is on classpath. But TCS exams still ask about
Class.forName(), so always know it.
Step 3: Connection URL Format
// Format: jdbc:mysql://hostname:port/database_name // Example — connecting to "ilpdb" on your own machine: jdbc:mysql://localhost:3306/ilpdb
| Part | Meaning | Example |
|---|---|---|
jdbc:mysql:// | Protocol — tells JDBC to use MySQL | Always this for MySQL |
localhost | Hostname — where the DB server runs | localhost or 192.168.1.5 |
3306 | Port — MySQL default port | 3306 (default) |
ilpdb | Database name | Your database name |
5. The 7 Steps of JDBC
// Step 1: Import the java.sql package import java.sql.*; public class JDBCDemo { public static void main(String[] args) { try { // Step 2: Load/Register the driver Class.forName("com.mysql.cj.jdbc.Driver"); // Step 3: Establish the connection Connection con = DriverManager.getConnection( "jdbc:mysql://localhost:3306/ilpdb", // URL "root", // username "password" // password ); // Step 4: Create a Statement object Statement stmt = con.createStatement(); // Step 5: Execute the SQL query ResultSet rs = stmt.executeQuery("SELECT * FROM employees"); // Step 6: Process the results while (rs.next()) { System.out.println(rs.getInt("id") + " - " + rs.getString("name")); } // Step 7: Close the connection rs.close(); stmt.close(); con.close(); } catch (Exception e) { e.printStackTrace(); } } }
1. Import →
import java.sql.*;2. Load →
Class.forName("...")3. Establish →
DriverManager.getConnection()4. Create →
con.createStatement()5. Execute →
stmt.executeQuery() or stmt.executeUpdate()6. Process →
while(rs.next()) { ... }7. Close →
con.close()
6. Connection
Getting a Connection
Connection con = DriverManager.getConnection(url, username, password);
The Connection object represents a live session with the database. As long as it's open, you can send queries.
Important Connection Methods
| Method | What It Does |
|---|---|
createStatement() | Creates a Statement object |
prepareStatement(sql) | Creates a PreparedStatement with SQL template |
prepareCall(sql) | Creates a CallableStatement for stored procedures |
setAutoCommit(false) | Turns off auto-commit for manual transactions |
commit() | Commits the current transaction |
rollback() | Rolls back the current transaction |
close() | Closes the connection and releases resources |
isClosed() | Returns true if connection is closed |
Try-With-Resources (Best Practice)
Always use try-with-resources so connections auto-close even if an exception occurs:
try (Connection con = DriverManager.getConnection(url, user, pass); Statement stmt = con.createStatement(); ResultSet rs = stmt.executeQuery("SELECT * FROM employees")) { while (rs.next()) { System.out.println(rs.getString("name")); } // No need to call close() — happens automatically! } catch (SQLException e) { e.printStackTrace(); }
7. Statement Types
| Feature | Statement | PreparedStatement | CallableStatement |
|---|---|---|---|
| Used for | Simple, one-time SQL | Repeated SQL with parameters | Stored procedures |
| SQL Injection | Vulnerable | Safe (parameterized) | Safe |
| Performance | Slower (compiled every time) | Faster (pre-compiled) | Depends on DB |
| Parameters | None — SQL is a raw string | Uses ? placeholders |
Uses ? for IN/OUT params |
| Creation | con.createStatement() |
con.prepareStatement(sql) |
con.prepareCall(sql) |
| Best for | DDL (CREATE, DROP, ALTER) | DML (INSERT, UPDATE, SELECT) | Calling stored procedures |
Statement Example
Statement stmt = con.createStatement(); ResultSet rs = stmt.executeQuery("SELECT * FROM employees WHERE dept='IT'");
Why Statement is Dangerous
// User enters this as their name: ' OR '1'='1 String userInput = "' OR '1'='1"; String sql = "SELECT * FROM users WHERE name='" + userInput + "'"; // Becomes: SELECT * FROM users WHERE name='' OR '1'='1' // This returns ALL rows! This is SQL Injection. stmt.executeQuery(sql);
8. PreparedStatement in Detail
PreparedStatement is the right way to execute SQL with user input. It uses ? placeholders instead of string concatenation.
// Step 1: Write SQL with ? placeholders (no quotes around ?) String sql = "SELECT * FROM employees WHERE dept = ? AND salary > ?"; // Step 2: Create the PreparedStatement PreparedStatement pstmt = con.prepareStatement(sql); // Step 3: Set the parameter values (index starts at 1, not 0!) pstmt.setString(1, "IT"); // First ? → "IT" pstmt.setDouble(2, 50000.0); // Second ? → 50000.0 // Step 4: Execute ResultSet rs = pstmt.executeQuery();
Common Setter Methods
| Method | Java Type | SQL Type |
|---|---|---|
setInt(index, value) | int | INTEGER |
setString(index, value) | String | VARCHAR |
setDouble(index, value) | double | DOUBLE |
setFloat(index, value) | float | FLOAT |
setLong(index, value) | long | BIGINT |
setDate(index, value) | java.sql.Date | DATE |
setBoolean(index, value) | boolean | BOOLEAN |
PreparedStatement, the SQL structure is compiled first, then the values are plugged in separately. The database treats the values as data only, never as SQL commands.Even if a user enters
' OR '1'='1, the database sees it as a literal string — not as SQL code. The SQL structure can't be changed after compilation.
pstmt.setString(1, "value") is correct. pstmt.setString(0, "value") throws an exception.
9. executeQuery() vs executeUpdate() vs execute()
| Method | Used For | Returns | Example SQL |
|---|---|---|---|
executeQuery() |
SELECT statements | ResultSet |
SELECT * FROM users |
executeUpdate() |
INSERT, UPDATE, DELETE, DDL | int (rows affected) |
INSERT INTO users VALUES(...) |
execute() |
Any SQL statement | boolean (true if ResultSet) |
Dynamic/unknown SQL type |
// executeQuery() — for SELECT ResultSet rs = stmt.executeQuery("SELECT * FROM employees"); // executeUpdate() — for INSERT/UPDATE/DELETE int rowsAffected = stmt.executeUpdate("DELETE FROM employees WHERE id=5"); System.out.println(rowsAffected + " row(s) deleted"); // execute() — for any SQL (rarely used in exams) boolean hasResultSet = stmt.execute("SELECT * FROM employees"); if (hasResultSet) { ResultSet rs2 = stmt.getResultSet(); }
A: An
int — the number of rows affected. For DDL statements (CREATE TABLE, etc.), it returns 0.Q: What does executeQuery() return?
A: A
ResultSet object containing the query results.
10. ResultSet
A ResultSet is like a table of results. It has a cursor that starts before the first row. You call next() to move to each row.
ResultSet rs = stmt.executeQuery("SELECT id, name, salary FROM employees"); // Cursor starts BEFORE first row // rs.next() moves to first row, returns true // rs.next() moves to second row, returns true // ... when no more rows, returns false while (rs.next()) { int id = rs.getInt("id"); // by column name String name = rs.getString(2); // by column index (starts at 1!) double salary = rs.getDouble("salary"); System.out.println(id + " | " + name + " | " + salary); }
ResultSet Navigation Methods
| Method | What It Does | Default Available? |
|---|---|---|
next() | Move cursor forward one row | Yes (always) |
previous() | Move cursor back one row | Only if scrollable |
first() | Move to first row | Only if scrollable |
last() | Move to last row | Only if scrollable |
absolute(n) | Move to row number n | Only if scrollable |
beforeFirst() | Move before first row | Only if scrollable |
ResultSet Types
| Type Constant | Scrollable? | Reflects DB Changes? |
|---|---|---|
TYPE_FORWARD_ONLY | No — only next() | No |
TYPE_SCROLL_INSENSITIVE | Yes | No — snapshot at query time |
TYPE_SCROLL_SENSITIVE | Yes | Yes — sees changes made by others |
ResultSet Concurrency
| Concurrency Constant | Meaning |
|---|---|
CONCUR_READ_ONLY | Can only read data (default) |
CONCUR_UPDATABLE | Can update data directly through the ResultSet |
Creating a Scrollable ResultSet
Statement stmt = con.createStatement( ResultSet.TYPE_SCROLL_INSENSITIVE, // scrollable ResultSet.CONCUR_READ_ONLY // read-only ); ResultSet rs = stmt.executeQuery("SELECT * FROM employees"); rs.last(); // jump to last row System.out.println("Total rows: " + rs.getRow()); // get current row number rs.beforeFirst(); // go back to start
11. CRUD Operations — Complete Examples
Every database application revolves around these four operations. All examples use PreparedStatement.
CREATE — INSERT a New Employee
String sql = "INSERT INTO employees (name, dept, salary) VALUES (?, ?, ?)"; try (PreparedStatement pstmt = con.prepareStatement(sql)) { pstmt.setString(1, "Darshan"); // name pstmt.setString(2, "Engineering"); // dept pstmt.setDouble(3, 75000.0); // salary int rows = pstmt.executeUpdate(); // returns number of rows inserted System.out.println(rows + " row inserted"); }
READ — SELECT All Employees
String sql = "SELECT id, name, dept, salary FROM employees"; try (PreparedStatement pstmt = con.prepareStatement(sql); ResultSet rs = pstmt.executeQuery()) { while (rs.next()) { System.out.println( rs.getInt("id") + " | " + rs.getString("name") + " | " + rs.getString("dept") + " | " + rs.getDouble("salary") ); } }
READ — SELECT with WHERE (find by department)
String sql = "SELECT * FROM employees WHERE dept = ?"; try (PreparedStatement pstmt = con.prepareStatement(sql)) { pstmt.setString(1, "IT"); ResultSet rs = pstmt.executeQuery(); while (rs.next()) { System.out.println(rs.getString("name") + " — " + rs.getDouble("salary")); } }
UPDATE — Change Salary
String sql = "UPDATE employees SET salary = ? WHERE id = ?"; try (PreparedStatement pstmt = con.prepareStatement(sql)) { pstmt.setDouble(1, 85000.0); // new salary pstmt.setInt(2, 101); // employee ID int rows = pstmt.executeUpdate(); System.out.println(rows + " row(s) updated"); }
DELETE — Remove an Employee
String sql = "DELETE FROM employees WHERE id = ?"; try (PreparedStatement pstmt = con.prepareStatement(sql)) { pstmt.setInt(1, 101); // ID to delete int rows = pstmt.executeUpdate(); System.out.println(rows + " row(s) deleted"); }
12. Transaction Management
By default, JDBC runs in auto-commit mode — every SQL statement is committed immediately. But sometimes you need multiple statements to succeed or fail together.
1. Deduct 5000 from A →
UPDATE accounts SET balance = balance - 5000 WHERE id = 'A'2. Add 5000 to B →
UPDATE accounts SET balance = balance + 5000 WHERE id = 'B'What if Step 1 succeeds but Step 2 fails? Money vanishes! That's why we need transactions — both must succeed, or both must be undone (rolled back).
try { con.setAutoCommit(false); // Step 1: Turn OFF auto-commit // Step 2: Execute multiple statements PreparedStatement ps1 = con.prepareStatement( "UPDATE accounts SET balance = balance - ? WHERE id = ?"); ps1.setDouble(1, 5000); ps1.setString(2, "A"); ps1.executeUpdate(); PreparedStatement ps2 = con.prepareStatement( "UPDATE accounts SET balance = balance + ? WHERE id = ?"); ps2.setDouble(1, 5000); ps2.setString(2, "B"); ps2.executeUpdate(); con.commit(); // Step 3: All good — COMMIT both System.out.println("Transfer successful!"); } catch (SQLException e) { con.rollback(); // Step 4: Something failed — ROLLBACK both System.out.println("Transfer failed, rolled back."); e.printStackTrace(); } finally { con.setAutoCommit(true); // Reset auto-commit }
Savepoints
A Savepoint marks a point within a transaction. You can rollback to a savepoint instead of rolling back the entire transaction.
con.setAutoCommit(false); stmt.executeUpdate("INSERT INTO logs VALUES (1, 'Step 1 done')"); Savepoint sp = con.setSavepoint("afterStep1"); // Mark this point stmt.executeUpdate("INSERT INTO logs VALUES (2, 'Step 2 done')"); // Oops, Step 2 was wrong — rollback only Step 2 con.rollback(sp); // Rolls back to savepoint — Step 1 is still intact con.commit(); // Commits Step 1 only
setAutoCommit(false) — start manual transactioncommit() — save all changes permanentlyrollback() — undo all changes since last commitsetSavepoint("name") — create a checkpointrollback(savepoint) — undo changes to a specific checkpoint
13. Batch Processing
When you need to insert 100 or 1000 rows, running them one by one is slow. Batch processing sends multiple SQL statements to the database in one go.
String sql = "INSERT INTO employees (name, dept, salary) VALUES (?, ?, ?)"; PreparedStatement pstmt = con.prepareStatement(sql); con.setAutoCommit(false); // Use transaction for batch // Add row 1 to batch pstmt.setString(1, "Alice"); pstmt.setString(2, "HR"); pstmt.setDouble(3, 50000); pstmt.addBatch(); // Add to batch — not executed yet // Add row 2 to batch pstmt.setString(1, "Bob"); pstmt.setString(2, "IT"); pstmt.setDouble(3, 60000); pstmt.addBatch(); // Add row 3 to batch pstmt.setString(1, "Charlie"); pstmt.setString(2, "Finance"); pstmt.setDouble(3, 55000); pstmt.addBatch(); int[] results = pstmt.executeBatch(); // Execute all 3 at once! con.commit(); // Commit the transaction System.out.println("Inserted " + results.length + " rows");
addBatch() — adds the current parameters to the batchexecuteBatch() — sends all batched statements to the DB at once, returns int[]clearBatch() — clears the current batch without executing
14. Connection Pooling (Brief)
Creating a new database connection is expensive (takes time and resources). In a real web app, hundreds of users hit the server — creating a new connection for each request is wasteful.
Connection Pooling = keep a pool of pre-created connections ready. When a request comes, grab one from the pool. When done, return it to the pool instead of closing it.
With: App starts → Create 10 connections in pool → User request → Grab one → Use → Return to pool — Fast
Popular Connection Pool Libraries
| Library | Notes |
|---|---|
| HikariCP | Fastest. Default in Spring Boot. Industry standard. |
| Apache DBCP | Older but still used. Part of Apache Commons. |
| C3P0 | Legacy. Rarely used in new projects. |
15. Common JDBC Exceptions
| Exception | When It Happens | Fix |
|---|---|---|
ClassNotFoundException |
Driver JAR not in classpath | Add mysql-connector-j.jar to your project |
SQLException |
Any SQL or connection error | Check SQL syntax, URL, credentials |
Communications link failure |
Database server is not running | Start MySQL server |
Access denied for user |
Wrong username or password | Check credentials in getConnection() |
Unknown database 'xyz' |
Database name in URL doesn't exist | Create the database or fix the name |
No suitable driver found |
Wrong URL format or driver not loaded | Check URL starts with jdbc:mysql:// |
SQLException, always print:e.getMessage() — what went wronge.getSQLState() — standard SQL error codee.getErrorCode() — database-specific error code
16. Practice Questions
Which of the following is the correct order of JDBC steps?
What does Class.forName("com.mysql.cj.jdbc.Driver") do?
Which Statement type prevents SQL injection?
What does executeUpdate() return?
What does executeQuery() return?
In a PreparedStatement, what does the ? represent?
Which is the correct JDBC URL for connecting to a MySQL database named "testdb" on localhost?
Which method is used to establish a connection in JDBC?
What is the default ResultSet type in JDBC?
Which method is used to move the ResultSet cursor to the next row?
To manually control transactions, what must you do first?
What happens if a transaction fails and you call con.rollback()?
Which JDBC driver type is the most commonly used and is written entirely in Java?
What exception is thrown when the JDBC driver JAR is not found in the classpath?
Which method is used to add SQL commands to a batch?
In PreparedStatement, what is the index of the first parameter placeholder?
What is the purpose of connection pooling?
Which interface is used to call stored procedures in JDBC?