7 min read

In this article written by Shameer Kunjumohamed and Hamidreza Sattari, authors of the book Spring Essentials, we will learn how to access data with Spring.

(For more resources related to this topic, see here.)

Data access or persistence is a major technical feature of data-driven applications. It is a critical area where careful design and expertise is required. Modern enterprise systems use a wide variety of data storage mechanisms, ranging from traditional relational databases such as Oracle, SQL Server, and Sybase to more flexible, schema-less NoSQL databases such as MongoDB, Cassandra, and Couchbase. Spring Framework provides comprehensive support for data persistence in multiple flavors of mechanisms, ranging from convenient template components to smart abstractions over popular Object Relational Mapping (ORM) tools and libraries, making them much easier to use. Spring’s data access support is another great reason for choosing it to develop Java applications.

Spring Framework offers developers the following primary approaches for data persistence mechanisms to choose from:

  • Spring JDBC
  • ORM data access
  • Spring Data

Furthermore, Spring standardizes the preceding approaches under a unified Data Access Object (DAO) notation called @Repository.

Another compelling reason for using Spring is its first class transaction support. Spring provides consistent transaction management, abstracting different transaction APIs such as JTA, JDBC, JPA, Hibernate, JDO, and other container-specific transaction implementations.

In order to make development and prototyping easier, Spring provides embedded database support, smart data source abstractions, and excellent test integration.

This article explores various data access mechanisms provided by Spring Framework and its comprehensive support for transaction management in both standalone and web environments, with relevant examples.

Why use Spring Data Access when we have JDBC?

JDBC (short for Java Database Connectivity), the Java Standard Edition API for data connectivity from Java to relational databases, is a very a low-level framework. Data access via JDBC is often cumbersome; the boilerplate code that the developer needs to write makes the it error-prone. Moreover, JDBC exception handling is not sufficient for most use cases; there exists a real need for simplified but extensive and configurable exception handling for data access. Spring JDBC encapsulates the often-repeating code, simplifying the developer’s code tremendously and letting him focus entirely on his business logic. Spring Data Access components abstract the technical details, including lookup and management of persistence resources such as connection, statement, and result set, and accept the specific SQL statements and relevant parameters to perform the operation. They use the same JDBC API under the hood while exposing simplified, straightforward interfaces for the client’s use. This approach helps make a much cleaner and hence maintainable data access layer for Spring applications.

DataSource

The first step of connecting to a database from any Java application is obtaining a connection object specified by JDBC. DataSource, part of Java SE, is a generalized factory of java.sql.Connection objects that represents the physical connection to the database, which is the preferred means of producing a connection. DataSource handles transaction management, connection lookup, and pooling functionalities, relieving the developer from these infrastructural issues.

DataSource objects are often implemented by database driver vendors and typically looked up via JNDI. Application servers and servlet engines provide their own implementations of DataSource, a connector to the one provided by the database vendor, or both. Typically configured inside XML-based server descriptor files, server-supplied DataSource objects generally provide built-in connection pooling and transaction support. As a developer, you just configure your DataSource objects inside the server (configuration files) declaratively in XML and look it up from your application via JNDI.

