覆盖 QuerydslPredicateExecutor 的 findAll() 方法

overwrite findAll() method of QuerydslPredicateExecutor

我的目标是将动态 Predicate 添加到 QuerydslPredicateExecutorfindAll 方法中。这应该用于根据当前活动用户的组织过滤实体。

我正在使用 Spring 数据和 Spring 数据 REST 来获得开箱即用的 REST API,即我没有可以拦截的专用 REST 服务输入的数据并修改它。

通过扩展 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;
    }

}

但是显然这个扩展没有提供覆盖的方法。我调试了实现 QuerydslJpaPredicateExecutor 的连接方式,但这相当复杂,我看不出有什么办法可以轻松地在此处插入一些东西。

另一个想法是使用过滤器拦截 URL 调用并添加参数,但这听起来不太好。 我也可以用 @BasePathAwareController 覆盖 finder 的控制器路径,但这意味着要对我拥有的所有实体执行此操作,而不是在一个地方执行此操作。

有什么想法可以实现我的目标吗?也许还有完全不同的选项可以实现我向 Querydsl 添加额外过滤的目标 Predicate

同时我找到了一个方法。它需要提供自己的 QuerydslPredicateExecutor 实现。但这在 Spring 数据中并不容易。答案是由 提出的,但同时构造函数已更改为较新的 Spring 数据,为什么不能采用 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 包装器 class 而不是默认包装器。默认一个作为参数传递并包装。

/**
 * 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 引导应用程序中注册,以使 Spring 知道从哪里获取存储库实现,例如与:

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

这里是 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);
    }
}