skip to content

What extra behavior does @Repository provide beyond making a class a bean, and how does persistence exception translation actually work?

level: middleimportance: must knowfreq 65%

answer

  1. @Repository = bean + exception translation marker
  2. PersistenceExceptionTranslationPostProcessor proxies @Repository beans
  3. native SQLException/PersistenceException -> DataAccessException
  4. PersistenceExceptionTranslator does the mapping
  5. 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 lines
java
import 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

for a junior

Know @Repository is for DAOs and that it converts DB exceptions into Spring's exceptions.

for a middle

Name DataAccessException hierarchy and that a post-processor + translator perform the mapping via AOP.

for a senior

Explain the proxy mechanics, self-invocation caveat, and that Spring Data repositories get this for free.

for a principal

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

context