The full picture
public void register(User u) {
try {
jdbc.update("INSERT INTO users(email, name) VALUES (?, ?)",
u.email(), u.name());
} catch (DuplicateKeyException e) { // translated from vendor code
throw new EmailAlreadyRegisteredException(u.email(), e);
}
}
// vs raw JDBC: getConnection/prepare/execute/close x3 in nested finally,
// then: if (e.getSQLState().startsWith("23")) … per vendor. All gone.@Repositoryactivates the translation post-processor for JPA repositories; JdbcTemplate translates natively viaSQLErrorCodeSQLExceptionTranslator(sql-error-codes.xml per vendor).- Where each tool fits: JdbcTemplate/JdbcClient (3.2+) for explicit SQL, reporting, bulk ops; Spring Data JPA for aggregate CRUD; jOOQ/MyBatis when SQL is the domain language. Senior teams mix deliberately rather than forcing one.
- The hierarchy is layered: transient (
TransientDataAccessException— retry may work: deadlock loser, timeout) vs non-transient — which is exactly what your retry middleware should key on.