我需要一些启用了QueryDSL的自定义查询方法,并遵循此SO答案。
效果很好,但是升级到Spring Boot 2.1(升级了Spring Data)之后,我发现它QuerydslJpaRepository已被弃用。简单地将其替换为QuerydslJpaPredicateExecutor-文档会告诉我使用- 会导致错误:
QuerydslJpaRepository
QuerydslJpaPredicateExecutor
引起原因:java.lang.IllegalArgumentException:类[… ProjectingQueryDslJpaRepositoryImpl]的对象必须是接口org.springframework.data.jpa.repository.support.JpaRepositoryImplementation的实例。
…但是实施JpaRepositoryImplementation将意味着我必须实施所有标准的CRUD方法,这显然是我所不希望的。因此,如果我repositoryBaseClass从中删除config @EnableJpaRepositories,以将其视为带有实现的存储库片段,即使它标有@NoRepositoryBean,它也会尝试实例化该片段,从而给我错误:
JpaRepositoryImplementation
repositoryBaseClass
@EnableJpaRepositories
@NoRepositoryBean
原因:java.lang.IllegalArgumentException:无法创建方法公共抽象java.util.Optional ProjectingQueryDslJpaRepository.findOneProjectedBy(com.querydsl.core.types.Expression,com.querydsl.core.types.Predicate)的查询!提供至少1个参数,但查询中仅提供0个参数。 … 由以下原因引起:java.lang.IllegalArgumentException:提供了至少1个参数,但查询中仅存在0个参数。
原因:java.lang.IllegalArgumentException:无法创建方法公共抽象java.util.Optional ProjectingQueryDslJpaRepository.findOneProjectedBy(com.querydsl.core.types.Expression,com.querydsl.core.types.Predicate)的查询!提供至少1个参数,但查询中仅提供0个参数。
…
由以下原因引起:java.lang.IllegalArgumentException:提供了至少1个参数,但查询中仅存在0个参数。
原始版本:
@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(RepositoryMetadatametadata)。在这种方法中,您可以为任何自定义存储库提供基本(或更具体的片段)实现,每个扩展存储库都应采用这些实现。
JpaRepositoryFactory
getRepositoryFragments(RepositoryMetadatametadata)
让我给你看一个例子:
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); }
以下界面结合了不同的存储库。
资料储存库:
@NoRepositoryBean public interface DataRepository<T> extends CrudRepository<T, String>, QueryableReadRepository<T> { }
现在,您特定的域存储库可以从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的默认实现。该示例提供了新的存储库,但您也可以覆盖QuerydslPredicateExecutorin的默认实现CustomRepositoryFactory
QuerydslPredicateExecutor
CustomRepositoryFactory