Blogs

SpringSource Blog

Fine-tuning Spring Data repositories

Oliver Gierke

It's only been a few days only since we've released Spring Data JPA 1.0 GA which is the first major version of a Spring Data project shipping with an implementation of the repository abstraction inside our Spring Data Commons module. The repository abstraction consists of three major parts: defining a repository interface, exposing CRUD methods and adding query methods. Adding query methods was discussed in detail in the first Spring Data JPA blog post. But defining a repository interface and exposing CRUD methods triggered quite some questions in earlier blog posts. That's why will have a closer look at them right now discussing the options available to users.

Declaring a Spring Data repository interface

The declaration of a repository interface can be done in two ways: using annotations or by extending a marker interface:

@RepositoryDefinition(domainClass=Customer.class, idClass=Long.class)
public interface CustomerRepository {
  // declare query methods
}

public interface AccountRepository extends Repository<Account, Long> {
  // declare query methods
}

Which style to pick is pretty much a matter of taste. Purists who like to reduce the dependencies of there code as much as possible will probably stick to the annotation based approach but the inheritance based one is actually more consistent to the rest of the programming model. Whatever approach you choose, the Spring Data infrastructure will understand how to implement repository methods defined in these interfaces.

CRUD methods

The next step you might wanna do is exposing CRUD methods. To aid in doing so we provide two separate interfaces: CrudRepository and PagingAndSortingRepository where the latter extends the former. As the name suggests CrudRepository exposes basic CRUD methods like T save(T entity), T findOne(ID id) and void delete(T entity). PagingAndSortingRepository exposes methods to do pagination out of the box, like Page findAll(Pageable pageable). To expose these methods to your clients simply extend these interfaces like this:

public interface CustomerRepository extends PagingAndSortingRepository<Customer, Long> {
  // declare query methods here
}

Fine-tuning repository interfaces

Extending the previously shown interfaces is quite neat for a quick start but has a significant downside: you expose a pre-defined set of operations to clients that are not under you control, it's pretty much all or nothing until now. There's seemingly no way to only expose read operations while hiding state changing operations entirely.

To circumvent this issue we allow you to define custom base interfaces for your repositories where you can selectively expose the CRUD methods you like:

@NoRepositoryBean
public interface ReadOnlyRepository<T, ID extends Serializable> extends Repository<T, ID> {
  T findOne(ID id);
  Iterable<T> findAll();
  Iterable<T> findAll(Sort sort);
  Page<T> findAll(Pageable pageable);
}

There are a few crucial things to consider here: First, the intermediate interface has to be a Spring Data repository interface as well, which means it either has to be annotated with @RepositoryDefinition or extend Repository as shown here. You should also annotate the interface with @NoRepositoryBean to prevent an interface with formal type parameters T and ID from being picked up by the Spring Data classpath scanning. The last – and probably most crucial – part is that the method signatures have to match the ones in CrudRepository or PagingAndSortingRepository. Your actual repository interface would then look like this:

public interface CustomerRepository extends ReadOnlyRepository<Customer, Long> {
  // declare query methods here
}

As the repository implementations backing the proxy created for that interface implement the methods declared in your custom intermediate interface we can route the calls to those into the implementations. You can even declare shared query methods inside a shared base interface:

@NoRepositoryBean
public interface NamedRepository<T, ID extends Serializable> extends ReadOnlyRepository<T, ID> {
  List<T> findByName(String name);
}

public interface CustomerRepository extends NamedRepository<Customer, Long> { … }

public interface PersonRepository extends NamedRepository<Person, Long> { … }

This assumes Customer and Person share a name property (not necessarily through inheritance). since the query will be automatically derived from the method name then. It's even possible to manually define the queries executed for both concrete repositories by simply declaring JPA named queries Customer.findByName and Person.findByName. Using this approach you can easily come up with tailor-made base interfaces for your application scenario.

Technology specific interfaces

This leaves us with the question of when to use the technology specific base interfaces like JpaRepository. The first reason might be just the quick-start one: it exposes List-based read methods (e.g. List findAll()) which developers using JPA are probably more used to as well as all the CRUD methods derived from it's super interfaces. We also expose some JPA specific methods here as some client's might need to call them. We actually recommend not to expose technology specifics to clients but sometimes it's simply pragmatism over theory. All these specialties can be exposed through a custom base interface as well (redeclare read methods returning Lists, exposing JPA-specific methods), so you pretty much choose what's the least possible effort in your specific situation.

So if you want to get started with the repository abstraction of Spring Data feel free to have a look at our example projects for JPA and Mongo at Github.

Similar Posts

Share this Post
  • Digg
  • Sphinn
  • del.icio.us
  • Facebook
  • Mixx
  • Google Bookmarks
  • DZone
  • LinkedIn
  • Slashdot
  • Technorati
  • Twitter
 

