Data Access Object/DAO support in Spring is aimed at making it easier to work with data access technologies (such as JDBC, Hibernate or JPA). This allows you to easily switch between the above persistent storage technologies without having to worry about catching exceptions specific to each technology.

Consistent hierarchy of exceptions

Spring provides a convenient transition from technology-specific exceptions such as SQLException to its own hierarchy of exception classes, in which the root exception is DataAccessException. These exceptions wrap the original exception, so there is no risk that you might lose any information about what might have gone wrong.

In addition to JDBC exceptions, Spring can also wrap JPA and Hibernate specific exceptions, transforming them into a set of organized runtime exceptions. This allows you to handle most non-recoverable persistent exceptions only at the appropriate levels, without resorting to annoying boilerplate catch-and-throw blocks and exception declarations in your DAOs. (However, you can still catch and handle exceptions wherever needed). As mentioned above, JDBC exceptions (including database-specific dialects) are also translated into the same hierarchy, which means you can do some JDBC operations within the consistent programming model.

The previous explanation also applies to the different template classes in the Spring support for different ORM frameworks. If you use interceptor-based classes, the application should handle the handling of HibernateExceptions and PersistenceExceptions itself, preferably delegating it to the convertHibernateAccessException(..) or convertJpaAccessException(..) of the SessionFactoryUtils utility, respectively. These methods convert exceptions to exceptions that are compatible with the exceptions in the org.springframework.dao exception hierarchy. Since PersistenceExceptions are unchecked, they can also be thrown (at the expense of the DAO's typed abstraction in terms of exceptions).

The following image shows the exception hierarchy that Spring offers. (Note that the class hierarchy shown in the figure shows only part of the entire DataAccessException hierarchy.

Annotations used to configure DAO or repository classes

The best way to ensure that your data access objects (DAOs) or repositories are guaranteed to resolve exceptions is to use the @Repository annotation. This annotation also allows component scanning support tools to find and configure your DAOs and repositories without having to provide XML configuration records for them. The following example shows how to use the @Repository annotation:

Java
@Repository 
public class SomeMovieFinder implements MovieFinder {
    // ...
}
  1. Annotation @Repository.
Kotlin
@Repository 
class SomeMovieFinder : MovieFinder {
    // ...
}
  1. Annotation @Repository.

Any DAO implementation or repository requires access to a persistent storage resource, depending on the persistent storage technology used. For example, a JDBC-based repository needs access to DataSource from JDBC, while a JPA-based repository needs access to EntityManager. The easiest way to achieve this is to inject a resource dependency using one of the @Autowired, @Inject, @Resource or @PersistenceContext annotations. The following example is working in relation to the JPA repository:

Java
@Repository
public class JpaMovieFinder implements MovieFinder {
    @PersistenceContext
    private EntityManager entityManager;
    // ...
}
Kotlin
@Repository
class JpaMovieFinder : MovieFinder {
    @PersistenceContext
    private lateinit var entityManager: EntityManager
    // ...
}

If you are using the classic Hibernate APIs, you can implement a SessionFactory, as shown in the following example:

Java
@Repository
public class HibernateMovieFinder implements MovieFinder {
    private SessionFactory sessionFactory;
    @Autowired
    public void setSessionFactory(SessionFactory sessionFactory) {
        this.sessionFactory = sessionFactory;
    }
    // ...
}
Kotlin
@Repository
class HibernateMovieFinder(private val sessionFactory: SessionFactory) : MovieFinder {
    // ...
}

The last example we'll demonstrate is from a typical JDBC support tool. DataSource can be injected into an initialization method or constructor where you create JdbcTemplate and other data access support classes (such as SimpleJdbcCall, etc. ) using this DataSource. The following example automatically discovers and binds DataSource:

Java
@Repository
public class JdbcMovieFinder implements MovieFinder {
    private JdbcTemplate jdbcTemplate;
    @Autowired
    public void init(DataSource dataSource) {
        this.jdbcTemplate = new JdbcTemplate(dataSource);
    }
    // ...
}
Kotlin
@Repository
class JdbcMovieFinder(dataSource: DataSource) : MovieFinder {
    private val jdbcTemplate = JdbcTemplate(dataSource)
    // ...
}
For details on how to configure your application context to take advantage of these annotations, see the specific description of each persistent storage technology.