用QuerydslJpaPredicateExecutor替换不推荐使用的QuerydslJpaRepository失败 [英] Replacing deprecated QuerydslJpaRepository with QuerydslJpaPredicateExecutor fails

查看:43
本文介绍了用QuerydslJpaPredicateExecutor替换不推荐使用的QuerydslJpaRepository失败的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我需要一些启用QueryDSL的自定义查询方法,并遵循

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

效果很好,但是在升级到Spring Boot 2.1(升级了Spring Data)之后,我发现 QuerydslJpaRepository 已被弃用.只需将其替换为 QuerydslJpaPredicateExecutor -

That worked great, but after upgrading to Spring Boot 2.1 (which upgrades Spring Data), I've found that QuerydslJpaRepository has been deprecated. Simply replacing it with QuerydslJpaPredicateExecutor - which the documentation tells me to use - leads to an error:

由于:java.lang.IllegalArgumentException:类的对象[... ProjectingQueryDslJpaRepositoryImpl]必须是界面org.springframework.data.jpa.repository.support.JpaRepositoryImplementation

Caused by: java.lang.IllegalArgumentException: Object of class [...ProjectingQueryDslJpaRepositoryImpl] must be an instance of interface org.springframework.data.jpa.repository.support.JpaRepositoryImplementation

...但是实现 JpaRepositoryImplementation 意味着我必须实现所有标准的CRUD方法,这显然是我所不希望的.因此,如果我从 @EnableJpaRepositories 中删除 repositoryBaseClass 配置,则将其与实现中的存储库片段一样对待,即使标记为,它也会尝试实例化该片段. @NoRepositoryBean ,给我错误:

...but implementing JpaRepositoryImplementation would mean that I have to implement all the standard CRUD methods, which I obviously don't want. So if I remove the repositoryBaseClass config from @EnableJpaRepositories, to treat this just like a repository fragment with implementation, it will try to instantiate the fragment, even though it is marked with @NoRepositoryBean, giving me the error:

原因:java.lang.IllegalArgumentException:无法创建查询用于方法public abstract java.util.OptionalProjectingQueryDslJpaRepository.findOneProjectedBy(com.querydsl.core.types.Expression,com.querydsl.core.types.Predicate)!提供了至少1个参数,但其中仅存在0个参数查询.

Caused by: java.lang.IllegalArgumentException: Failed to create query for method public abstract java.util.Optional ProjectingQueryDslJpaRepository.findOneProjectedBy(com.querydsl.core.types.Expression,com.querydsl.core.types.Predicate)! At least 1 parameter(s) provided but only 0 parameter(s) present in query.

...

由以下原因引起:java.lang.IllegalArgumentException:至少1个参数提供,但查询中仅提供0个参数.

Caused by: java.lang.IllegalArgumentException: At least 1 parameter(s) provided but only 0 parameter(s) present in query.

来源的简要版本:

@Configuration
@EnableJpaRepositories(basePackageClasses = Application.class, repositoryBaseClass = ProjectingQueryDslJpaRepositoryImpl.class)
@EnableTransactionManagement
@EnableJpaAuditing
@RequiredArgsConstructor(onConstructor = @__({@Autowired}))
public class DatabaseConfig {}

_

@NoRepositoryBean
public interface ProjectingQueryDslJpaRepository<T> extends QuerydslBinderCustomizer<EntityPath<T>>, QuerydslPredicateExecutor<T> {

    @NonNull
    <P> Page<P> findPageProjectedBy(@NonNull Expression<P> factoryExpression, Predicate predicate, 
            @NonNull Pageable pageable);

    @NonNull
    <P> Optional<P> findOneProjectedBy(@NonNull Expression<P> factoryExpression, @NonNull Predicate predicate);

    @Override
    default void customize(@NonNull QuerydslBindings bindings, @NonNull EntityPath<T> root){
        bindings.bind(String.class).first((SingleValueBinding<StringPath, String>) StringExpression::containsIgnoreCase);
    }
}

_

public class ProjectingQueryDslJpaRepositoryImpl<T, ID extends Serializable> extends QuerydslJpaRepository<T, ID>
implements ProjectingQueryDslJpaRepository<T> {

    private static final EntityPathResolver DEFAULT_ENTITY_PATH_RESOLVER = SimpleEntityPathResolver.INSTANCE;

    private final EntityPath<T> path;
    private final Querydsl querydsl;

    public ProjectingQueryDslJpaRepositoryImpl(@NonNull JpaEntityInformation<T, ID> entityInformation, @NonNull EntityManager entityManager) {
        this(entityInformation, entityManager, DEFAULT_ENTITY_PATH_RESOLVER);
    }

    public ProjectingQueryDslJpaRepositoryImpl(@NonNull JpaEntityInformation<T, ID> entityInformation, @NonNull EntityManager entityManager,
                                           @NonNull EntityPathResolver resolver) {
        super(entityInformation, entityManager, resolver);

        this.path = resolver.createPath(entityInformation.getJavaType());
        PathBuilder<T> builder = new PathBuilder<>(path.getType(), path.getMetadata());
        this.querydsl = new Querydsl(entityManager, builder);
    }

    @Override
    public <P> Page<P> findPageProjectedBy(@NonNull Expression<P> factoryExpression, Predicate predicate, 
        @NonNull Pageable pageable) {

        final JPQLQuery<?> countQuery = createCountQuery(predicate);
        JPQLQuery<P> query = querydsl.applyPagination(pageable, createQuery(predicate).select(factoryExpression));

        return PageableExecutionUtils.getPage(query.fetch(), pageable, countQuery::fetchCount);
    }

    @Override
    public <P> Optional<P> findOneProjectedBy(@NonNull Expression<P> factoryExpression, @NonNull Predicate predicate) {
        try {
            return Optional.ofNullable(createQuery(predicate).select(factoryExpression).from(path).fetchOne());
        } catch (NonUniqueResultException ex) {
            throw new IncorrectResultSizeDataAccessException(ex.getMessage(), 1, ex);
        }
    }
}

