BT

Hades - JPA Repositories Done Right

Posted by Oliver Gierke on Dec 20, 2010 |

Introduction

Almost every application out there in the world has to access data to do its work. Using a domain driven design approach you will need to define and build repositories for the entities that make up your domain. Java developers nowadays often use JPA to implement these repositories. JPA has made this task easier, but still requires a lot of boilerplate code. Hades is an open source library that's built on top of JPA and Spring to significantly improve the implementation of data access layers by reducing the effort to the amount that's actually needed. This article will take you on a guided tour and give you an overview on what Hades can actually do for you regarding data access layer development and takes a look at the new features of the very recent version 2.0.

The very core part of Hades consists of a generic repository implementation that provides you not only with basic CRUD (create, read, update and delete) methods but adds some functionality on top that removes the need to implement common scenarios around that (e.g. pagination) yourself. This includes the ability to query the repository for pages of entities, dynamically adding sort definitions and so on.

Queries

As most of the data access operations you typically have to implement are querying operations one of Hades' core features is to ease defining and executing queries with the least effort possible. The process of doing so actually consists of three steps:

  1. Add an entity specific repository interface
  2. Add query methods
  3. Fine tune queries

In the first step you declare an interface that extends GenericDao<Entity, Id> which will cause the CRUD operations defined in the Hades interface being available on your interface as well:

public interface UserRepository extends GenericDao<User, Long> { … }

The next step is adding methods for your queries to the interface that serve your business requirements:

List findByUsername(String username);

This should raise the question of how the query will be derived from the method. If you don't add any additional metadata, Hades will try to lookup a JPA named query of the form {entity}.{method-name} and use that if available. In case it does not find any it will try to parse the method name and create a query from it. So the prior example would result in a query select u from User u where u.username = ?. The query parser of course supports a lot more keywords, so feel free to check them out in the reference documentation.

If you prefer defining the query manually you can leverage Hades' @Query annotation to define the query to be executed directly at the method:

@Query("select from User u where u.lastname = ?");
List someReallyStrangeName(String lastname);

This gives you freedom in naming methods the way you like while preserving the full control over the query definition without bothering you with the boilerplate query execution code. There's lots of other stuff around executing queries like using pagination with queries, executing modifying queries and so on.

Bootstrapping Hades

So now that you have seen how to create repository interfaces with query methods it's probably interesting how you get that stuff started and ready to be used. You can create instances for the repository interface by creating a GenericDaoFactory instance and ask it to create repositories for you:

EntityManager em = … // Get access to EntityManager
GenericDaoFactory factory = GenericDaoFactory.create(em);
UserRepository userRepository = factory.getDao(UserRepository.class);

As repositories are usually handed to its clients by dependency injection there is sophisticated integration for Hades' usage inside a Spring application. So to bootstrap Hades in a Spring app you simply use the Hades namespace and declare a base package that shall be scanned for repositories.

<hades:dao-config base-package="com.acme.*.repository" />

This would pick up all repository interfaces extending GenericDao and create Spring beans for each of them. Of course the namespace allows much more fine grained control over what's to be detected. The Hades Eclipse plugin provides seamless integration into Spring IDE and SpringSource Tool Suite (STS) so that you will be able to reference Hades repositories from other beans, have them marked as Spring beans in your workspace and so on. For details check out the reference documentation.

Auditing

It's a very common requirement to track creator, modifier and the according dates for entities. Hades provides an EntityListener that will transparently do the job for you. To activate Hades auditing you need to define the AuditingEntityListener in your orm.xml

<persistence-unit-metadata>
  <persistence-unit-defaults>
    <entity-listeners>
      <entity-listener class="org.synyx.hades.domain.auditing.support.AuditingEntityListener" />
    </entity-listeners>
  </persistence-unit-defaults>
</persistence-unit-metadata>

and then activate auditing in your Spring config as follows:

 

<hades:auditing auditor-aware-ref="auditorAware" />