In a Spring application, you configure your DataSource reference as a Spring bean and inject it as a dependency to your DAOs or other persistence resources. The Spring <jee:jndi-lookup/> tag (of  the http://www.springframework.org/schema/jee namespace) shown here allows you to easily look up and construct JNDI resources, including a DataSource object defined from inside an application server. For applications deployed on a J2EE application server, a JNDI DataSource object provided by the container is recommended.

<jee:jndi-lookup id="taskifyDS"
                 jndi-name="java:jboss/datasources/taskify"/>

For standalone applications, you need to create your own DataSource implementation or use third-party implementations, such as Apache Commons DBCP, C3P0, and BoneCP. The following is a sample DataSource configuration using Apache Commons DBCP2:

<bean id="taskifyDS" class="org.apache.commons.dbcp2.BasicDataSource"
                           destroy-method="close">
   <property name="driverClassName" value="${driverClassName}" />
   <property name="url" value="${url}" />
   <property name="username" value="${username}" />
   <property name="password" value="${password}" />
   . . .
</bean>

Make sure you add the corresponding dependency (of your DataSource implementation) to your build file. The following is the one for DBCP2:

<dependency>
   <groupId>org.apache.commons</groupId>
   <artifactId>commons-dbcp2</artifactId>
   <version>2.1.1</version>
</dependency>

Spring provides a simple implementation of DataSource called DriverManagerDataSource, which is only for testing and development purposes, not for production use. Note that it does not provide connection pooling. Here is how you configure it inside your application:

<bean id="taskifyDS"
class="org.springframework.jdbc.datasource.DriverManagerDataSource">
   <property name="driverClassName" value="${driverClassName}" />
   <property name="url" value="${url}" />
   <property name="username" value="${username}" />
   <property name="password" value="${password}" />
</bean>

It can also be configured in a pure JavaConfig model, as shown in the following code:

@Bean
DataSource getDatasource() {
   DriverManagerDataSource dataSource = new
       DriverManagerDataSource(pgDsProps.getProperty("url"));
   dataSource.setDriverClassName(
                   pgDsProps.getProperty("driverClassName"));
   dataSource.setUsername(pgDsProps.getProperty("username"));
   dataSource.setPassword(pgDsProps.getProperty("password"));
   return dataSource;
}

Never use DriverManagerDataSource on production environments. Use third-party DataSources such as DBCP, C3P0, and BoneCP for standalone applications, and JNDI DataSource, provided by the container, for J2EE containers instead.

Using embedded databases

For prototyping and test environments, it would be a good idea to use Java-based embedded databases for quickly ramping up the project. Spring natively supports HSQL, H2, and Derby database engines for this purpose. Here is a sample DataSource configuration for an embedded HSQL database:

@Bean
DataSource getHsqlDatasource() {
   return new
         EmbeddedDatabaseBuilder().setType(EmbeddedDatabaseType.HSQL)
         .addScript("db-scripts/hsql/db-schema.sql")
         .addScript("db-scripts/hsql/data.sql")
         .addScript("db-scripts/hsql/storedprocs.sql")
         .addScript("db-scripts/hsql/functions.sql")
         .setSeparator("/").build();
}

The XML version of the same would look as shown in the following code:

<jdbc:embedded-database id="dataSource" type="HSQL">
<jdbc:script location="classpath:db-scripts/hsql/db-schema.sql" />
   . . .
</jdbc:embedded-database>

Handling exceptions in the Spring data layer

With traditional JDBC based applications, exception handling is based on java.sql.SQLException, which is a checked exception. It forces the developer to write catch and finally blocks carefully for proper handling and to avoid resource leakages. Spring, with its smart exception hierarchy based on runtime exception, saves the developer from this nightmare. By having DataAccessException as the root, Spring bundles a big set of meaningful exceptions that translate the traditional JDBC exceptions. Besides JDBC, Spring covers the Hibernate, JPA, and JDO exceptions in a consistent manner.

Spring uses SQLErrorCodeExceptionTranslator, which inherits SQLExceptionTranslator in order to translate SQLExceptions to DataAccessExceptions. We can extend this class for customizing the default translations. We can replace the default translator with our custom implementation by injecting it into the persistence resources (such as JdbcTemplate, which we will cover soon).

DAO support and @Repository annotation

The standard way of accessing data is via specialized DAOs that perform persistence functions under the data access layer. Spring follows the same pattern by providing DAO components and allowing developers to mark their data access components as DAOs using an annotation called @Repository. This approach ensures consistency over various data access technologies, such as JDBC, Hibernate, JPA, and JDO, as well as project-specific repositories. Spring applies SQLExceptionTranslator across all these methods consistently.

Spring recommends your data access components to be annotated with the stereotype @Repository. The term “repository” was originally defined in Domain-Driven Design, Eric Evans, Addison-Wesley, as “a mechanism for encapsulating storage, retrieval, and search behavior which emulates a collection of objects.” This annotation makes the class eligible for DataAccessException translation by Spring Framework.

Spring Data, another standard data access mechanism provided by Spring, revolves around @Repository components.

Summary

We have so far explored Spring Framework’s comprehensive coverage of all the technical aspects around data access and transaction. Spring provides multiple convenient data access methods, which takes away much of the hard work involved in building the data layer from the developer and also standardizes business components. Correct usage of Spring data access components will ensure that our data layer is clean and highly maintainable.

Resources for Article:


Further resources on this subject:


LEAVE A REPLY

Please enter your comment!
Please enter your name here