Lesson 8 · Data Management · starts Section 2

Spring JDBC

Talking to a relational database without the boilerplate — JdbcTemplate, result-set callbacks, and portable exceptions.

What you'll be tested on Which JdbcTemplate method to use for a query vs an update vs DDL; the callback interfaces that map result sets (especially RowMapper); and Spring's DataAccessException hierarchy — what it is, that it's unchecked, and why translation matters.

The problem: JDBC boilerplate

Plain JDBC forces you to open a connection, create a statement, loop the ResultSet, handle a checked SQLException, and close everything in finally — the same ~15 lines around every query. JdbcTemplate does all of that for you; you supply only the SQL and the row-mapping logic.

JdbcTemplate handlesYou supply
Opening & closing the Connection / Statement / ResultSetThe SQL string
Iterating the ResultSetA RowMapper (or other callback)
Translating SQLExceptionDataAccessExceptionQuery parameters

Configure it with a DataSource (it's thread-safe once built):

@Bean
public JdbcTemplate jdbcTemplate(DataSource ds) {
  return new JdbcTemplate(ds);
}
Design pattern & connection timing JdbcTemplate is a classic template-method + callback design: it runs the fixed workflow and calls back into your code for the variable part. It acquires a Connection from the DataSource lazily — when a query/update method is executed — and releases it afterwards, not at construction.

Which method for which job

MethodUse forReturns
queryForObject(sql, rowMapper, args)a query expecting one rowa single object
queryForObject(sql, Integer.class, args)a single scalar (e.g. a COUNT)a single value
query(sql, rowMapper, args)a query returning many rowsa List<T>
queryForList(sql, args)rows as generic mapsList<Map>
update(sql, args)INSERT / UPDATE / DELETErows affected (int)
execute(sql)DDL (CREATE TABLE …) or arbitrary SQLvoid / varies

Read-only? the query* family. Change data? update. Schema/DDL? execute.

Callbacks — handling the ResultSet

JdbcTemplate hands the raw ResultSet to a callback you provide:

CallbackMethodUse when
RowMapper<T>mapRow(rs, rowNum) → one object per rowMost common — map each row to a domain object
ResultSetExtractor<T>extractData(rs) → one object for the whole result setAggregate many rows into one result
RowCallbackHandlerprocessRow(rs) → returns voidStream/side-effects, no collected result
List<Account> accounts = jdbc.query(
    "select id, name from account where active = ?",
    (rs, rowNum) -> new Account(rs.getLong("id"), rs.getString("name")),  // RowMapper
    true);

The DataAccessException hierarchy

JDBC's checked SQLException is cryptic and vendor-specific (error codes differ per database). Spring translates it into a rich, portable hierarchy rooted at DataAccessException:

queryForObject and "wrong number of rows" Expecting exactly one row, queryForObject throws EmptyResultDataAccessException if it finds none, and IncorrectResultSizeDataAccessException if it finds more than one — it does not return null. A common exam catch.
Primary source — read this
Spring Framework 5.3 — Data Access: Using the JDBC Core Classes (JdbcTemplate)

Read "Using JdbcTemplate", "Running Queries", and "Consistent Exception Hierarchy".

Check yourself

This covers the whole JDBC section of your book — answer here rather than opening it. Options shuffle on every load.

Second pass The same concepts are in your book's JDBC section (8 questions). Then keep going — transactions build directly on this.
I'm your teacher — ask me anything. Want to see a full DAO with a RowMapper, or the difference between RowMapper and ResultSetExtractor with code? Ask. Say "continue" for Lesson 9 — Transactions.
← Lesson 7 · AOP Lesson 9 · Transactions →