Spring JDBC provides an abstraction layer that simplifies JDBC database access and the parsing of database-vendor specific error codes.
—
Pending
Does it follow best practices?
Impact
Pending
No eval scenarios have been run
Pending
The risk profile of this skill
Spring JDBC provides JDBC abstraction with automatic resource management, exception translation, and template-based operations.
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>7.0.1</version>
</dependency>import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate;
import org.springframework.jdbc.core.simple.JdbcClient;
import javax.sql.DataSource;
// Setup
DataSource ds = new DriverManagerDataSource("jdbc:postgresql://localhost:5432/mydb", "user", "pass");
JdbcTemplate jdbc = new JdbcTemplate(ds);
NamedParameterJdbcTemplate named = new NamedParameterJdbcTemplate(ds);
JdbcClient client = JdbcClient.create(ds); // Spring 6.1+| Class | Use Case | Parameters |
|---|---|---|
JdbcTemplate | Basic JDBC operations | Positional ? |
NamedParameterJdbcTemplate | Named parameters | :paramName |
JdbcClient | Fluent API (modern) | Both positional and named |
SimpleJdbcInsert | Metadata-driven inserts | Auto key retrieval |
SimpleJdbcCall | Stored procedures/functions | Auto parameter mapping |
Query Operations:
queryForObject(sql, Class, args)queryForObject(sql, RowMapper, args)query(sql, RowMapper, args)queryForList(sql, Class, args)queryForMap(sql, args) or queryForList(sql, args)queryForStream(sql, RowMapper, args)query(sql, ResultSetExtractor, args)Update Operations:
update(sql, args)update(PreparedStatementCreator, KeyHolder)batchUpdate(sql, List<Object[]>)namedTemplate.batchUpdate(sql, SqlParameterSource[])Complex Operations:
SimpleJdbcCallSimpleJdbcInsert@Transactional or TransactionTemplateUser user = jdbc.queryForObject(
"SELECT id, name, email FROM users WHERE id = ?",
(rs, row) -> new User(rs.getLong("id"), rs.getString("name"), rs.getString("email")),
123
);List<User> users = jdbc.query(
"SELECT id, name, email FROM users WHERE status = ?",
(rs, row) -> new User(rs.getLong("id"), rs.getString("name"), rs.getString("email")),
"active"
);KeyHolder keyHolder = new GeneratedKeyHolder();
jdbc.update(con -> {
PreparedStatement ps = con.prepareStatement(
"INSERT INTO users (name, email) VALUES (?, ?)",
Statement.RETURN_GENERATED_KEYS
);
ps.setString(1, "John");
ps.setString(2, "john@example.com");
return ps;
}, keyHolder);
Long id = keyHolder.getKey().longValue();Map<String, Object> params = Map.of("status", "active", "minAge", 18);
List<User> users = named.query(
"SELECT * FROM users WHERE status = :status AND age >= :minAge",
params,
(rs, row) -> new User(...)
);User user = client.sql("SELECT * FROM users WHERE id = ?")
.param(123)
.query((rs, row) -> new User(...))
.single();
List<User> users = client.sql("SELECT * FROM users WHERE status = :status")
.param("status", "active")
.query((rs, row) -> new User(...))
.list();List<Object[]> batch = List.of(
new Object[]{"John", "john@example.com"},
new Object[]{"Jane", "jane@example.com"}
);
int[] counts = jdbc.batchUpdate("INSERT INTO users (name, email) VALUES (?, ?)", batch);SimpleJdbcInsert insert = new SimpleJdbcInsert(ds)
.withTableName("users")
.usingGeneratedKeyColumns("id");
Number id = insert.executeAndReturnKey(Map.of("name", "John", "email", "john@example.com"));@Transactional
public void transfer(Long fromId, Long toId, BigDecimal amount) {
jdbc.update("UPDATE accounts SET balance = balance - ? WHERE id = ?", amount, fromId);
jdbc.update("UPDATE accounts SET balance = balance + ? WHERE id = ?", amount, toId);
}DataAccessException (unchecked)
├── BadSqlGrammarException (invalid SQL)
├── DataIntegrityViolationException
│ └── DuplicateKeyException
├── DataAccessResourceFailureException
│ └── CannotGetJdbcConnectionException
├── InvalidResultSetAccessException
└── UncategorizedSQLExceptionHandle specific exceptions:
try {
jdbc.update("INSERT INTO users (email) VALUES (?)", "user@example.com");
} catch (DuplicateKeyException e) {
// Handle duplicate
} catch (DataAccessException e) {
// Handle other errors
}Thread-safe (reusable):
Not thread-safe:
jdbc.setFetchSize(1000)queryForStream() instead of query()