For a long time, creating a DAO layer in Spring has been a largely manual process:
- Create a base generic DAO interface.
- Create a generic abstract DAO implementation with general-purpose CRUD methods and common queries (e.g.,
findAll()). - For each DAO we want, extend the base DAO interface with an entity-specific interface (e.g.,
CustomerDao). - For each DAO we want, extend the abstract DAO class with an entity-specific concrete class (e.g.,
HibernateCustomerDao).
Items 1 and 2 amount to creating a homegrown DAO framework, and items 3 and 4 amount to using it to implement DAOs.
Now there’s a better way to do things. The Spring Data family of projects provides a ready-made DAO framework. There are different projects, such as Spring Data JPA, Spring Data Neo4j and Spring Data MongoDB. Something they all have in common is that they provide framework code so we don’t have to implement it ourselves.
Moreover, Spring Data is able to generate concrete DAO implementations and custom queries automatically. So even step 4 above goes away in many cases. With Spring Data JPA you can create DAO tiers by defining interfaces.
In this post we’ll learn how to use Spring Data JPA to clean up our DAO tier. Let’s get the POM and configuration out of the way first. Then we’ll get into the actual repository code. We won’t get into the details of mapping actual entities (via JPA annotations or otherwise) as that’s outside the scope of what we want to cover here.
POM
First we need to choose which JPA provider and which package versions we want to work with. For the JPA provider, we’ll use Hibernate. For the package versions, we’ll use Spring 3.1.1, Spring Data JPA 1.0.3 and Hibernate 4.1.1 since those are current at the time I’m writing this.
Here are the relevant Maven dependencies for pom.xml:
<?xml version="1.0" encoding="UTF-8"?>
<project ...>
...
<!-- Use whatever versions make sense for your project. -->
<properties>
<hibernate.version>4.1.1.Final</hibernate.version>
<spring.version>3.1.1.RELEASE</spring.version>
<spring.data.version>1.0.3.RELEASE</spring.data.version>
</properties>
<dependencies>
... standard Spring dependencies (beans, context, core, etc.) ...
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-jpa</artifactId>
<version>${spring.data.version}</version>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-core</artifactId>
<version>${hibernate.version}</version>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-entitymanager</artifactId>
<version>${hibernate.version}</version>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-ehcache</artifactId>
<version>${hibernate.version}</version>
</dependency>
</dependencies>
...
</project>
JPA configuration
The JPA configuration goes here: /src/main/resources/META-INF/persistence.xml. Here’s the configuration itself. (Adjust as necessary if you’re not using MySQL.)
<?xml version="1.0" encoding="UTF-8"?>
<persistence xmlns="http://java.sun.com/xml/ns/persistence"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_1_0.xsd"
version="1.0">
<persistence-unit name="RunbookManager" transaction-type="RESOURCE_LOCAL">
<description>This unit manages runbooks.</description>
<provider>org.hibernate.ejb.HibernatePersistence</provider>
<jta-data-source>jdbc/RunbookDS</jta-data-source>
<properties>
<property name="hibernate.dialect" value="org.hibernate.dialect.MySQL5Dialect" />
<property name="hibernate.show_sql" value="false" />
</properties>
</persistence-unit>
</persistence>
The configuration above is where we declare Hibernate as our JPA provider.
Now let’s look at the Spring configuration.
Spring configuration
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:jee="http://www.springframework.org/schema/jee"
xmlns:jpa="http://www.springframework.org/schema/data/jpa"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
http://www.springframework.org/schema/data/jpa http://www.springframework.org/schema/data/jpa/spring-jpa-1.0.xsd
http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee-3.1.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.1.xsd">
<jee:jndi-lookup id="dataSource" jndi-name="java:comp/env/jdbc/RunbookDS" resource-ref="true" />
<bean id="entityManagerFactory"
class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean"
p:persistenceUnitName="RunbookManager"
p:dataSource-ref="dataSource" />
<bean id="transactionManager"
class="org.springframework.orm.jpa.JpaTransactionManager"
p:entityManagerFactory-ref="entityManagerFactory" />
<jpa:repositories base-package="com.example.runbooks.repo" />
<tx:annotation-driven />
... other beans ...
</beans>
Note the use of the jpa namespace to declare a package containing the repositories. This package contains the various interfaces we’re about to define. The <jpa:repositories> configuration tells Spring to scan for interfaces and create the repository implementations, magically.
Repository interfaces
OK, now we’ve made it to the good stuff. We’ll look at a few examples here.
To create a new DAO, we simply extend the JpaRepository interface, which is part of Spring Data JPA. It takes two type parameters: the relevant entity type, and its ID type. The interface comes with a bunch of standard CRUD operations and queries; see the JpaRepository API documentation for details on those.
Example 1: A barebones repo
First, the most barebones example would be where we’re perfectly happy with the standard CRUD and query operations that the Spring Data JpaRepository already provides. In that case, all we have to do is extend the interface and we’re done.
package com.example.runbooks.repo;
import org.springframework.data.jpa.repository.JpaRepository;
import com.example.runbooks.model.RunbookGroup;
public interface RunbookGroupRepo
extends JpaRepository<RunbookGroup, Long> { }
With that simple interface definition, Spring Data JPA will be able to create an implementation dynamically that gives us methods like count(), findAll(), findOne(), save(), delete(), deleteAll(), deleteInBatch() and more for free.
Example 2. A simple custom query
Say we have a ChapterType entity with a key property, and we want a query that can find chapter types by key. No problem. We can use conventions around method names to tell Spring Data JPA which query we’d like to see:
package com.example.runbooks.repo;
import org.springframework.data.jpa.repository.JpaRepository;
import com.example.runbooks.model.ChapterType;
public interface ChapterTypeRepo
extends JpaRepository<ChapterType, Long> {
ChapterType findByKey(String key);
}
Spring Data maps the method to a query that effectively accomplishes
from ChapterType where key = :key
Example 3. A more complex custom query
The scheme from example 2 above extends to cases where the properties in question are complex, in a couple of different ways: first, they might involve multiple conditions in the “where” clause; second, they might involve joins. Suppose, for instance, that we want to find a chapter having a certain runbook ID and a certain chapter number. Suppose also that the JPQL would be
from Chapter c where c.runbook.id = :runbookId and
c.chapterType.number = :chapterNumber
Here’s how to build a repo supporting that query:
package com.example.runbooks.repo;
import org.springframework.data.jpa.repository.JpaRepository;
import com.example.runbooks.model.Chapter;
public interface ChapterRepo
extends JpaRepository<Chapter, Long> {
Chapter findByRunbookIdAndChapterTypeNumber(
Long runbookId, Integer chapterNumber);
}
The convention implicit in the method name is fairly obvious, especially in light of the JPQL query. The convention can admittedly lead to some awkward method names, as it does in this case. (A better name might be findByRunbookIdAndChapterNumber().) But the convenience is tough to beat.

