Spring Data – Part 2: JPA

What happened before?

Part 1: Spring Data Commons
Part 2: Spring Data JPA

After looking at the Spring Data Commons project in the first part of this blog series, today I’m going to introduce you to the sub project Spring Data JPA.

JPA

Being a part of JEE stack, JPA is a standard API to persist Java objects into relational database systems. With the JPQL query language, database queries can be written independent of a specific SQL dialect. So understanding Spring Data JPA requires at least a basic knowledge of the JPA API.

Spring Data JPA

Based on Spring’s standard JPA Support, Spring Data JPA further simplifies writing JPA repositories (and much more). Usually one or more parameters have to be set before executing a JPQL query. To do so, the developer writes boilerplate code like this:

@Entity
@NamedQuery( name="myQuery", query = "SELECT u FROM User u where u.fullName = :fullName" )
public class User {
...
}
 
@Repository
public class ClassicUserRepository {
 
   @PersistenceContext EntityManager em;
 
   public List<User> findByFullName(String fullName) {
      TypedQuery<User> q = getEntityManger().createNamedQuery("myQuery", User.class);
 
      q.setParameter("fullName", fullName);
 
      return q.getResultList();
   }
   ...

This can be slightly reduced by using the fluent interface of a TypedQuery

@Repository
public class ClassicUserRepository {
 
   @PersistenceContext EntityManager em;
 
   public List<User> findByFullName(String fullName) {
      return getEntityManger().createNamedQuery("myQuery", User.class)
         .setParameter("fullName", fullName)
         .getResultList();
   }
   ...

… but still you are implementing a method that calls setters and executes the query for each and every single query. With Spring Data JPA the same query comes down to the following piece of code:

package repositories;
 
public interface UserRepository extends JpaRepository<User, String> {
 
   List<User> findByFullName(String fullName);
}

The basic idea is to derive all information that is required to execute a query from the signature of method declared in an interface(!). At runtime Spring injects a corresponding implementation that creates and executes the query via the JPA criteria API. This way, a very large subset of queries within a project can be written very fast and concise. The pattern can be combined with the common sorting and pagination features:

public interface UserRepository extends JpaRepository<User, String> {
 
   List<User> findByFullName(String fullName, Sort sort);
 
   List<User> findByFullName(String fullName, Pageable paging);
}

With Spring Data JPA, JPQL queries don’t have to be declared as @NamedQuerys in the class file of the corresponding JPA entity. Instead a query is an annotation of the repository method(!):

	@Transactional(timeout = 2, propagation = Propagation.REQUIRED)
	@Query("SELECT u FROM User u WHERE u.fullName = 'User 3'")
	List<User> findByGivenQuery();

I really like this approach. The query is located in the place where it is executed and does not pollute the JPA entity itself. Separation of Concerns (SoC) at its best. A drawback is that you have to use a new Spring annotation @Query. Why didn’t they allow the use of @NamedQuery annotations?

A nice bonus is the validation of JPQL queries when the application context is assembled. This way JPQL syntax errors are detected as early as possible. Usually these errors are detected at query execution time.

Example

I provide a maven project holding all example source code at Github. The examples use OpenJPA as a JPA provider and the RDBMS HyperSQL DB. The unit test jpa.JpaRepoTest is a good place to start.

The most important configuration part is the one that defines the package structure of our repositorie interfaces to be automagically JPA-ified:

	<jpa:repositories base-package="jpa"/>

If your are using more than one EntityManagerFactory, you have to specifiy which one should be used by your repositories.

Is there more?

Yes. As I said in the first part of this blog series, I’m just presenting some choosen aspects of Spring Data JPA. For a complete list of features, please check the project’s homepage.

What’s next?

Expect upcoming blog posts on Spring Data MongoDB or Spring Data Neo4J.

  • Facebook
  • Delicious
  • Digg
  • StumbleUpon
  • Reddit
  • Blogger
  • LinkedIn
Tobias Trelle

4 Responses to Spring Data – Part 2: JPA

  1. Fabian Lange says:

    Tobias,

    public @interface javax.persistenc.NamedQuery
    

    has this annotation

    @Target({TYPE})
    

    While I agree with your preference of having the query on the method, the JPA Interface doesnt allow this.
    I wonder if a backwards compatible change to the spec allowing this at method level would be possible.

  2. Oliver Gierke says:

    The reason for introducing @Query is that we support some additional quirks (defining a count query e.g.) in it besides the fact which Fabian mentioned. Regarding the potential change of being able to use @NamedQuery on a method I don’t think this is gonna happen as this does not make sense in a pure JPA context and I don’t expect the expert group to add stuff to it just because some arbitrary framework would like to see this ;).

  3. Java dude says:

    Good post, I didn’t know much about Spring JPA support but doesn’t it like combining JEE with Spring ?

  4. will says:

    Very cool. Ideally, I’d like to read the query from an external file (say myQuery.sql)
    This would allow me to not have to recompile the code every time the query changes.

    Is this possible in spring boot jpa?

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>