Search code examples
springspring-dataspring-data-jpaquerydsl

Best practise when using Querydsl with Spring Data


Using Spring Data nad Querydsl we can just declare repository interface and skip the implementation class. Some methods with a specific name or using @Query annotation and that's all.

But sometimes I'd like to use JPAQuery and define method's body by myself, let's say

@Repository
public class MyRepositoryImpl implements MyRepository {

    @PersistenceContext
    private EntityManager em;

    @Override
    public List<Tuple> someMethod(String arg) {
        JPAQuery query = new JPAQuery(em);
        ...
    }

but this way I would have to implement other MyRepository interface methods, which ruins all Spring Data's advantages!

I can see two options:

  • Declare another interface per each repository and then normally implement it (which doubles number of interfaces)
  • Inject EntityManager into @Service class and implement my custom methods there

I like option #2 more, but as far I as know, in @Service class we should only call repository methods, so it's not a perfect solution as well.

So how does programmers deal with it?


Solution

  • You should not implement the actual Spring Data repository, instead you have to declare another custom interface where you can put your custom methods.

    Let's say you have a MyRepository, defined as

    @Repository
    public interface MyRepository extends JpaRepository<Tuple, Long> {}
    

    Now you want to add your custom findTuplesByMyArg(), for a sake of purpose you need to create custom repository interface

    public interface MyRepositoryCustom {
       List<Tuple> findTuplesByMyArg(String myArg);
    }
    

    Afterwards comes the implementation of custom interface

    public class MyRepositoryCustomImpl implements MyRepositoryCustom {
        @PersistenceContext
        private EntityManager em;
    
        @Override
        public List<Tuple> findTuplesByMyArg(String myArg) {
            JPAQuery query = new JPAQuery(em);
            ...
        }    
    }
    

    And we need to change MyRepository declaration, so it extends custom repository, so that

    @Repository
    public interface MyRepository extends JpaRepository<Tuple, Long>, MyRepositoryCustom {}
    

    And you can easily access your findTuplesByMyArg() by injecting MyRepository, e.g.

    @Service
    public class MyService {
       @Autowired
       private MyRepository myRepository;
    
       public List<Tuple> retrieveTuples(String myArg) { 
          return myRepository.findTuplesByMyArg(myArg);
       }
    }
    

    Pay attention that names are important here (you need to have Impl postfix by default configs in repo implementation).

    You can find all needed information here