The referenced Spring bean auditorAware has to implement the AuditorAware interface that you'd typically implement querying your security module for the current user. If you only want to track creation and modification date, just omit the attribute. For more on the auditing feature, consult the documentation.

JPA 2.0 and transactional repositories

As of version 2.0 Hades is based on JPA 2.0 as well as the according versions of Hibernate, EclipseLink and OpenJPA. Starting with that version the CRUD operations are transactional out of the box so that in very simple cases there's no need for a transactional wrapping layer anymore. Furthermore concrete repository interfaces can be made transactional easily as well.

public interface UserRepository extends GenericDao<User, Long> {
  @Transactional(readOnly = true);
  List<User> findByUsername();

  @Override
  @Transactional(readOnly = true, timeout = 60);
  List<User> readAll();
}

As you can see you can simply annotate the query methods with @Transactional to let them participate in transactions. You don't have to use annotations if you don't like to, XML based transaction configuration simply works as well. The CRUD operations implemented in GenericDao are transactional by default (with readOnly set to true for read-only operations). Reconfiguring transaction settings for those methods is done by simply redeclaring the method and applying a custom @Transactional on it. For more details check out the reference documentation on transactions.

Specifications

A quite cool feature of the latest release is the extension ofGenericDao to be able to execute specifications. A specification is a Domain Driven Design concept coined by Eric Evans and Martin Fowler that boils down to capturing business rules for an entity into a predicate. Hades provides an abstraction to easily build such predicates based on the JPA 2.0 criteria API. Supposed you have books written by authors. Hades let's you define specifications like that:

class BookSpecifications {
  public Specification<Book> hasAuthorWithFirstnameLike(final String firstname) {
    return new Specification<Book>() {
      public Predicate toPredicate(Root<Book> root, CriteriaQuery<?> cq, CriteriaBuilder cb) {
        return cb.like(root.join("author").get("firstname"), firstname);
      }
    }
  }

  public Specification<Book> hasGenre(final Genre genre) {
    return new Specification<Book>() {
      public Predicate toPredicate(Root<Book> root, CriteriaQuery<?> cq, CriteriaBuilder cb) {
        return cb.equal(root.get( "genre"), genre);
      }
    }
  }
}

