Understanding Spring DAO support with DataAccessException

[Updated: Nov 14, 2017, Created: Nov 13, 2017]

The Data Access Object (DAO) support in Spring allows us to isolate minimal amount of code related to particular database technology easily. The most important DAO support is DataAccessException hierarchy which let the client code handle the exceptions without knowing the details of the particular data access API in use (e.g. JDBC, JPA, Hibernate etc). That means it is possible to handle an exception like JdbcSQLException in a generic way without knowing that JDBC is being used in the DAO layer. This allows one to switch between the persistence technologies easily without changing a lot of code.

Also DataAccessException is a RuntimeException exception. That allows us to handle non-recoverable persistence exceptions without writing boilerplate catch-and-throw blocks and exception declarations in DAOs.

Example

We are going to use H2 database in this example.

src/main/resources/createEmployeeTable.sql

CREATE TABLE EMPLOYEE(
ID BIGINT  PRIMARY KEY AUTO_INCREMENT,
NAME VARCHAR(255) NOT NULL,
DEPT VARCHAR(255) NOT NULL
);

Implementing Data Access Layer

public interface EmployeeDao {
  void save(Employee employee);
}

We are intentionally not going to populate 'DEPT NOT NULL' column so that we can see how the exception is handled.

@Repository
public class EmployeeJdbcDao implements EmployeeDao {
  @Autowired
  private DataSource dataSource;
  private JdbcTemplate jdbcTemplate;

  @PostConstruct
  private void postConstruct() {
      jdbcTemplate = new JdbcTemplate(dataSource);
  }

  @Override
  public void save(Employee employee) {
      String sql = "insert into EMPLOYEE (NAME) values (?)";
      jdbcTemplate.update(sql, employee.getName());
  }
}
public class Employee {
  private long id;
  private String name;
  private String dept;
    .............
}

The @Repository annotation

As seen in above example, we are using @Repository annotation instead of of @Component in our DAO implementation. This annotation guarantees that all Spring DAO support, including the exception translation is provided in a consistent way.

Service Layer

@Service
public class EmployeeService {
  @Autowired
  EmployeeDao employeeDao;

  public void saveEmployee(Employee employee) {
      try {
          employeeDao.save(employee);
      } catch (DataAccessException dae) {
          System.err.println(dae);
      }
  }
}

As seen above, in our service layer we are handling data technology specific exception by using spring's DataAccessException which is translated from the underlying exception. There are two advantages; first we don't have to catch any H2 or JDBC related exceptions in our DAO (even we don't have to have the knowledge of those exceptions), second if in future we want to migrate from JDBC to other technology e.g. JPA then we just need to change our DAO implementation without changing our service layer.

Java Config and main class

@Configuration
@ComponentScan
public class AppConfig {

  @Bean
  public DataSource h2DataSource() {
      return new EmbeddedDatabaseBuilder()
              .setType(EmbeddedDatabaseType.H2)
              .addScript("createEmployeeTable.sql")
              .build();
  }

  public static void main(String[] args) {
      AnnotationConfigApplicationContext context =
              new AnnotationConfigApplicationContext(AppConfig.class);
      EmployeeService service = context.getBean(EmployeeService.class);
      service.saveEmployee(Employee.create("Sam", "IT"));
  }
}
Nov 13, 2017 10:53:04 PM org.springframework.context.support.AbstractApplicationContext prepareRefresh
INFO: Refreshing org.springframework.context.annotation.AnnotationConfigApplicationContext@736e9adb: startup date [Mon Nov 13 22:53:04 CST 2017]; root of context hierarchy
Nov 13, 2017 10:53:04 PM org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseFactory initDatabase
INFO: Starting embedded database: url='jdbc:h2:mem:testdb;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=false', username='sa'
Nov 13, 2017 10:53:04 PM org.springframework.jdbc.datasource.init.ScriptUtils executeSqlScript
INFO: Executing SQL script from class path resource [createEmployeeTable.sql]
Nov 13, 2017 10:53:04 PM org.springframework.jdbc.datasource.init.ScriptUtils executeSqlScript
INFO: Executed SQL script from class path resource [createEmployeeTable.sql] in 16 ms.
Nov 13, 2017 10:53:04 PM org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitions
INFO: Loading XML bean definitions from class path resource [org/springframework/jdbc/support/sql-error-codes.xml]
Nov 13, 2017 10:53:04 PM org.springframework.jdbc.support.SQLErrorCodesFactory <init>
INFO: SQLErrorCodes loaded: [DB2, Derby, H2, HSQL, Informix, MS-SQL, MySQL, Oracle, PostgreSQL, Sybase, Hana]
org.springframework.dao.DataIntegrityViolationException: PreparedStatementCallback; SQL [insert into EMPLOYEE (NAME) values (?)NULL not allowed for column "DEPT"; SQL statement:
insert into EMPLOYEE (NAME) values (?) [23502-196]; nested exception is org.h2.jdbc.JdbcSQLException: NULL not allowed for column "DEPT"; SQL statement:

In above output, the H2 JdbcSQLException is translated to DataIntegrityViolationException which is a subclass of DataAccessException. Check out more subclasses of this exception here.

Example Project

Dependencies and Technologies Used :

  • spring-context 5.0.1.RELEASE: Spring Context.
  • spring-jdbc 5.0.1.RELEASE: Spring JDBC.
  • h2 1.4.196: H2 Database Engine.
  • JDK 1.8
  • Maven 3.3.9

Spring Dao Exception Handling Example Select All Download
  • data-access-exception-example
    • src
      • main
        • java
          • com
            • logicbig
              • example
        • resources

See Also