By Balamaci April 26, 2012 - 12:06 am
I’m still questioning why it’s necesary to have this empty shell dao classes for every entyty instead of having a GeneralDao on which you can instead pass the entity type as parameter. do generalDao.findById(Customer.class, 3)
By Oliver Gierke April 26, 2012 - 2:45 am
A repository is not an EntityManager. A repository is an abstraction of a collection of entities of a given type. As Willie already stated: where would you put entity specific queries with a general purpose repository?
Beyond that there a re two sides to the story: First, you don’t do repository-per-entity anyway. Repositories are created for aggregate roots in the Domain Driven Design sense. So you would have a CarRepository but no WheelRepository as the Car is the aggregate root and persistence of Wheel instance is done through cascades and manipulation of the Car instances.
Second, I’d argue there’s an architectural aspect to it. Suppose you have a UserRepository that allows to save Users. On top of that you have a UserService that cares about initial password creation and encryption. With that scenario you can enforce clients to go through the UserService all the time ensuring passwords correctly handled. By no means you should allow something like myGenericRepo.save(new User(“foo”, “bar”)) from anywhere but the user management subsystem. If you did so you could easily create unwanted dependencies in the codebase, inconsistent (unprotected in this case) data and so on. So there’s benefit in not allowing each part of the system persisting any type in the system.
By Balamaci April 26, 2012 - 12:36 pm
With a generalDao repository you get also generic components like a generic DataTable over any entity Wheels.class included.
DataTable uses GeneralService method:
public List getList(Class type, ISearch search);
public int count(ISearch search);
which means I can have:
Search filter = new Search(Wheel.class);
filter.addFilterEqual(“manufacturer”, “Michelin”);
btw I’m using hibernate-generic-dao see http://code.google.com/p/hibernate-generic-dao/wiki/SearchExamples
DataTable dataTable = DataTable(Wheel.class, search, max_results_per_row);
This let’s me solve 90% of the business usecases. GeneralDao translates further up also I can have any general component instead of building specialized WheelDataTable, CarDataTable and that’s speed coming from not having to write boilerplate classes, even from the IDE not having to load and compile all those additional classes.
Did I mention I can have this GeneralService and GeneralDao in a library and just import it into a project to have persistence out of the box? True also for the web components.
Sure nothing stops me to have specialized methods in a service UserService dedicated to user queries if I want but which can work still on just the generalDao with the Search(User.class) in most cases. Sure I may have some edge cases where I really need a query if the Search class is not enough but then again in that particular case I can construct a specialized dao of course. But this does not happen often.
PS: I don’t have also to fatten up my Car class to just use the CarDao to be able to get it’s AirFreshner.
By Willie Wheeler April 26, 2012 - 12:20 am
Great question. They could have, but bear in mind that simplifying DAO implementation in the way you suggest makes the actual use of the DAOs somewhat more burdensome, because you have to provide type information with every call instead of simply providing it once with the declaration.
The other thing to note is that custom queries tend to be entity-specific, and there needs to be a place to hang them. It is arguably more intuitive to attach a method like
findByUsername()on theUserRepowith the rest of the CRUD and query methods than to have a general DAO and a separateUserCustomQueryRepowith custom queries.No doubt these are value judgments but I think they’re defensible.
By Oliver Gierke April 26, 2012 - 2:52 am
Nice article, two minor additions:
1. As of Spring 3.1 you don’t need the persistence.xml anymore. You can configure all that stuff on the LocalContainerEntityManagerFactoryBean directly and enable classpath scanning for @Entity classes (through the packagesToScan property).
2. You don’t necessarily need to extend JpaRepository. Extending the marker interface Repository or the more extended ones CrudRepository, PagingAndSortingRepository. Extending JpaRepository you expose the underlying persistence technology to some degree which might be okay but not necessary. You can even simply only extend Repository and add the needed CRUD methods to that interface. As long as they match the signatures in CrudRepository we’re able to route it to the backing implementation.
3. There’s no generation going on. Probably a philosophical aspect but we’re not generating any code at all. We’re simply building a query meta model for the repository interfaces and create and execute the according queries from that on method invocation. For CRUD methods it’s even only delegating to an appropriate implementation class. So it boils down to a bit of proxy magic and intelligent delegation.
Anyway, thanks a lot for spreading the word!
By Willie Wheeler April 26, 2012 - 3:02 am
Thanks Oliver. Yeah, on “generating”, I intended something like “dynamically-generated proxies”, but that’s a good clarification since people familiar with (say) Roo might interpret it in the way you suggest. It’s probably too late for this blog post, but I have a chapter in the book that has some of the same material, and I’ll change the verbiage to better reflect what’s happening.
Regarding spreading the word–my pleasure. I’ve been building these ad hoc DAO frameworks for years now, and so it was a breath of fresh air to see Spring Data. I liked the fact that you made the ID a type param too.
We have a Spring-based CMDB app at work, and as you may know, such apps tend to have lots and lots of entities with standalone lifecycles. Right now we’re using the ad hoc DAO approach but I’m looking forward to converting it over to Spring Data JPA at some point.
By Oliver Gierke April 26, 2012 - 3:05 am
Cool, if you’d like someone to proof read parts of it, feel free to ping me! Looking forward to the book in either case! :)
By Willie Wheeler April 26, 2012 - 10:19 am
That would be great. I have some material on Spring Data MongoDB and Spring Data Neo4j that I’d like to run by you and Michael Hunger as well. I’ll get in touch shortly.