11 responses


  1. Sorry but I still didn't get the point why should I complicate my code with creating and using so much useless interfaces. I prefer using POJO as a repository class which has only methods that are relevant. I don't want to force myself with implementing unused repository methods and vice versa.


  2. Well, that's exactly the point. There's no need to use all the intermediate declarations shown here. If you want to start simple you declare exactly *one* interface per entity class. You can also declare the CRUD methods inside that interface. The point I am discussing in this blog post is that people usually don't want to redeclare a save(T entity) method inside each of their repositories. Thus you want to come up with a common repository interface (which most projects have anyway). The blog post is discussing options what to do at that stage *if you like to*.

    Beyond that the crucial part is that you don't need no implementation whatsoever as we take care of routing the calls to a generic implementation or simply trigger a query. I don't see that removing most of the implementation code of a layer (keeping only the stuff you *really need to implement*) complicates the codebase :) .


  3. Thank you for this so interesting article. The interfaces solution is elegant, but I have a question: Would it better to use a adapter pattern? I think that a abstract class that implements the methods of interface could allow to choose the correct method/s without the mandatory creation of adhoc interfaces. (Adapter classes could be native to spring-data next version ;-) ).

    Regards.


  4. Hi,

    I a really excited by this project but have some concerns about the encapsulation of certain parts of JPA that, in the real world, are necessary to have access to. One thing that springs to mind is that I can't see how I would be able to specify a lock mode on a query for situations where one may need to use pessimistic locking.

    I'd be interested to know how I could use Spring Data JPA, especially the very cool Querydsl integration, whilst still giving me the power to fine tune things by accessing the underlying JPA API when requirements demand that I do.

    Thanks,

    Andrew


  5. Hello,

    thanks a lot for another well written Spring Data blog post. Your examples are clear and real-world-related.

    I like the idea of read-only repositories. But I also would like to stick with the Annotation-only-approach. How does this fit together? I would rather not define several interfaces but instead use maybe an annotation parameter like "@Repository( readonly = true )", just an idea.

    As a side-question: Is it possible to implement more than one reposiroty-interface at once to get recognized by Spring Data, so I have a bunch of "standard" interfaces to use:

    "public interface PersonRepository extends NamedRepository, ReadOnlyRepository { … }"?


  6. Shouldn't be better rename findOne(ID id) to findById(Id id)? Method findOne() with ID argument is understood as "give my the entity by its id", isn't it?.


  7. Timo, an annotation based approach wouldn't really work as it's essentially the compiler we need to satisfy. We're not generating any methods so you need them to be declared somewhere. You can easily extend multiple interfaces. As long the eventually resulting interface has the @RepositoryDefinition annotation in one of its extended interfaces or inherits from Repository in some way it'll be found.

    Jaro, we intentionally renamed our former findById(…) method to find one for two reasons: first we align to the other findAll(…) methods zo that it's either All or One. The second reason is that findById implies that it's backed by a query method actually. So with findOne(…) we have a clean separation between CRUD methods and finders. In fact, you could easily declare query a query method findById(…) and tweak the query as you like, apply query hints and so on.


  8. > Timo, an annotation based approach wouldn't really work as it's essentially
    > the compiler we need to satisfy. We're not generating any methods so you need
    > them to be declared somewhere.

    Oh yes of course, thanks!

    What I currently don't understand is, for what purpose does the @RepositoryDefinition annotation exist? As far as I understand, to get basic CRUD methods, I have to extend the interface "CrudRepository". So I end up with the following exemplary interface:

    ————————-
    @RepositoryDefinition( domainClass = Customer.class, idClass = Long.class )
    public interface CustomerRepository extends CrudRepository {

    }
    ————————-

    To my understanding, Spring Data finds my CustomerRepository via component-scanning, even if I do not use the "@RepositoryDefinition" annotation. So why should one use it (I'm sure there is a reason, but I can't imagine why, currently). Or is, in this case, the following one possible way to do it:

    ————————-
    @Repository
    public interface CustomerRepository extends CrudRepository {

    }
    ————————-

    Thanks a lot


  9. I have a simple Spring Data JPA project and I'm getting this error when the application context is loaded:

    Unexpected exception parsing XML document from class path resource [applicationContext-repository.xml]; nested exception is java.lang.AbstractMethodError: org.springframework.data.jpa.repository.config.SimpleJpaRepositoryConfiguration.getRepositoryBaseInterface()Ljava/lang/Class;


  10. @Timo

    My understanding is that one can use either the @RepositoryDefinition annotation or extend on a Spring Data interface.

    It's a matter of choice.

    From the Spring Data book on page 20:

    Create an interface either extending Repository or annotated with @RepositoryDefinition.


  11. As much as Spring Data JPA tries to make it simpler for us, I must say I still fight a bit to get a generic base repository off the ground.

    Also I'm trying to have all data related code reside in the repository layer and not spill out into the service layer.

    I have a service method using only primitive types:
    public List search(String searchTerm, int pageIndex, int pageSize) {
    return adminRepository.search(searchTerm, pageIndex, pageSize);
    }
    And a repository method routing it to a standard Jpa method:
    public List searchOnPage(String searchTerm, int pageIndex, int pageSize) {
    Pageable pageSpec = buildPageSpecification(pageIndex, pageSize);
    Page wanted = search(searchTerm, pageSpec);
    return wanted.getContent();
    }
    With the following method in the repository interface:
    @Query("SELECT a FROM Admin a WHERE LOWER(a.firstname) LIKE LOWER(:searchTerm) OR LOWER(a.lastname) LIKE LOWER(:searchTerm) ORDER BY a.lastname ASC, a.firstname ASC")
    public List search(@Param("searchTerm") String searchTerm, Pageable page);

    But the service method sees the repository interface method instead of the repository implementation one.

Leave a Reply