Admittedly this is not the most beautiful piece of code to be written (it will hopefully become a lot better with Java 8's Single Abstract Method (SAM) feature). On the positive side we gain the possibility to execute specifications right on the repository:

bookRepository.readAll(hasAuthorWithFirstnameLike("Oliv*"));

Okay, that's pretty much what we could have achieved with a declared query, right? The power of specifications really shines when combining them into new specifications. Hades provides a Specifications helper class that allows on-the-fly composition:

bookRepository.readAll(where(hasAuthorWithFirstnameLike( "Oliv*").or(hasGenre(Genres.IT)));

This way, extending your repository becomes just a matter of adding new specifications. With a hand full of specs you get a quite flexible DSL-like API to query your repository without polluting the repository with query methods for every exotic use case. A more detailed introduction can be found at in the documentation.

Extensions

Another part of the 2.0 release is an extensions module that seamlessly integrates Hades with upper layer technologies of your application like Spring MVC. It provides a PropertyEditor and Spring 3.0 Converter that allows to transparently bind entities to Spring MVC controller methods by their id as well as an MVC extension that dynamically extracts pagination information from an HTTP request. So a controller displaying a page of users could look something like this:

@Controller
class UserController {
  @Autowired
  UserRepository userRepository;

  @RequestMapping("/users")
  public void showUsers(Pageable pageable, Model model) {
    model.addAttribute("users", userRepository.readAll(pageable));
  }

  @RequestMapping("/users/{id}")
  public String showUser(@PathVariable("id") User user, Model model) {
    model.addAttribute("user", user);
    return"user";
  }
}

As you can see for the showUsers(…) method there's no need to parse the HttpServletRequest for pagination information yourself. Note, that in showUser(…) the method parameter bound to the id path variable is already the entity. As the configuration of Spring MVC infrastructure goes beyond the scope of this article feel free to checkout the reference documentation chapter on the extensions module provides configuration examples to ease the setup.

What's next?

Looking forward, beyond the 2.1.x line, Hades will become part of the Spring Data project where it's core will serve as a base for repository implementations for other datastores as well. Spring Data is a SpringSource project that aims to provide idiomatic Spring support for the varieties of emerging, specialized datastores, including NoSQL database.

Summary

Hades hugely eases implementing data access layers with JPA. You get sophisticated CRUD operations, execution of queries as well as specifications for free. It can be used standalone but integrates nicely with Spring as well. Beyond that, there's a Spring IDE/STS Eclipse plugin as well as a Spring Roo add-on to easily create repositories with Roo. For more information see the project website.

About the author

Oliver Gierke is Senior Consultant at SpringSource, a division of VMware and the project lead of the Hades open source project. He started the project at his former company Synyx about two years ago.

Hello stranger!

You need to Register an InfoQ account or or login to post comments. But there's so much more behind being registered.

Get the most out of the InfoQ experience.

Tell us what you think

Allowed html: a,b,br,blockquote,i,li,pre,u,ul,p

Email me replies to any of my messages in this thread

done right? by Andy Jefferson

While you say it is "done right", perhaps fix your manual which has
@Entity
@NamedQuery(name = "User.findByEmailAddress",
query = "FROM User u WHERE u.emailAddress = ?1")

where the query is invalid JPQL. According to the JPA2 spec BNF
select_statement :: = select_clause from_clause 
[where_clause] [groupby_clause]
[having_clause] [orderby_clause]

so having a select clause is not "optional" (maybe in HQL, but that is not what you are presenting).

Presumably this would also work in other JPA providers such as DataNucleus ? i.e the JPA persistence solution being used in VMforce (being worked by the same company IIRC).

Regards
--Andy (DataNucleus)

Re: done right? by Oliver Gierke

Hi Andy,

thanks for the detailled look you took. I'll fix the documentation glitch ASAP.

As Hades is build on top of JPA I expect DataNucleus to work out of the box. It should just be a matter of setting up the EntityManagerFactory correctly. The move to Spring Data has just been started, which means that there hasn't been a deep involvement of SpringSource until now. This is the reason why the vmforce team is not involved that much yet. We will have a MongoDB specific release of the Hades concepts in the near future followed by the JPA port right after that.

Cheers,
Ollie

Re: done right? by Khosro Asgharifard

Really interesting.
Khosro.

Querying by Anthavio Lenz

Hades caught my eye before, but ultimate typesafe querying tool is still Querydsl
source.mysema.com/static/querydsl/2.0.6/referen...

Re: Querying by Timo Westkämper

Hades has an interesting approach, but I believe that the syntactic overhead for specifications is too high. The Querydsl equivalents would be


//has author with firstName like
List<Book> books = query.from(book).where(book.author.firstName.like(firstName));

// has genre
List<Book> books = query.from(book).where(book.genre.eq(genre));


Integrating Hades with Querydsl might be a win-win. Contact us if you are interested.

Br,
Timo Westkämper.</book></book>

Re: Querying by Oliver Gierke

Querydsl looks really cool. A lot cleaner than the JPA criteria API. While it's probably a bit late for Hades we should definitely hook up to do something for the Spring Data JPA project. I've added you on Skype, so feel free to ping me ;).

Cheers,
Ollie

Re: Querying by Andy Jefferson

When VMware get around to providing Spring Data for JDO too then it will, automatically, gain access to JDO Typesafe queries, something directly related to QueryDSL ;-)

Allowed html: a,b,br,blockquote,i,li,pre,u,ul,p

Email me replies to any of my messages in this thread

Allowed html: a,b,br,blockquote,i,li,pre,u,ul,p

Email me replies to any of my messages in this thread

7 Discuss

Educational Content

General Feedback
Bugs
Advertising
Editorial
InfoQ.com and all content copyright © 2006-2014 C4Media Inc. InfoQ.com hosted at Contegix, the best ISP we've ever worked with.
Privacy policy
BT