JDBC Fundamentals
Connections, statements, result sets.
JDBC Fundamentals is a free Java Academy lesson on CoddyKit — lesson 1 of 4. You can read the complete lesson below for free — then practise it hands-on in the browser with a built-in code editor and a 24/7 AI tutor. It is part of the Java Academy learning path, one of 4 lessons in the course, and your progress syncs across the web and the CoddyKit app.
What is JDBC?
JDBC (Java Database Connectivity) is the standard API for talking to relational databases from Java.
- It lives in the
java.sqlpackage. - Each database ships a driver that implements the JDBC interfaces.
- You write code against interfaces like
Connection,Statement, andResultSet— never against the vendor classes directly.
This abstraction lets the same code work against PostgreSQL, MySQL, H2, or Oracle.
The Core Interfaces
Three interfaces do most of the work:
Connection— an open session with the database.Statement/PreparedStatement— sends SQL.ResultSet— a cursor over the rows returned by a query.
You obtain a Connection from DriverManager or a DataSource.
Opening a Connection
DriverManager.getConnection(url, user, password) returns a live Connection.
The JDBC URL identifies the database, for example jdbc:postgresql://localhost:5432/shop. Modern drivers register themselves automatically, so you no longer call Class.forName(...).
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
public class Main {
public static void main(String[] args) {
String url = "jdbc:postgresql://localhost:5432/shop";
try (Connection conn = DriverManager.getConnection(url, "app", "secret")) {
System.out.println("Connected: " + !conn.isClosed());
} catch (SQLException e) {
System.out.println("Connection failed: " + e.getMessage());
}
}
}try-with-resources
Connection, Statement, and ResultSet all implement AutoCloseable.
Always open them inside a try-with-resources block so they close automatically — even when an exception is thrown. Leaking connections quickly exhausts the database.
- Resources close in reverse order.
- No
finallyblock needed.
Running a Query
For a simple SELECT, create a Statement and call executeQuery, which returns a ResultSet.
The example uses an in-memory simulation so you can see the flow without a real database. In production the SQL would run against the connection.
import java.util.List;
public class Main {
public static void main(String[] args) {
// Simulated result of: SELECT name FROM users
List<String> names = List.of("Ada", "Linus", "Grace");
System.out.println("Query returned " + names.size() + " rows:");
for (String name : names) {
System.out.println(" - " + name);
}
}
}Reading a ResultSet
A ResultSet starts before the first row. Call rs.next() in a loop; it returns false when there are no more rows.
Read columns by name or 1-based index with typed getters:
rs.getInt("id")rs.getString("name")rs.getDouble("price")
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
public class Main {
public static void main(String[] args) {
String url = "jdbc:postgresql://localhost:5432/shop";
try (Connection conn = DriverManager.getConnection(url, "app", "secret");
Statement st = conn.createStatement();
ResultSet rs = st.executeQuery("SELECT id, name FROM users")) {
while (rs.next()) {
System.out.println(rs.getInt("id") + ": " + rs.getString("name"));
}
} catch (SQLException e) {
System.out.println(e.getMessage());
}
}
}executeUpdate for Writes
For INSERT, UPDATE, and DELETE use executeUpdate. It returns an int: the number of affected rows.
Use execute only when you do not know in advance whether the statement returns a result set.
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.sql.Statement;
public class Main {
public static void main(String[] args) {
String url = "jdbc:postgresql://localhost:5432/shop";
try (Connection conn = DriverManager.getConnection(url, "app", "secret");
Statement st = conn.createStatement()) {
int rows = st.executeUpdate("DELETE FROM sessions WHERE expired = true");
System.out.println("Deleted " + rows + " rows");
} catch (SQLException e) {
System.out.println(e.getMessage());
}
}
}Mapping Rows to Objects
Most applications convert each row into a domain object. Loop over the ResultSet and build instances.
Here is the pattern, simulated so it runs standalone. A record makes the mapping concise.
import java.util.ArrayList;
import java.util.List;
public class Main {
record User(int id, String name) {}
public static void main(String[] args) {
// Stand-in for iterating a ResultSet
String[][] rows = {{"1", "Ada"}, {"2", "Linus"}};
List<User> users = new ArrayList<>();
for (String[] row : rows) {
users.add(new User(Integer.parseInt(row[0]), row[1]));
}
users.forEach(System.out::println);
}
}Handling SQLException
Almost every JDBC method throws the checked exception SQLException.
It carries useful diagnostics:
getMessage()— human-readable text.getSQLState()— the standard 5-character state code.getErrorCode()— the vendor-specific code.
Log these instead of swallowing them.
import java.sql.SQLException;
public class Main {
public static void main(String[] args) {
SQLException e = new SQLException("duplicate key", "23505", 7);
System.out.println("Message: " + e.getMessage());
System.out.println("SQLState: " + e.getSQLState());
System.out.println("ErrorCode: " + e.getErrorCode());
}
}DatabaseMetaData
conn.getMetaData() returns a DatabaseMetaData object describing the database itself.
It can tell you the product name and version, supported features, and even list tables and columns. This is how tools like schema browsers work without hardcoding vendor details.
Putting It Together
A typical read flow:
- Open a
Connectionin try-with-resources. - Create a
Statement(or better, aPreparedStatement). - Call
executeQueryand loop withrs.next(). - Map columns into objects.
- Let try-with-resources close everything.
Next lesson we make queries safe against injection.
Quick Check
What does Statement.executeUpdate return?
Recap
You now know JDBC's core flow:
DriverManager.getConnectionopens aConnection.Statementsends SQL;executeQueryreturns aResultSet,executeUpdatereturns affected row count.- Iterate with
rs.next()and typed getters. - Always use try-with-resources, and handle
SQLExceptionwith its state and error codes.
Frequently asked questions
Is the “JDBC Fundamentals” lesson free?
Yes — the full text of “JDBC Fundamentals” is free to read here on the web, and the Java Academy course includes 4 lessons in total. To practise it interactively (a built-in code editor and a 24/7 AI tutor) and unlock the rest of the Java Academy course, upgrade to CoddyKit PRO.
What will I learn in “JDBC Fundamentals”?
Connections, statements, result sets. You practise Java Academy with hands-on code you run directly in the browser, and a 24/7 AI tutor answers your questions as you work through the lesson.
Do I need any experience to start Java Academy?
No prior experience is required. Java Academy on CoddyKit is structured for beginners through advanced learners; this is — lesson 1 of 4, so you can start here or from the beginning and move at your own pace.
How long does the “JDBC Fundamentals” lesson take?
Most CoddyKit lessons take about 5–10 minutes. Each one is bite-sized and interactive, so you make steady progress and pick up exactly where you left off across the web and the app.
Can I write and run code in this Java Academy lesson?
Yes. Every Java Academy lesson includes a built-in code editor, so you write and run real code right in your browser and get instant AI feedback — no local setup required.
All lessons in this course
- JDBC Fundamentals
- PreparedStatement
- Transactions
- Connection Pooling with HikariCP