Spring JDBC provides an abstraction layer that simplifies JDBC database access and the parsing of database-vendor specific error codes.
npx @tessl/cli install tessl/maven-org-springframework--spring-jdbc@7.0.0Spring 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()