What extra behavior does @Repository provide beyond making a class a bean, and how does persistence exception translation actually work?
answer
- @Repository = bean + exception translation marker
- PersistenceExceptionTranslationPostProcessor proxies @Repository beans
- native SQLException/PersistenceException -> DataAccessException
- PersistenceExceptionTranslator does the mapping
- unchecked, technology-agnostic hierarchy
basics
~10 s@Repository marks persistence classes and enables automatic translation of native, technology-specific exceptions (like JDBC SQLException or JPA PersistenceException) into Spring's consistent DataAccessException hierarchy, so callers don't depend on the underlying data-access API.
solid answer
~40 s@Repository is a @Component specialization for the persistence layer. Its distinctive feature is enabling **persistence exception translation**: an infrastructure bean, PersistenceExceptionTranslationPostProcessor (PET-PP), advises all @Repository beans with an AOP interceptor (PersistenceExceptionTranslationInterceptor). When a method throws a native exception — a JDBC SQLException, a JPA PersistenceException, a Hibernate exception — the interceptor delegates to registered PersistenceExceptionTranslators to convert it into a subclass of Spring's unchecked DataAccessException (e.g., DataIntegrityViolationException, DuplicateKeyException). This gives a uniform, technology-agnostic exception hierarchy so service code catches Spring exceptions instead of vendor-specific ones. In Spring Boot the PET-PP is auto-configured. Translation only happens for beans annotated @Repository and requires a translator for the persistence tech (JPA/Hibernate/JDBC providers register one).
code
java · 20 linesimport org.springframework.stereotype.Repository;
import org.springframework.dao.DataIntegrityViolationException;
import jakarta.persistence.EntityManager;
import jakarta.persistence.PersistenceContext;
@Repository // eligible for persistence exception translation
public class JpaUserDao {
@PersistenceContext EntityManager em;
public void save(User u) {
// A native jakarta.persistence.PersistenceException thrown here
// is translated by the PersistenceExceptionTranslationInterceptor
// into a Spring DataAccessException, e.g.:
// DataIntegrityViolationException on a unique-constraint breach.
em.persist(u);
}
}
// Service can catch the portable type:
// catch (DataIntegrityViolationException e) { ... }go deeper
Know @Repository is for DAOs and that it converts DB exceptions into Spring's exceptions.
Name DataAccessException hierarchy and that a post-processor + translator perform the mapping via AOP.
Explain the proxy mechanics, self-invocation caveat, and that Spring Data repositories get this for free.
Discuss decoupling the service layer from vendor error semantics and how portable categories aid multi-DB support and testing.
**The problem it solves:** different persistence technologies throw different exceptions — JDBC throws checked `java.sql.SQLException`, JPA throws `javax.persistence.PersistenceException` (and subclasses), Hibernate throws its own `HibernateException`. If service code caught these directly, it would be coupled to the data-access technology and to vendor-specific error semantics. Spring standardizes this with **`DataAccessException`** (`org.springframework.dao.DataAccessException`), a rich hierarchy of **unchecked** exceptions: `DataIntegrityViolationException`, `DuplicateKeyException`, `OptimisticLockingFailureException`, `CannotAcquireLockException`, `EmptyResultDataAccessException`, and more. **How @Repository triggers translation:** 1. **`@Repository`** (`org.springframework.stereotype.Repository`) is both a stereotype (registers the bean) and a **marker** that the class is eligible for exception translation. 2. A **`PersistenceExceptionTranslationPostProcessor`** (a `BeanPostProcessor`) must be present in the context. It scans for beans annotated `@Repository` and wraps them in an AOP proxy carrying a **`PersistenceExceptionTranslationInterceptor`**. In Spring Boot this post-processor is registered automatically; in plain Spring you add it (or `@EnableTransactionManagement`/`<context:annotation-config>` style setups often bring in the pieces; for JPA, `LocalContainerEntityManagerFactoryBean` also participates). 3. At runtime, when a repository method throws a native runtime exception, the interceptor asks each registered **`PersistenceExceptionTranslator`** to translate it. `HibernateJpaDialect`/`EntityManagerFactory` and `JpaTransactionManager` expose translators for JPA/Hibernate; `SQLExceptionTranslator` handles JDBC (used inside `JdbcTemplate`). 4. If a translator recognizes the exception, it returns the matching `DataAccessException` subtype, which is thrown instead. If none recognizes it, the original exception propagates unchanged. **Important nuances:** - Translation is applied via **AOP proxying**, so it only kicks in when the call crosses the proxy boundary — an internal self-call within the repository bean is not intercepted. - Translation applies to **unchecked** exceptions thrown out of the method; checked `SQLException` from raw JDBC isn't thrown by templates because `JdbcTemplate` already translates internally. - **Spring Data JPA** repositories are already proxied and translate exceptions, so you often get this for free even without your own `@Repository` class. - The `@Repository` annotation on its own does nothing without the post-processor; the two work together. - Translation maps native error to a **portable** category — e.g., a unique-constraint violation from Postgres and MySQL both surface as `DuplicateKeyException`/`DataIntegrityViolationException`, decoupling callers from vendor SQL state codes. **When to rely on it:** put `@Repository` on hand-written DAO classes (raw JDBC via `JdbcTemplate`, or an `EntityManager`) so service-layer code can catch `DataAccessException` types uniformly and keep persistence details out of the business layer.
- What bean must exist for @Repository exception translation to actually happen?A PersistenceExceptionTranslationPostProcessor (auto-registered by Spring Boot). It proxies @Repository beans with the translation interceptor; without it, @Repository does not translate exceptions.
- What is the root of Spring's translated exception hierarchy, and is it checked or unchecked?org.springframework.dao.DataAccessException — an unchecked (RuntimeException) hierarchy, so callers aren't forced to declare or catch it.
- Does exception translation happen on an internal self-invocation inside the repository?No — translation is AOP-proxy-based, so a self-call that doesn't cross the proxy boundary isn't intercepted.
saying these in an interview costs you the question
- Saying @Repository translates exceptions all by itself with no post-processor
- Claiming DataAccessException is checked
- Thinking translation converts exceptions for @Service/@Component too
- Confusing it with @Transactional rollback behavior