Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Using generics in Spring Data JPA repositories

I have a number of simple object types that need to be persisted to a database. I am using Spring JPA to manage this persistence. For each object type I need to build the following:

import org.springframework.data.jpa.repository.JpaRepository;  public interface FacilityRepository extends JpaRepository<Facility, Long> { }   public interface FacilityService {     public Facility create(Facility facility); }  @Service public class FacilityServiceImpl implements FacilityService {      @Resource     private FacilityRepository countryRepository;      @Transactional     public Facility create(Facility facility) {         Facility created = facility;         return facilityRepository.save(created);     } } 

It occurred to me that it may be possible to replace the multiple classes for each object type with three generics based classes, thus saving a lot of boilerplate coding. I am not exactly sure how to go about it and in fact if it is a good idea?

like image 869
skyman Avatar asked Oct 17 '13 03:10

skyman


People also ask

Should I use JpaRepository or CrudRepository?

Crud Repository doesn't provide methods for implementing pagination and sorting. JpaRepository ties your repositories to the JPA persistence technology so it should be avoided. We should use CrudRepository or PagingAndSortingRepository depending on whether you need sorting and paging or not.

What is the difference between findById and getById?

As a consequence, findById() returns the actual object and getById returns a reference of the entity.

What is difference between PagingAndSortingRepository and JpaRepository?

PagingAndSortingRepository provides methods to do pagination and sort records. JpaRepository provides JPA related methods such as flushing the persistence context and delete records in a batch.

Is JpaRepository deprecated?

Method Summary Deletes the given entities in a batch which means it will create a single query. Deprecated.


1 Answers

First of all, I know we're raising the bar here quite a bit but this is already tremendously less code than you had to write without the help of Spring Data JPA.

Second, I think you don't need the service class in the first place, if all you do is forward a call to the repository. We recommend using services in front of the repositories if you have business logic that needs orchestration of different repositories within a transaction or has other business logic to encapsulate.

Generally speaking, you can of course do something like this:

interface ProductRepository<T extends Product> extends CrudRepository<T, Long> {      @Query("select p from #{#entityName} p where ?1 member of p.categories")     Iterable<T> findByCategory(String category);      Iterable<T> findByName(String name); } 

This will allow you to use the repository on the client side like this:

class MyClient {    @Autowired   public MyClient(ProductRepository<Car> carRepository,                    ProductRepository<Wine> wineRepository) { … } } 

and it will work as expected. However there are a few things to notice:

This only works if the domain classes use single table inheritance. The only information about the domain class we can get at bootstrap time is that it will be Product objects. So for methods like findAll() and even findByName(…) the relevant queries will start with select p from Product p where…. This is due to the fact that the reflection lookup will never ever be able to produce Wine or Car unless you create a dedicated repository interface for it to capture the concrete type information.

Generally speaking, we recommend creating repository interfaces per aggregate root. This means you don't have a repo for every domain class per se. Even more important, a 1:1 abstraction of a service over a repository is completely missing the point as well. If you build services, you don't build one for every repository (a monkey could do that, and we're no monkeys, are we? ;). A service is exposing a higher level API, is much more use-case drive and usually orchestrates calls to multiple repositories.

Also, if you build services on top of repositories, you usually want to enforce the clients to use the service instead of the repository (a classical example here is that a service for user management also triggers password generation and encryption, so that by no means it would be a good idea to let developers use the repository directly as they'd effectively work around the encryption). So you usually want to be selective about who can persist which domain objects to not create dependencies all over the place.

Summary

Yes, you can build generic repositories and use them with multiple domain types but there are quite strict technical limitations. Still, from an architectural point of view, the scenario you describe above shouldn't even pop up as this means you're facing a design smell anyway.

like image 125
Oliver Drotbohm Avatar answered Oct 05 '22 23:10

Oliver Drotbohm