Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Spring Data JPA: Implementing Custom Repository Behavior with Specifications

I would like to create a Spring Data JPA repository with custom behavior, and implement that custom behavior using Specifications. I have gone through the Spring Data JPA documentation for implementing custom behavior in a single repository to set this up, except there is no example of using a Spring Data Specification from within a custom repository. How would one do this, if even possible?

I do not see a way to inject something into the custom implementation that takes a specification. I thought I would be tricky and inject the CRUD repository portion of the repository into the custom portion, but that results in a circular instantiation dependency.

I am not using QueryDSL. Thanks.

like image 472
SingleShot Avatar asked Jan 17 '15 01:01

SingleShot


2 Answers

I guess the primary source for inspiration could be how SimpleJpaRepository handles specifications. The key spots to have a look at are:

  • SimpleJpaRepository.getQuery(…) - it's basically creating a CriteriaQuery and bootstraps a select using a JPA Root. Whether the latter applies to your use case is already up to you. I think the former will apply definitely.
  • SimpleJpaRepository.applySpecificationToCriteria(…) - it basically uses the artifacts produced in getQuery(…) (i.e. the Root and the CriteriaQuery) and applies the given Specification to exactly these artifacts.
like image 61
Oliver Drotbohm Avatar answered Oct 20 '22 08:10

Oliver Drotbohm


this is not using Specification, so not sure if it's relevant to you, but one way that I was able to inject custom behavior is as follows,

  1. Basic structure: as follows

    i. create a generic interface for the set of entity classes which are modeled after a generic parent entity. Note, this is optional. In my case I had a need for this hierarchy, but it's not necessary

    public interface GenericRepository<T> {
    
    // add any common methods to your entity hierarchy objects, 
    // so that you don't have to repeat them in each of the children entities
    // since you will be extending from this interface
    }
    

    ii. Extend a specific repository from generic (step 1) and JPARepository as

    public interface MySpecificEntityRepository extends GenericRepository<MySpecificEntity>, JpaRepository<MySpecificEntity, Long> {
    
    // add all methods based on column names, entity graphs or JPQL that you would like to 
    // have here in addition to what's offered by JpaRepository
    }
    

    iii. Use the above repository in your service implementation class

    1. Now, the Service class may look like this,

      public interface GenericService<T extends GenericEntity, ID extends Serializable> {
         // add specific methods you want to extend to user
      }
      
    2. The generic implementation class can be as follows,

      public abstract class GenericServiceImpl<T extends GenericEntity, J extends JpaRepository<T, Long> & GenericRepository<T>> implements GenericService<T, Long> {
      
      // constructor takes in specific repository
          public GenericServiceImpl(J genericRepository) {
             // save this to local var
          } 
        // using the above repository, specific methods are programmed
      
      }
      
    3. specific implementation class can be

      public class MySpecificEntityServiceImpl extends GenericServiceImpl<MySpecificEntity, MySpecificEntityRepository> implements MySpecificEntityService {
      
          // the specific repository is autowired
          @Autowired
          public MySpecificEntityServiceImpl(MySpecificEntityRepository genericRepository) {
               super(genericRepository);
               this.genericRepository = (MySpecificEntityRepository) genericRepository;
           }
       }
      
like image 25
ameet chaubal Avatar answered Oct 20 '22 08:10

ameet chaubal