Replacing deprecated QuerydslJpaRepository with QuerydslJpaPredicateExecutor fails

后端 未结 3 1926
感情败类
感情败类 2020-12-09 19:21

I needed some custom QueryDSL enabled query methods and followed this SO answer.

That worked great, but after upgrading to Spring Boot 2.1 (which upgrades Spring Dat

3条回答
  •  鱼传尺愫
    2020-12-09 20:00

    With Spring Boot 2.1.1 the following solution may help you. The key is to extend JpaRepositoryFactory and override the method getRepositoryFragments(RepositoryMetadata metadata). In this method you can provide base (or more specific fragment) implementations for any custom repository which should be taken for every extending repository.

    Let me show you an example:

    QueryableReadRepository:

    @NoRepositoryBean
    public interface QueryableReadRepository extends Repository {
    
      List findAll(Predicate predicate);
    
      List findAll(Sort sort);
    
      List findAll(Predicate predicate, Sort sort);
    
      List findAll(OrderSpecifier... orders);
    
      List findAll(Predicate predicate, OrderSpecifier... orders);
    
      Page findAll(Pageable page);
    
      Page findAll(Predicate predicate, Pageable page);
    
      Optional findOne(Predicate predicate);
    
      boolean exists(Predicate predicate);
    }
    

    The following interface combines different repositories.

    DataRepository:

    @NoRepositoryBean
    public interface DataRepository
        extends CrudRepository, QueryableReadRepository {
    }
    

    Now your specific domain repos can extend from DataRepository:

    @Repository
    public interface UserRepository extends DataRepository {
    
    }
    

    QueryableReadRepositoryImpl:

    @Transactional
    public class QueryableReadRepositoryImpl extends QuerydslJpaPredicateExecutor
        implements QueryableReadRepository {
    
      private static final EntityPathResolver resolver = SimpleEntityPathResolver.INSTANCE;
    
      private final EntityPath path;
      private final PathBuilder builder;
      private final Querydsl querydsl;
    
      public QueryableReadRepositoryImpl(JpaEntityInformation entityInformation,
          EntityManager entityManager) {
        super(entityInformation, entityManager, resolver, null);
        this.path = resolver.createPath(entityInformation.getJavaType());
        this.builder = new PathBuilder(path.getType(), path.getMetadata());
        this.querydsl = new Querydsl(entityManager, builder);
      }
    
      @Override
      public Optional findOne(Predicate predicate) {
        return super.findOne(predicate);
      }
    
      @Override
      public List findAll(OrderSpecifier... orders) {
        return super.findAll(orders);
      }
    
      @Override
      public List findAll(Predicate predicate, Sort sort) {
        return executeSorted(createQuery(predicate).select(path), sort);
      }
    
      @Override
      public Page findAll(Predicate predicate, Pageable pageable) {
        return super.findAll(predicate, pageable);
      }
    
      @Override
      public List findAll(Predicate predicate) {
        return super.findAll(predicate);
      }
    
      public List findAll(Sort sort) {
        return executeSorted(createQuery().select(path), sort);
      }
    
      @Override
      public Page findAll(Pageable pageable) {
        final JPQLQuery countQuery = createCountQuery();
        JPQLQuery query = querydsl.applyPagination(pageable, createQuery().select(path));
    
        return PageableExecutionUtils.getPage(
            query.distinct().fetch(), 
            pageable,
            countQuery::fetchCount);
      }
    
      private List executeSorted(JPQLQuery query, Sort sort) {
        return querydsl.applySorting(sort, query).distinct().fetch();
      }
    }
    

    CustomRepositoryFactoryBean:

    public class CustomRepositoryFactoryBean, S, I>
        extends JpaRepositoryFactoryBean {
    
      public CustomRepositoryFactoryBean(Class repositoryInterface) {
        super(repositoryInterface);
      }
    
      protected RepositoryFactorySupport createRepositoryFactory(EntityManager entityManager) {
        return new CustomRepositoryFactory(entityManager);
      }
    

    CustomRepositoryFactory:

    public class CustomRepositoryFactory extends JpaRepositoryFactory {
    
      private final EntityManager entityManager;
    
      public CustomRepositoryFactory(EntityManager entityManager) {
        super(entityManager);
        this.entityManager = entityManager;
      }
    
      @Override
      protected RepositoryFragments getRepositoryFragments(RepositoryMetadata metadata) {
        RepositoryFragments fragments = super.getRepositoryFragments(metadata);
    
        if (QueryableReadRepository.class.isAssignableFrom(
            metadata.getRepositoryInterface())) {
    
          JpaEntityInformation entityInformation = 
              getEntityInformation(metadata.getDomainType());
    
          Object queryableFragment = getTargetRepositoryViaReflection(
              QueryableReadRepositoryImpl.class, entityInformation, entityManager);
    
          fragments = fragments.append(RepositoryFragment.implemented(queryableFragment));
        }
    
        return fragments;
      }
    

    Main class:

    @EnableJpaRepositories(repositoryFactoryBeanClass = CustomRepositoryFactoryBean.class)
    public class App {
    }
    

    This has the advantage that you provide only one (fragment) implementation for a custom repo. The base repository implementation is still Spring's default implementation. The example provided a new repo but you can probably also just override the default implementation of QuerydslPredicateExecutor in CustomRepositoryFactory

提交回复
热议问题