spring-data-jpa - 覆盖 QuerydslPredicateExecutor 的 findAll() 方法

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

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

我将 Spring Data 与 Spring Data REST 一起使用来获取开箱即用的 REST API,即我没有专用的 REST 服务来拦截传入数据并对其进行修改。

通过扩展 SimpleJpaRepository 并将其注册到 @EnableJpaRepositories,可以覆盖方法并更改其默认行为。我想这样做,但我的 Repository 接口(interface)正在实现 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 覆盖查找器的 Controller 路径,但这意味着要对我拥有的所有实体执行此操作,而不是在单个位置执行此操作。

有什么想法可以实现我的目标吗?也许还有完全不同的选项可以实现我的目标,即向 Querydsl Predicate

添加额外的过滤

最佳答案

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

我在问题中使用了不同的示例,但通过此解决方案,您还可以完全自由地添加和附加任何谓词。作为示例,我在这里采用一个自定义的 Querydsl 实现,如果没有传递任何内容,则始终使用实体的创建日期作为排序标准。我假设在此示例中该列存在于所有实体的某些 @MappedSuperClass 中。使用现实生活中生成的静态元数据而不是硬编码字符串“creationDate”。

首先,包装委托(delegate)所有 CustomQuerydslJpaRepositoryIml,将所有方法委托(delegate)给 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);
    }
}

关于spring-data-jpa - 覆盖 QuerydslPredicateExecutor 的 findAll() 方法,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/55818816/

相关文章:

带有 Hibernate 的 Spring Boot - 从另一个项目 Autowiring 存储库

mysql - Spring Data JPQL 如何使用动态where子句创建查询?

java - 在 Spring Data 上选择字段

java - 如何使用条件注释覆盖 spring 中的配置(例如)

Spring Data REST 分页

java - JdbcSQLIntegrityConstraintViolationException : NULL not allowed for column

spring - 无法使用 spring jpa 获取更新的实体并且唯一约束不会停止更新

java - Spring 数据休息2.6.10 : Find by embedded attributes value via Rest api

java - 如何使用 Spring RestTemplate 使用 Page<Entity> 响应

java - Hibernate DefaultEntityAliases 引发 NullPointerException