Replacing deprecated QuerydslJpaRepository with QuerydslJpaPredicateExecutor fails

后端 未结 3 1928
感情败类
感情败类 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:23

    In Spring Data JPA 2.1.6 the constructor of QuerydslJpaPredicateExecutor has changed. I present here an alternative approach using a wrapper to https://stackoverflow.com/a/53960209/3351474. This makes the solution independent from the internals of Spring Data JPA. Three classes have to be implemented.

    As an example I take here a customized Querydsl implementation using always the creationDate of an entity as sort criteria if nothing is passed. I assume in this example that this column exists in some @MappedSuperClass for all entities. Use generated static metadata in real life instead the hard coded string "creationDate".

    As first the wrapped delegating all CustomQuerydslJpaRepositoryIml delegating all methods to the QuerydslJpaPredicateExecutor:

    /**
     * Customized Querydsl JPA repository to apply custom filtering and sorting logic.
     *
     */
    public class CustomQuerydslJpaRepositoryIml implements QuerydslPredicateExecutor {
    
        private final QuerydslJpaPredicateExecutor querydslPredicateExecutor;
    
        public CustomQuerydslJpaRepositoryIml(QuerydslJpaPredicateExecutor querydslPredicateExecutor) {
            this.querydslPredicateExecutor = querydslPredicateExecutor;
        }
    
        private Sort applyDefaultOrder(Sort sort) {
            if (sort.isUnsorted()) {
                return Sort.by("creationDate").ascending();
            }
            return sort;
        }
    
        private Pageable applyDefaultOrder(Pageable pageable) {
            if (pageable.getSort().isUnsorted()) {
                Sort defaultSort = Sort.by(AuditableEntity_.CREATION_DATE).ascending();
                pageable = PageRequest.of(pageable.getPageNumber(), pageable.getPageSize(), defaultSort);
            }
            return pageable;
        }
    
        @Override
        public Optional findOne(Predicate predicate) {
            return querydslPredicateExecutor.findOne(predicate);
        }
    
        @Override
        public List findAll(Predicate predicate) {
            return querydslPredicateExecutor.findAll(predicate);
        }
    
        @Override
        public List findAll(Predicate predicate, Sort sort) {
            return querydslPredicateExecutor.findAll(predicate, applyDefaultOrder(sort));
        }
    
        @Override
        public List findAll(Predicate predicate, OrderSpecifier... orders) {
            return querydslPredicateExecutor.findAll(predicate, orders);
        }
    
        @Override
        public List findAll(OrderSpecifier... orders) {
            return querydslPredicateExecutor.findAll(orders);
        }
    
        @Override
        public Page findAll(Predicate predicate, Pageable pageable) {
            return querydslPredicateExecutor.findAll(predicate, applyDefaultOrder(pageable));
        }
    
        @Override
        public long count(Predicate predicate) {
            return querydslPredicateExecutor.count(predicate);
        }
    
        @Override
        public boolean exists(Predicate predicate) {
            return querydslPredicateExecutor.exists(predicate);
        }
    }
    

    Next the CustomJpaRepositoryFactory doing the magic and providing the Querydsl wrapper class instead of the default one. The default one is passed as parameter and wrapped.

    /**
     * Custom JpaRepositoryFactory allowing to support a custom QuerydslJpaRepository.
     *
     */
    public class CustomJpaRepositoryFactory extends JpaRepositoryFactory {
    
        /**
         * Creates a new {@link JpaRepositoryFactory}.
         *
         * @param entityManager must not be {@literal null}
         */
        public CustomJpaRepositoryFactory(EntityManager entityManager) {
            super(entityManager);
        }
    
        @Override
        protected RepositoryComposition.RepositoryFragments getRepositoryFragments(RepositoryMetadata metadata) {
            final RepositoryComposition.RepositoryFragments[] modifiedFragments = {RepositoryComposition.RepositoryFragments.empty()};
            RepositoryComposition.RepositoryFragments fragments = super.getRepositoryFragments(metadata);
            // because QuerydslJpaPredicateExecutor is using som internal classes only a wrapper can be used.
            fragments.stream().forEach(
                    f -> {
                        if (f.getImplementation().isPresent() &&
                                QuerydslJpaPredicateExecutor.class.isAssignableFrom(f.getImplementation().get().getClass())) {
                            modifiedFragments[0] = modifiedFragments[0].append(RepositoryFragment.implemented(
                                    new CustomQuerydslJpaRepositoryIml((QuerydslJpaPredicateExecutor) f.getImplementation().get())));
                        } else {
                            modifiedFragments[0].append(f);
                        }
                    }
            );
            return modifiedFragments[0];
        }
    }
    

    Finally the CustomJpaRepositoryFactoryBean. This must be registered with the Spring Boot application, to make Spring aware where to get the repository implementations from, e.g. with:

    @SpringBootApplication
    @EnableJpaRepositories(basePackages = "your.package",
            repositoryFactoryBeanClass = CustomJpaRepositoryFactoryBean.class)
    ...
    

    Here now the class:

    public class CustomJpaRepositoryFactoryBean, S, I> extends JpaRepositoryFactoryBean {
    
        /**
         * Creates a new {@link JpaRepositoryFactoryBean} for the given repository interface.
         *
         * @param repositoryInterface must not be {@literal null}.
         */
        public CustomJpaRepositoryFactoryBean(Class repositoryInterface) {
            super(repositoryInterface);
        }
    
        protected RepositoryFactorySupport createRepositoryFactory(EntityManager entityManager) {
            return new CustomJpaRepositoryFactory(entityManager);
        }
    }
    

提交回复
热议问题