Last updated 19 September 2026. Default examples are mid-level Spring and Java 17–21. Junior and senior sit in labeled sections so the first screen is not a fresher dump.
Spring data access is templates, exception translation, and transaction boundaries around JDBC and JPA. Interviews still ask when to use JdbcTemplate versus Spring Data JPA. This hub is that choice. JDBC child questions stay as deep links. Hibernate has its own hub; do not duplicate mapping theory here.
Junior
DataSource is the connection factory. Spring never wants you to drive DriverManager in a web app. JdbcTemplate runs SQL, maps rows, and translates SQLException into DataAccessException. You pass a RowMapper. You do not leak a Connection if you stay on the template.
@Transactional on a service method starts a transaction and binds a connection. The repository should not open a second transaction by accident. REQUIRED joins. A repository with REQUIRED and a service with REQUIRED is one transaction if the call stack is proxied correctly.
Spring Data JPA repositories are interfaces. The framework writes the query from the method name or from @Query. That is fast to write and easy to hide an N+1. findAll() on a large table is not a design.
Choose JdbcTemplate when the SQL is the product: reporting, CTEs, vendor hints. Choose JPA when you mutate a graph of entities and want dirty checking. Choose both in one app if you keep transactions consistent.
Mid-level
Transaction isolation and timeout belong on @Transactional. readOnly=true is a hint that skips dirty checking in Hibernate and may use a read replica if you routed it. It is not a security boundary.
Exception translation: DataIntegrityViolationException, EmptyResultDataAccessException, CannotAcquireLockException. Catch those at the service or API layer. Catching SQLException in a Spring service means you skipped the template or the translator.
Batch: JdbcTemplate.batchUpdate or a StatelessSession. JPA persist in a loop without batch properties is a latency bug. Clear the persistence context in batches or you grow the session until the heap dies.
Multiple DataSources: you need a transaction manager per source or JTA, plus @Transactional("txManagerName"). RoutingDataSource is a lookup, not magic consistency across two databases.
Testing: @DataJpaTest plus Testcontainers beats an in-memory database when you use PostgreSQL JSON or advisory locks. Flyway should run in tests the same way it runs in prod.
Senior
Senior data access is connection pool sizing (Hikari), leak detection, and the relation between pool size, transaction time, and throughput. A pool of 10 and a 2-second transaction caps you at five transactions per second per instance if every request needs a connection the whole time. Virtual threads do not add connections.
Read replicas, delayed consistency, and transactional outbox. If you publish an event after commit, use afterCommit or an outbox table. Publishing inside the transaction and then rolling back is how you lie to consumers. This site's IndexNow outbox is the same pattern in another domain.
Query plan awareness: EXPLAIN, indexes that match WHERE and JOIN, and why OFFSET pagination dies on large pages. Keyset pagination is the senior answer for deep pages.
Spring Data JDBC and jOOQ exist. Mention them when JPA's persistence context is the problem, not the solution.
Probe yourself
JdbcTemplate versus Spring Data JPA, one use each?
JdbcTemplate for explicit SQL you must control. JPA for entity graphs you mutate under a persistence context.
What does Spring do with SQLException from a DataSource?
It translates it to a DataAccessException hierarchy so service code does not depend on JDBC types.
Why can readOnly=true still write in some setups?
It is a hint. A native query or a mis-routed DataSource can still write. Do not treat it as enforcement.
Related questions on this topic are linked below. Read the full answer on the question URL; this hub does not repeat those answers.
Pitfalls interviewers still use
Injecting JdbcTemplate and EntityManager into the same method without a shared transaction. You can write with JPA and read uncommitted with JDBC or the opposite, depending on isolation and whether they share a connection. One @Transactional on the service, one transaction manager, or admit you have two units of work.
Spring Data method names that generate cartesian products: findByAccountCustomersAddressCity. The SQL is legal and horrible. Write a @Query you have EXPLAINed.
Ignoring getSingleResult() versus an empty Optional. EmptyResultDataAccessException versus NoResultException versus Optional.empty(). Pick the API and test the empty path.
Connection pool default of 10 on a 32-core box with 200 virtual threads. You will queue on Hikari, not on the JVM. Size the pool to the database, not to the thread count.
Running Flyway in prod as a side effect of the web process without a lock. Two dynos migrate at once. Use a single migrator job or a lock Flyway already supports.
For the room: sketch a transfer: debit, credit, one transaction, one outbox row, afterCommit publish. If the event is inside the transaction, they will ask what happens on rollback.
How to answer in the room
Name the stack you are on: JdbcTemplate, Spring Data JDBC, JPA with Hibernate, or R2DBC. Do not mix answers. JdbcTemplate is SQL you write plus exception translation. Spring Data JPA is a repository interface plus a vendor. R2DBC is reactive and a different transaction story.
One service method, one transaction, one transaction manager when two resources must commit together. If you inject JdbcTemplate and EntityManager and they do not share a connection, you have two units of work. Say so. afterCommit is where you publish events that must not fire on rollback.
Spring Data derived queries are fine until they join three graphs and return a cartesian product. Then write @Query, project to a DTO, and EXPLAIN it. getOne/getById/getReferenceById semantics changed across versions; say which version you are on and whether you get a reference or a load.
Empty results: Optional, empty list, EmptyResultDataAccessException, NoResultException. Pick one API and test the empty path. getSingleResult() on zero rows is not Optional.empty() unless you wrapped it.
Pools: Hikari size is a database conversation. Virtual threads do not mean 500 connections. Timeouts on connection acquire should fail the request, not hang the dyno. Leak detection is a config you turn on in staging.
Migrations run once. Two dynos starting Flyway without a lock is a race. Prefer a release job. ddl-auto in production is a defect unless you are a throwaway demo and you say that out loud.
JDBC still matters: PreparedStatement, parameter binding, and never string-concatenated SQL. Batch updates exist. Generated keys exist. If the interview is JDBC-only, stay there; do not hide behind JPA annotations you cannot translate to SQL.
If they ask you to choose JPA or JDBC for a reporting query that returns five columns from three tables, pick JDBC or a DTO query. Hydrating entities to throw them away is how services get slow and then get an extra cache. The cache then hides a wrong join. Start with the SQL. If the next question is 'how do you paginate', say keyset over OFFSET for large tables, and prove it with EXPLAIN, not with a repository method name. If they want a transaction around the report, say read-only and ask whether isolation must be repeatable or a warehouse snapshot is enough.