Streamlining database access with Spring JdbcTemplate beyond JPA

Spring's JdbcTemplate is one of those quiet workhorses many Java teams overlook once they adopt JPA. Across Australian software houses, from fintech startups in Sydney to enterprise systems running out of Perth, the plain SQL approach remains a deliberate choice rather than a fallback. It gives engineers full control over queries and produces code that is straightforward to read.

For projects where the data model is stable and performance matters, JdbcTemplate offers a refreshing alternative. It keeps the door open for raw SQL while providing connection pooling, exception translation, and integration with Spring transactions.

Why JdbcTemplate remains relevant in modern Spring projects

ORM frameworks excel when object graphs dominate the domain. Many real systems, however, deal with flat tables, reporting views, or legacy schemas. In those situations, an extra layer of mapping becomes friction rather than productivity. JdbcTemplate hands the developer a thin wrapper around java.sql.Connection, so the SQL you write is the SQL the database executes.

Australian teams often weigh this trade-off explicitly. A Brisbane-based logistics platform may issue fifty read-only queries against a denormalised view, while a Melbourne accounting firm might need only three tables and a handful of inserts. In both cases, the setup cost of JPA does not pay back.

Adding the right dependencies to your build

The starter artefact is spring-boot-starter-jdbc, which pulls in the template, transaction management, and an exception translator. For local development many Sydney engineers default to H2, while production deployments rely on PostgreSQL or MySQL hosted on AWS Sydney or Google Cloud's australia-southeast1 region.

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<dependency>
    <groupId>org.postgresql</groupId>
    <artifactId>postgresql</artifactId>
    <scope>runtime</scope>
</dependency>

Configuration lives in application.yml. Spring Boot auto-configures a DataSource from the supplied JDBC URL and credentials, so there is no need for an EntityManagerFactory or Hibernate dialect settings.

Core operations and parameter binding

Three methods cover most daily work: query, queryForObject, and update. The first returns a list of mapped objects, the second fetches a single row, and the third handles inserts, updates, and deletes. Named parameters through NamedParameterJdbcTemplate become preferable once a query exceeds three bindings.

List<Customer> customers = jdbcTemplate.query(
    "SELECT id, name, email FROM customer WHERE state = :state",
    Map.of("state", "NSW"),
    new CustomerRowMapper()
);

The update method returns the number of affected rows, suitable for both inserts and conditional updates. KeyHolder captures autogenerated keys without an extra round trip, a pattern frequently used by Perth teams integrating with back-office mining systems.

Mapping result sets cleanly with RowMapper

RowMapper is the bridge between a ResultSet and a domain object. BeanPropertyRowMapper handles simple POJOs through reflection, while a custom mapper gives finer control over type conversion and column aliasing. Australian teams working with monetary values often need BigDecimal mapped from NUMERIC columns to preserve precision when handling AUD amounts.

public class CustomerRowMapper implements RowMapper<Customer> {
    @Override
    public Customer mapRow(ResultSet rs, int rowNum) throws SQLException {
        Customer c = new Customer();
        c.setId(rs.getLong("id"));
        c.setName(rs.getString("name"));
        c.setCreditLimit(rs.getBigDecimal("credit_limit"));
        return c;
    }
}

RowMappers also help when dealing with database views that return computed columns. The mapper can apply formatting, currency conversion, or timezone adjustments from AEST to UTC before the object leaves the data layer.

Transactional behaviour without JPA

Spring's @Transactional annotation works with JdbcTemplate out of the box because both share the same PlatformTransactionManager. A service method that calls multiple update statements commits together or rolls back as a single unit, with no need for an entity manager. TransactionTemplate offers a callback-style API that integrates cleanly with legacy code at banks such as Commonwealth Bank or ANZ, where transaction boundaries must align with strict audit requirements.

HikariCP, which Spring Boot uses by default, performs well out of the box. Production databases hosted in ap-southeast-2 benefit from explicit pool sizing to avoid stalls when requests queue waiting for a free connection.

Comparing JdbcTemplate with other data access options

Every data access tool in the Spring ecosystem has a sweet spot. The comparison below summarises the practical differences between JdbcTemplate, NamedParameterJdbcTemplate, Spring Data JPA, and MyBatis, focusing on the concerns that matter during architectural discussions on Australian projects.

Feature JdbcTemplate NamedParameterJdbcTemplate Spring Data JPA MyBatis
SQL control Full Full Indirect via HQL/JPQL Full via XML or annotations
Learning curve Low Low Medium Medium
Boilerplate Low Low Very low Medium
Caching and dirty checking No No Yes (first-level cache) No
Best suited for Reporting, ETL, simple CRUD Read-heavy services Rich domain models Legacy SQL mapping

For teams already invested in Spring, the choice often narrows to JdbcTemplate and JPA. MyBatis remains common in older Australian government projects that predate the Spring Data wave.

Practical patterns for Australian engineering teams

The choice between JdbcTemplate and JPA rarely comes down to fashion. Reporting services, ETL pipelines, financial systems handling AUD transactions, and ATO integrations all benefit from the transparency of raw SQL. Object-heavy domains with cascading relationships are better served by JPA. Knowing the difference separates a productive Spring codebase from one weighed down by accidental complexity.

A pragmatic approach is to mix both. Use JPA for the core domain where aggregates and consistency matter, and reach for JdbcTemplate in supporting services where the data is flat and queries are predictable. This split matches the way many Sydney fintechs and Melbourne consultancies already structure their microservices.

Key advantages of the JdbcTemplate approach

Best practices for production code