Chapter 18. DAO support

18.1. Introduction

Spring promotes the use of data access interfaces in your application architecture. These interfaces encapsulate the storage and retrieval of data and objects specific to your business domain without reference to a specific persistence API. Within a layered architecture, the service layer is typically responsible for coordinating responses to a particular business request and it delegates any persistence related activities to objects that implement these data access interfaces. These objects are commonly referred to as DAOs (Data Access Objects) and the architectural layer as a DAL (Data Access Layer).

The benefits of using a DAOs in your application are increased portability across persistence technology and ease of testing. Testing is more easily facilitates because a mock or stub implementation of the data access interface can easily created in a NUnit test so that service layer functionality can be tested without any dependency on the database. This is beneficial because tests that rely on the database are usually hard to setup and tear down and also are impractical for testing exceptional behavior.

The Data Access Object (DAO) support in Spring is aimed at making it easy to work with data access technologies like ADO.NET and NHibernate in a standardized way. Spring provides two central pieces of functionality to meet this goal. The first is providing a common exception hierarchy across providers and the second is providing base DAOs classes that raise the level of abstraction when performing common ADO.NET operations. This allows one to switch between the aforementioned persistence technologies fairly easily and it also allows one to code without worrying about catching exceptions that are specific to each technology.

18.2. Consistent exception hierarchy

Database exceptions in the ADO.NET API are not consistent across providers. The .NET 1.1 BCL did not provide a common base class for ADO.NET exceptions. As such you were required to handle exceptions specific to each provider such as System.Data.SqlClient.SqlException or System.Data.OracleClient.OracleException. The .NET 2.0 BCL improved in this regard by introducing a common base class for exceptions, System.Data.Common.DbException. However the common DbException is not very portable either as it provides a vendor specific error code as the underlying piece of information as to what went wrong. This error code is different across providers for the same conceptual error, such as a violation of data integrity or providing bad SQL grammar.

To promote writing portable and descriptive exception handling code Spring provides a convenient translation from technology specific exceptions like System.Data.SqlClient.SqlException or System.Data.OracleClient.OracleException to its own exception hierarchy with the Spring.Dao.DataAccessException as the root exception. These exceptions wrap the original exception so there is never any risk that one might lose any information as to what might have gone wrong.

In addition to exceptions from ADO.NET providers, Spring can also wrap NHibernate-specific exceptions.. This allows one to handle most persistence exceptions, which are non-recoverable, only in the appropriate layers, without boilerplate using or catch and throw blocks, and exception declarations. As mentioned above, ADO.NET exceptions (including database-specific dialects) are also converted to the same hierarchy, meaning that one can perform some operations with ADO.NET within a consistent programming model. The above holds true for the various template-based versions of the ORM access framework.

The exception hierarchy that Spring uses is outlined in the following image:

(Please note that the class hierarchy detailed in the above image shows only a subset of the whole, rich, DataAccessException hierarchy.)

The exception translation functionality is in the namespace Spring.Data.Support and is based on the interface IAdoExceptionTranslator shown below.

public interface IAdoExceptionTranslator
  DataAccessException Translate( string task, string sql, Exception exception );

The arguments to the translator are a task string providing a description of the task being attempted, the SQL query or update that caused the problem, and the 'raw' exception thrown by the ADO.NET data provider. The additional task and SQL arguments allow for very readable and clear error messages to be created when an exception occurs.

A default implementation, ErrorCodeExceptionTranslator, is provides that uses the configuration file sql-error-codes.xml to define the exact mappings of error codes to Spring DataAccessExceptions. You can use this API directly in your own Spring independent data layer. If you are using Spring's ADO.NET abstraction class, AdoTemplate, the converted exceptions will be thrown automatically. Somewhere in between these two cases is using Spring's declarative transaction management features in .NET 2.0 with the raw ADO.NET APIs and using IAdoExceptionTranslator in your exception handling layer.

Some of the more common exceptions are described here. Please refer to the API documentation for more details.

Table 18.1. Common DataAccessExceptions

BadSqlGrammarExceptionException thrown when SQL specified is invalid.
DataIntegrityViolationExceptionException thrown when an attempt to insert or update data results in violation of an integrity constraint. For example, inserting a duplicate key.
PermissionDeniedDataAccessExceptionException thrown when the underling resource denied a permission to access a specific element, such as a specific database table.
DataAccessResourceFailureExceptionException thrown when a resource fails completely, for example, if we can't connect to a database.

18.3. Consistent abstract classes for DAO support

To make it easier to work with a variety of data access technologies such as ADO.NET, NHibernate, and iBatis.NET in a consistent way, Spring provides a set of abstract DAO classes that one can extend. These abstract classes have methods for providing the data source and any other configuration settings that are specific to the technology one currently is using.

DAO support classes:

  • AdoDaoSupport - super class for ADO.NET data access objects. Requires a DbProvider to be provided; in turn, this class provides a AdoTemplate instance initialized from the supplied DbProvider to subclasses. See the documentation for AdoTemplate for more information.

  • HibernateDaoSupport - super class for NHibernate data access objects. Requires a ISessionFactory to be provided; in turn, this class provides a HibernateTemplate instance initialized from the supplied SessionFactory to subclasses. Can alternatively be initialized directly via a HibernateTemplate, to reuse the latter's settings like SessionFactory, flush mode, exception translator, etc. This is contained in a download seperate from the main Spring.NET distribution.