переписать метод findAll () объекта QuerydslPredicateExecutor - PullRequest
0 голосов
/ 23 апреля 2019

Моя цель - добавить динамический Predicate к findAll методу QuerydslPredicateExecutor. Это следует использовать для фильтрации объектов на основе организации текущего активного пользователя.

Я использую Spring Data вместе с Spring Data 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, но это означало бы сделать это для всех сущностей, которые у меня есть, а не в одном месте.

Есть идеи для достижения моей цели? может быть, есть и другие варианты, позволяющие достичь цели по добавлению дополнительной фильтрации в Querydsl Predicate

1 Ответ

0 голосов
/ 18 июля 2019

Тем временем я нашел способ. Требуется предоставить собственную реализацию 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);
    }
}
...