I needed some custom QueryDSL enabled query methods and followed this SO answer.
That worked great, but after upgrading to Spring Boot 2.1 (which upgrades Spring Dat
With Spring Boot 2.1.1 the following solution may help you. The key is to extend JpaRepositoryFactory and override the method getRepositoryFragments(RepositoryMetadata metadata). In this method you can provide base (or more specific fragment) implementations for any custom repository which should be taken for every extending repository.
Let me show you an example:
QueryableReadRepository:
@NoRepositoryBean
public interface QueryableReadRepository extends Repository {
List findAll(Predicate predicate);
List findAll(Sort sort);
List findAll(Predicate predicate, Sort sort);
List findAll(OrderSpecifier>... orders);
List findAll(Predicate predicate, OrderSpecifier>... orders);
Page findAll(Pageable page);
Page findAll(Predicate predicate, Pageable page);
Optional findOne(Predicate predicate);
boolean exists(Predicate predicate);
}
The following interface combines different repositories.
DataRepository:
@NoRepositoryBean
public interface DataRepository
extends CrudRepository, QueryableReadRepository {
}
Now your specific domain repos can extend from DataRepository:
@Repository
public interface UserRepository extends DataRepository {
}
QueryableReadRepositoryImpl:
@Transactional
public class QueryableReadRepositoryImpl extends QuerydslJpaPredicateExecutor
implements QueryableReadRepository {
private static final EntityPathResolver resolver = SimpleEntityPathResolver.INSTANCE;
private final EntityPath path;
private final PathBuilder builder;
private final Querydsl querydsl;
public QueryableReadRepositoryImpl(JpaEntityInformation entityInformation,
EntityManager entityManager) {
super(entityInformation, entityManager, resolver, null);
this.path = resolver.createPath(entityInformation.getJavaType());
this.builder = new PathBuilder(path.getType(), path.getMetadata());
this.querydsl = new Querydsl(entityManager, builder);
}
@Override
public Optional findOne(Predicate predicate) {
return super.findOne(predicate);
}
@Override
public List findAll(OrderSpecifier>... orders) {
return super.findAll(orders);
}
@Override
public List findAll(Predicate predicate, Sort sort) {
return executeSorted(createQuery(predicate).select(path), sort);
}
@Override
public Page findAll(Predicate predicate, Pageable pageable) {
return super.findAll(predicate, pageable);
}
@Override
public List findAll(Predicate predicate) {
return super.findAll(predicate);
}
public List findAll(Sort sort) {
return executeSorted(createQuery().select(path), sort);
}
@Override
public Page findAll(Pageable pageable) {
final JPQLQuery> countQuery = createCountQuery();
JPQLQuery query = querydsl.applyPagination(pageable, createQuery().select(path));
return PageableExecutionUtils.getPage(
query.distinct().fetch(),
pageable,
countQuery::fetchCount);
}
private List executeSorted(JPQLQuery query, Sort sort) {
return querydsl.applySorting(sort, query).distinct().fetch();
}
}
CustomRepositoryFactoryBean:
public class CustomRepositoryFactoryBean, S, I>
extends JpaRepositoryFactoryBean {
public CustomRepositoryFactoryBean(Class extends T> repositoryInterface) {
super(repositoryInterface);
}
protected RepositoryFactorySupport createRepositoryFactory(EntityManager entityManager) {
return new CustomRepositoryFactory(entityManager);
}
CustomRepositoryFactory:
public class CustomRepositoryFactory extends JpaRepositoryFactory {
private final EntityManager entityManager;
public CustomRepositoryFactory(EntityManager entityManager) {
super(entityManager);
this.entityManager = entityManager;
}
@Override
protected RepositoryFragments getRepositoryFragments(RepositoryMetadata metadata) {
RepositoryFragments fragments = super.getRepositoryFragments(metadata);
if (QueryableReadRepository.class.isAssignableFrom(
metadata.getRepositoryInterface())) {
JpaEntityInformation, Serializable> entityInformation =
getEntityInformation(metadata.getDomainType());
Object queryableFragment = getTargetRepositoryViaReflection(
QueryableReadRepositoryImpl.class, entityInformation, entityManager);
fragments = fragments.append(RepositoryFragment.implemented(queryableFragment));
}
return fragments;
}
Main class:
@EnableJpaRepositories(repositoryFactoryBeanClass = CustomRepositoryFactoryBean.class)
public class App {
}
This has the advantage that you provide only one (fragment) implementation for a custom repo. The base repository implementation is still Spring's default implementation. The example provided a new repo but you can probably also just override the default implementation of QuerydslPredicateExecutor in CustomRepositoryFactory