覆盖QuerydslPredicateExecutor的findAll()方法

时间:2019-04-23 20:18:35

标签: spring-data-jpa spring-data-rest

我的目标是向Predicate的{​​{1}}方法中添加动态findAll。应该使用它来基于当前活动用户的组织来过滤实体。

我正在将Spring Data与Spring Data REST结合使用以立即使用REST API,即我没有专用的REST服务,可以在其中截获传入的数据并对其进行修改。

通过扩展QuerydslPredicateExecutor并向SimpleJpaRepository注册,可以覆盖方法并更改其默认行为。我想这样做,但是我的@EnableJpaRepositories接口正在实现Repository,这似乎不起作用。

我失败的方法开始于:

QuerydslPredicateExecutor

,但显然,此扩展名不提供要覆盖的方法。我调试了实现public class CustomizedJpaRepositoryIml<T, ID extends Serializable> extends SimpleJpaRepository<T, ID> { private EntityManager entityManager; @Autowired public CustomizedJpaRepositoryIml(JpaEntityInformation<T, ?> entityInformation, EntityManager entityManager) { super(entityInformation, entityManager); this.entityManager = entityManager; } } 的连接方式,但这相当复杂,我看不出在这里轻松插入某些内容的方法。

另一个想法是使用过滤器拦截URL调用并添加参数,但这听起来不太好。 我还可以使用QuerydslJpaPredicateExecutor覆盖查找程序的控制器路径,但这将意味着对所有我拥有的实体(而不是在单个位置)执行此操作。

有什么想法可以实现我的目标?也许还有完全不同的选项可以实现我为Querydsl @BasePathAwareController

添加附加过滤的目标

1 个答案:

答案 0 :(得分:0)

与此同时,我找到了一种方法。它需要提供自己的QuerydslPredicateExecutor实现。但这在Spring Data中并不容易。答案是由https://stackoverflow.com/a/53960209/3351474推动的,但是与此同时,在较新的Spring Data中,构造函数发生了变化,为什么不能将其设为1:1。

我使用与问题中不同的示例,但是使用此解决方案,您还有完全的自由来添加和附加任何Predicate。作为示例,我在此处采用自定义的Querydsl实现,如果不进行任何传递,则始终使用实体的creationDate作为排序标准。在此示例中,我假设所有实体的此列都存在于@MappedSuperClass中。在现实生活中使用生成的静态元数据代替硬编码字符串“ creationDate”。

首先,将所有CustomQuerydslJpaRepositoryIml委派给所有QuerydslJpaPredicateExecutor的包装委派:

/**
 * Customized Querydsl JPA repository to apply custom filtering and sorting logic.
 *
 */
public class CustomQuerydslJpaRepositoryIml<T> implements QuerydslPredicateExecutor<T> {

    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<T> findOne(Predicate predicate) {
        return querydslPredicateExecutor.findOne(predicate);
    }

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

    @Override
    public List<T> findAll(Predicate predicate, Sort sort) {
        return querydslPredicateExecutor.findAll(predicate, applyDefaultOrder(sort));
    }

    @Override
    public List<T> findAll(Predicate predicate, OrderSpecifier<?>... orders) {
        return querydslPredicateExecutor.findAll(predicate, orders);
    }

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

    @Override
    public Page<T> 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);
    }
}

接下来,CustomJpaRepositoryFactory发挥作用,并提供Querydsl包装器类,而不是默认的包装器类。默认值作为参数传递并包装。

/**
 * 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];
    }
}

最后是CustomJpaRepositoryFactoryBean。这必须在Spring Boot应用程序中注册,以使Spring知道从何处获取存储库实现。与:

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

现在是课程:

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

    /**
     * Creates a new {@link JpaRepositoryFactoryBean} for the given repository interface.
     *
     * @param repositoryInterface must not be {@literal null}.
     */
    public CustomJpaRepositoryFactoryBean(Class<? extends T> repositoryInterface) {
        super(repositoryInterface);
    }

    protected RepositoryFactorySupport createRepositoryFactory(EntityManager entityManager) {
        return new CustomJpaRepositoryFactory(entityManager);
    }
}