Lesson 1 of 628 minModule progress 0%

Module 16: Database Integration and Persistence

JDBC Fundamentals

Learn the direct JDBC workflow clearly enough that higher-level frameworks stop feeling magical. You will open connections, bind parameters safely, map rows into domain objects, and understand where low-level database code still matters even in modern stacks.

Author

Java Learner Editorial Team

Reviewer

Technical review by Java Learner

Last reviewed

2026-04-17

Java version

Java 25 LTS

How this lesson was prepared: AI-assisted draft, manually expanded into a full lesson guide, and checked against current official Java, Spring, testing, and delivery documentation.

Learning goals

  • Walk through the full JDBC flow from connection to mapped result
  • Use `PreparedStatement` as the default tool for parameterized SQL
  • Write resource-safe JDBC code that is readable enough to debug later

Before you start

  • You are comfortable with exceptions, classes, and file-style resource management

Lesson roadmap: Start with the mental model, then follow the design choices, common pitfalls, and the practical workflow you should apply in a real project.

JDBC is the direct low-level API for talking to relational databases from Java: It gives you explicit control over connections, SQL, parameters, and result processing.

The core flow is consistent: Open a connection, prepare a statement, bind parameters, execute, and read the result set.

Prepared statements matter for both safety and clarity: They reduce SQL injection risk and make parameter binding explicit.

Advanced habit: Use try-with-resources so connections, statements, and result sets always close correctly.

Mental model: JDBC is the thin layer between Java and SQL. It is explicit by design, which makes it a great place to learn what ORMs and repository abstractions are eventually doing on your behalf.

Mapping is part of the work: Running a query is only half the job. You still need to turn rows into objects in a consistent and maintainable way, which is why many teams centralize row-mapping logic instead of repeating it inline.

Error handling matters: SQL exceptions carry information you will need in real debugging. Do not swallow them; wrap them with context so the caller knows which query or action failed.

Repository boundary: Keep raw JDBC code close to the persistence layer. Higher layers should ask for domain objects or application-level results, not ResultSet details.

How to study this module: Keep a simple domain in mind, such as users, orders, or tasks. It is easier to understand JDBC, transactions, and ORM mapping when every SQL statement serves a clear business action.

Code review mindset: Database code is not only about “does it work.” Reviewers also look for resource safety, transaction boundaries, predictable query counts, and whether the persistence layer leaks too much SQL detail into higher layers.

Production habit: Measure query behavior early. Slow queries, long transactions, and leaked connections usually hurt real systems more than syntax mistakes do.

Runnable examples

PreparedStatement with one parameter

String sql = "SELECT id, name FROM users WHERE email = ?";
PreparedStatement statement = connection.prepareStatement(sql);
statement.setString(1, "ada@example.com");
ResultSet rs = statement.executeQuery();

Expected output

The query runs with one safely bound email parameter.

Map a row to a simple domain object

List<String> names = new ArrayList<>();
while (rs.next()) {
    names.add(rs.getString("name"));
}
System.out.println(names);

Expected output

Each row is converted into Java data that the rest of the application can use.

Common mistakes

Concatenating user input directly into SQL strings

Default to `PreparedStatement` so parameters stay explicit and safer to handle.

Leaving JDBC logic scattered through services or controllers

Keep connection, SQL, and row-mapping code behind a repository or DAO boundary.

Mini exercise

Write the pseudocode for a `findUserById(long id)` method that opens a connection, binds the ID, executes a query, maps one row, and closes every resource with try-with-resources.

Summary

  • JDBC is the direct database access layer in Java.
  • Prepared statements are safer than string-built SQL.
  • Try-with-resources is essential for clean database code.
  • Understanding JDBC makes higher-level persistence frameworks easier to reason about and debug.
  • Good JDBC code is explicit, parameterized, and disciplined about resource ownership.

Next step

Next, stop opening a brand-new connection for every request and use a pool instead.

Sources used

Advertisement

Lesson check

Why is `PreparedStatement` usually preferred over `Statement`?

Next lesson →