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