推荐答案

对于 Spring Boot 2.1.1 ,以下解决方案可能会对您有所帮助.关键是扩展 JpaRepositoryFactory 并覆盖方法 getRepositoryFragments(RepositoryMetadata元数据).在这种方法中,您可以为任何自定义存储库提供基础(或更具体的片段)实现,每个扩展存储库都应采用这些实现.

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.

让我给你看一个例子:

QueryableReadRepository:

@NoRepositoryBean
public interface QueryableReadRepository<T> extends Repository<T, String> {

  List<T> findAll(Predicate predicate);

  List<T> findAll(Sort sort);

  List<T> findAll(Predicate predicate, Sort sort);

  List<T> findAll(OrderSpecifier<?>... orders);

  List<T> findAll(Predicate predicate, OrderSpecifier<?>... orders);

  Page<T> findAll(Pageable page);

  Page<T> findAll(Predicate predicate, Pageable page);

  Optional<T> findOne(Predicate predicate);

  boolean exists(Predicate predicate);
}

以下界面结合了不同的存储库.

The following interface combines different repositories.

数据存储库:

@NoRepositoryBean
public interface DataRepository<T>
    extends CrudRepository<T, String>, QueryableReadRepository<T> {
}

现在,您特定的域存储库可以从DataRepository扩展:

Now your specific domain repos can extend from DataRepository:

@Repository
public interface UserRepository extends DataRepository<UserEntity> {

}

QueryableReadRepositoryImpl:

@Transactional
public class QueryableReadRepositoryImpl<T> extends QuerydslJpaPredicateExecutor<T>
    implements QueryableReadRepository<T> {

  private static final EntityPathResolver resolver = SimpleEntityPathResolver.INSTANCE;

  private final EntityPath<T> path;
  private final PathBuilder<T> builder;
  private final Querydsl querydsl;

  public QueryableReadRepositoryImpl(JpaEntityInformation<T, ?> entityInformation,
      EntityManager entityManager) {
    super(entityInformation, entityManager, resolver, null);
    this.path = resolver.createPath(entityInformation.getJavaType());
    this.builder = new PathBuilder<T>(path.getType(), path.getMetadata());
    this.querydsl = new Querydsl(entityManager, builder);
  }

  @Override
  public Optional<T> findOne(Predicate predicate) {
    return super.findOne(predicate);
  }

  @Override
  public List<T> findAll(OrderSpecifier<?>... orders) {
    return super.findAll(orders);
  }

  @Override
  public List<T> findAll(Predicate predicate, Sort sort) {
    return executeSorted(createQuery(predicate).select(path), sort);
  }

  @Override
  public Page<T> findAll(Predicate predicate, Pageable pageable) {
    return super.findAll(predicate, pageable);
  }

  @Override
  public List<T> findAll(Predicate predicate) {
    return super.findAll(predicate);
  }

  public List<T> findAll(Sort sort) {
    return executeSorted(createQuery().select(path), sort);
  }

  @Override
  public Page<T> findAll(Pageable pageable) {
    final JPQLQuery<?> countQuery = createCountQuery();
    JPQLQuery<T> query = querydsl.applyPagination(pageable, createQuery().select(path));

    return PageableExecutionUtils.getPage(
        query.distinct().fetch(), 
        pageable,
        countQuery::fetchCount);
  }

  private List<T> executeSorted(JPQLQuery<T> query, Sort sort) {
    return querydsl.applySorting(sort, query).distinct().fetch();
  }
}

CustomRepositoryFactoryBean :

public class CustomRepositoryFactoryBean<T extends Repository<S, I>, S, I>
    extends JpaRepositoryFactoryBean<T, S, I> {

  public CustomRepositoryFactoryBean(Class<? extends T> 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<?, Serializable> entityInformation = 
          getEntityInformation(metadata.getDomainType());

      Object queryableFragment = getTargetRepositoryViaReflection(
          QueryableReadRepositoryImpl.class, entityInformation, entityManager);

      fragments = fragments.append(RepositoryFragment.implemented(queryableFragment));
    }

    return fragments;
  }

主类:

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

这样做的好处是,您只能为自定义存储库提供一个(片段)实现.基本存储库实现仍然是Spring的默认实现.该示例提供了一个新的存储库,但您可能还可以覆盖 CustomRepositoryFactory

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

这篇关于用QuerydslJpaPredicateExecutor替换不推荐使用的QuerydslJpaRepository失败的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