Spring Data JPA и Querydsl для извлечения подмножества столбцов с помощью проекции bean / конструктора

У меня есть класс сущности, как показано ниже:

@Entity
public class UserDemo implements Serializable {

    @Id
    private Long id;

    private String username;

    private String createdBy;
    @Version
    private int version;

    /***
     *
     * Getters and setters
     */
}


Использование Spring Data JPA и Querydsl как мне получить страницу UserDemo с заполненными только свойствами id и username? Мне нужно использовать поиск по страницам. Короче хотелось бы добиться того же результата, что и

Page<UserDemo> findAll(Predicate predicate, Pageable pageable);

но с заполненным ограниченным полем UserDemo.


person Murali    schedule 18.08.2013    source источник


Ответы (7)


Похоже, реализация настраиваемого репозитория - это путь, пока что-то подобное не будет доступно в весенних данных.

Я прошел через http://static.springsource.org/spring-data/data-jpa/docs/current/reference/html/repositories.html#repositories.custom-implementations.

Вот моя реализация, которая работает. Однако было бы хорошо, если бы этот метод был доступен непосредственно в Spring-Data-JPA.

Шаг 1. Промежуточный интерфейс для общего поведения

public interface CustomQueryDslJpaRepository <T, ID extends Serializable>
        extends JpaRepository<T, ID>, QueryDslPredicateExecutor<T> {
    /**
     * Returns a {@link org.springframework.data.domain.Page} of entities matching the given {@link com.mysema.query.types.Predicate}.
     * This also uses provided projections ( can be JavaBean or constructor or anything supported by QueryDSL
     * @param constructorExpression this constructor expression will be used for transforming query results
     * @param predicate
     * @param pageable
     * @return
     */
    Page<T> findAll(FactoryExpression<T> factoryExpression, Predicate predicate, Pageable pageable);
}

Шаг 2. Внедрение промежуточного интерфейса

public class CustomQueryDslJpaRepositoryImpl<T, ID extends Serializable> extends QueryDslJpaRepository<T, ID>
        implements CustomQueryDslJpaRepository<T, ID> {

    //All instance variables are available in super, but they are private
    private static final EntityPathResolver DEFAULT_ENTITY_PATH_RESOLVER = SimpleEntityPathResolver.INSTANCE;

    private final EntityPath<T> path;
    private final PathBuilder<T> builder;
    private final Querydsl querydsl;

    public CustomQueryDslJpaRepositoryImpl(JpaEntityInformation<T, ID> entityInformation, EntityManager entityManager) {
        this(entityInformation, entityManager, DEFAULT_ENTITY_PATH_RESOLVER);
    }

    public CustomQueryDslJpaRepositoryImpl(JpaEntityInformation<T, ID> entityInformation, EntityManager entityManager,
                                 EntityPathResolver resolver) {

        super(entityInformation, entityManager);
        this.path = resolver.createPath(entityInformation.getJavaType());
        this.builder = new PathBuilder<T>(path.getType(), path.getMetadata());
        this.querydsl = new Querydsl(entityManager, builder);
    }

    @Override
    public Page<T> findAll(FactoryExpression<T> factoryExpression, Predicate predicate, Pageable pageable) {
        JPQLQuery countQuery = createQuery(predicate);
        JPQLQuery query = querydsl.applyPagination(pageable, createQuery(predicate));

        Long total = countQuery.count();
        List<T> content = total > pageable.getOffset() ? query.list(factoryExpression) : Collections.<T> emptyList();

        return new PageImpl<T>(content, pageable, total);
    }
}

Шаг 3. Создайте настраиваемую фабрику репозитория, чтобы заменить стандартную

public class CustomQueryDslJpaRepositoryFactoryBean<R extends JpaRepository<T, I>, T, I extends Serializable>
        extends JpaRepositoryFactoryBean<R, T, I> {

    protected RepositoryFactorySupport createRepositoryFactory(EntityManager entityManager) {

        return new CustomQueryDslJpaRepositoryFactory(entityManager);
    }
    private static class CustomQueryDslJpaRepositoryFactory<T, I extends Serializable> extends JpaRepositoryFactory {

        private EntityManager entityManager;

        public CustomQueryDslJpaRepositoryFactory(EntityManager entityManager) {
            super(entityManager);
            this.entityManager = entityManager;
        }

        protected Object getTargetRepository(RepositoryMetadata metadata) {
            return new CustomQueryDslJpaRepositoryImpl<>(getEntityInformation(metadata.getDomainType()), entityManager);
        }

        protected Class<?> getRepositoryBaseClass(RepositoryMetadata metadata) {
            return CustomQueryDslJpaRepository.class;
        }
    }
}

Шаг 4. Используйте фабрику настраиваемого репозитория

Использование аннотации

@EnableJpaRepositories(repositoryFactoryBeanClass=CustomQueryDslJpaRepositoryFactoryBean.class)

ИЛИ с использованием XML

<repositories base-package="com.acme.repository"  factory-class="com.acme.CustomQueryDslJpaRepositoryFactoryBean" />

Примечание. Не размещайте интерфейс и реализацию настраиваемого репозитория в том же каталоге, что и base-package. Если вы размещаете, то исключите их из сканирования, иначе Spring попытается создать для них beans.

Пример использования

public interface UserDemoRepository extends CustomQueryDslJpaRepository<UserDemo, Long>{
}

public class UserDemoService {
    @Inject 
    UserDemoRepository userDemoRepository;

    public Page<User> findAll(UserSearchCriteria userSearchCriteria, Pageable pageable) {
        QUserDemo user = QUserDemo.userDemo;
        return userDemoRepository.findAll(Projections.bean(UserDemo.class, user.id, user.username), UserPredicate.defaultUserSearch(userSearchCriteria), pageable);
    }

}
person Murali    schedule 19.08.2013
comment
Что такое FactoryExpression, это собственный класс или предопределенный. - person Satish Dhiman; 17.07.2014
comment
FactoryExpression - это базовый класс для проекций в Querydsl. querydsl.com/static/ querydsl / 3.2.0 / apidocs / com / mysema / query / - person Murali; 18.07.2014
comment
Я получил Caused by: org.springframework.data.mapping.PropertyReferenceException: No property find found for type <MY_TYPE_NAME>, когда попробовал это. Это почему? ‹MY_TYPE_NAME›, конечно, означает мой тип сущности. - person Sudarshan Shubakar; 21.10.2014
comment
Хорошо, я смог решить свою проблему после некоторых попыток. Я заметил, что вам нужно аннотировать пользовательский интерфейс с помощью @NoRepositoryBean, как указано в этом ответе, который также ссылается на этот пост :) . Спасибо за ваш пост @Murali. Очень полезно. - person Sudarshan Shubakar; 21.10.2014
comment
Этот помог мне узнать, как указать атрибуты содержащихся объектов с помощью Projections. - person elysch; 12.10.2015
comment
Для всех, кто приезжает сюда, есть запрос функции, который поднял Мурали, который можно найти на jira.spring .io / browse / DATAJPA-393 - person NealeU; 29.01.2016
comment
Ссылка на Документы устарела, похоже, это новое местоположение: docs.spring.io/spring-data/data-commons/docs/current/reference/ - person Elijah Lofgren; 25.07.2016

Для более поздних версий Spring Data я не мог заставить принятый ответ работать, не столкнувшись с проблемами, но обнаружил, что, идя по маршруту из документов Spring Data, действительно работает, пересматривая этот ответ следующим образом:

1. Интерфейс репозитория

@NoRepositoryBean
public interface QueryDslPredicateAndProjectionExecutor<T, ID extends Serializable>
        extends JpaRepository<T, ID>, QueryDslPredicateExecutor<T> {

    <PROJ> Page<PROJ> customFindWithProjection(FactoryExpression<PROJ> factoryExpression, Predicate predicate, Pageable pageable);
}

2. Реализация репозитория

public class QueryDslJpaEnhancedRepositoryImpl<T, ID extends Serializable> extends QueryDslJpaRepository<T, ID>
        implements QueryDslPredicateAndProjectionExecutor<T, ID> {

    //All instance variables are available in super, but they are private
    private static final EntityPathResolver DEFAULT_ENTITY_PATH_RESOLVER = SimpleEntityPathResolver.INSTANCE;

    private final EntityPath<T> path;
    private final PathBuilder<T> builder;
    private final Querydsl querydsl;

    public QueryDslJpaEnhancedRepositoryImpl(JpaEntityInformation<T, ID> entityInformation, EntityManager entityManager) {
        this(entityInformation, entityManager, DEFAULT_ENTITY_PATH_RESOLVER);
    }

    public QueryDslJpaEnhancedRepositoryImpl(JpaEntityInformation<T, ID> entityInformation, EntityManager entityManager,
                                 EntityPathResolver resolver) {

        super(entityInformation, entityManager, resolver);
        this.path = resolver.createPath(entityInformation.getJavaType());
        this.builder = new PathBuilder<T>(path.getType(), path.getMetadata());
        this.querydsl = new Querydsl(entityManager, builder);
    }

    @Override
    public <PROJ> Page<PROJ> customFindWithProjection(FactoryExpression<PROJ> factoryExpression, Predicate predicate, Pageable pageable) {
        JPQLQuery countQuery = createQuery(predicate);
        JPQLQuery query = querydsl.applyPagination(pageable, createQuery(predicate));

        Long total = countQuery.count();
        List<PROJ> content = total > pageable.getOffset() ? query.list(factoryExpression) : Collections.<PROJ>emptyList();

        return new PageImpl<PROJ>(content, pageable, total);
    }
}

3. Установка реализации репозитория по умолчанию

@EnableJpaRepositories(
    repositoryBaseClass=QueryDslJpaEnhancedRepositoryImpl.class,
    basePackageClasses=SomeRepository.class)
person NealeU    schedule 29.01.2016
comment
у вас есть кодовая база для этого где-нибудь, Is PROJ означает мой интерфейс проекции, я получаю некоторые ошибки компиляции, пожалуйста, поделитесь любым сайтом github или где угодно, где я могу сослаться ... - person surya; 07.09.2016
comment
на самом деле после того, как я реализовал, хотя часть dsl работает, результаты, которые я получаю, не проецируются, а целиком. так что вернемся к исходной точке .. пожалуйста, дайте мне знать, есть ли у вас где-нибудь образцы кодов для запроса dsl + projection - person surya; 07.09.2016
comment
И я использую версию весенней загрузки 1.4.0.RELEASE - person surya; 07.09.2016
comment
@NealeU в вашем репозитории impl count () и list (FactoryExpression) находится в версии 3.7, как мне заставить это работать в 4.1.4? - person techRunner; 07.12.2016
comment
Привет, @techRunner, вам нужно будет посмотреть документацию QueryDSL 4 для новых методов, хотя вы, вероятно, найдете ключевые изменения на blog.anthavio.net/2015/11/upgrading-querydsl-3-to-4.html. - person NealeU; 08.12.2016

Для текущих версий Spring Data (1.11.1) и QueryDSL (4) вам необходимо изменить реализацию метода customFindWithProjection следующим образом:

@Override
public <PROJ> Page<PROJ> customFindWithProjection(FactoryExpression<PROJ> factoryExpression, Predicate predicate, Pageable pageable) {

    final JPQLQuery<?> countQuery = createCountQuery(predicate);
    JPQLQuery<PROJ> query = querydsl.applyPagination(pageable, createQuery(predicate).select(factoryExpression));

    long total = countQuery.fetchCount();
    List<PROJ> content = pageable == null || total > pageable.getOffset() ? query.fetch() : Collections.<PROJ> emptyList();

    return new PageImpl<PROJ>(content, pageable, total);
}

Остальной код остается прежним.

person Flor Gadea    schedule 02.03.2017

В качестве обходного пути (хотя и очень уродливого и неэффективного) я просто получил обычный Page, содержащий объекты, из моего репозитория и вручную сопоставил их с проекцией в контроллере следующим образом:

@GetMapping(value = "/columns")
public Page<ColumnProjection> getColumns(@QuerydslPredicate(root = Column.class) final Predicate predicate,
                                         final Pageable pageable) {
 Page<Column> filteredColumns = columnRepository.findAll(predicate, pageable);
 List<ColumnProjection> filteredColumnProjections = new ArrayList<>();
 filteredColumns.forEach(c -> filteredColumnProjections.add(new ColumnProjectionImpl(c)));
 return new PageImpl<>(filteredColumnProjections, pageable, filteredColumnProjections.size());
}

Где ColumnProjectionImpl - это класс, реализующий мой ColumnProjection интерфейс.

Это было самое простое решение, которое я мог придумать, при этом мне не пришлось адаптировать существующий ColumnRepository.

person Patrick    schedule 13.07.2018
comment
Это приведет к извлечению всех столбцов из базы данных, что неэффективно, если у вас много столбцов и / или строк. - person bcody; 15.03.2021

Я сам столкнулся с той же проблемой. Вкратце - мы должны указать настраиваемый компонент фабрики репозитория, который сообщает использовать наше настраиваемое репо как еще один «фрагмент». Поэтому я переопределил factory.getRepositoryFragments, чтобы включить настраиваемую реализацию предиката проекции (IMHO, это решает проблему в вопросе Для типа… настраиваемого репозитория данных Spring не найдено ни одного свойства).

обновленный код, основанный на всех предыдущих ответах:

1.QuerydslPredicateProjectionRepositoryFactory

import org.springframework.data.jpa.repository.support.JpaEntityInformation;
import org.springframework.data.jpa.repository.support.JpaRepositoryFactory;
import org.springframework.data.querydsl.EntityPathResolver;
import org.springframework.data.querydsl.SimpleEntityPathResolver;
import org.springframework.data.repository.core.RepositoryMetadata;
import org.springframework.data.repository.core.support.RepositoryComposition;
import org.springframework.data.repository.core.support.RepositoryFragment;

import javax.persistence.EntityManager;
import java.io.Serializable;

import static org.springframework.data.querydsl.QuerydslUtils.QUERY_DSL_PRESENT;

public class QuerydslPredicateProjectionRepositoryFactory extends JpaRepositoryFactory {

    private final EntityManager entityManager;
    private EntityPathResolver entityPathResolver;

    public QuerydslPredicateProjectionRepositoryFactory(EntityManager entityManager) {
        super(entityManager);
        this.entityManager = entityManager;
        this.entityPathResolver = SimpleEntityPathResolver.INSTANCE;
    }

    @Override
    protected RepositoryComposition.RepositoryFragments getRepositoryFragments(RepositoryMetadata metadata) {
        RepositoryComposition.RepositoryFragments fragments = super.getRepositoryFragments(metadata);

        boolean isQueryDslRepository = QUERY_DSL_PRESENT
                && QuerydslPredicateProjectionRepository.class.isAssignableFrom(metadata.getRepositoryInterface());

        if (isQueryDslRepository) {

            JpaEntityInformation<?, Serializable> entityInformation = getEntityInformation(metadata.getDomainType());

            Object querydslFragment = getTargetRepositoryViaReflection(QuerydslPredicateProjectionRepositoryImpl.class, entityInformation,
                    entityManager, entityPathResolver, null);

            fragments = fragments.append(RepositoryFragment.implemented(querydslFragment));
        }

        return fragments;

    }
}

2.QuerydslPredicateProjectionRepositoryFactoryBean

import java.io.Serializable;

import javax.persistence.EntityManager;

import org.springframework.data.jpa.repository.support.JpaRepositoryFactoryBean;
import org.springframework.data.repository.Repository;
import org.springframework.data.repository.core.support.RepositoryFactorySupport;

public class QuerydslPredicateProjectionRepositoryFactoryBean<T extends Repository<S, ID>, S, ID extends Serializable> extends JpaRepositoryFactoryBean<T, S, ID> {
    public QuerydslPredicateProjectionRepositoryFactoryBean(Class<? extends T> repositoryInterface) {
        super(repositoryInterface);
    }

    protected RepositoryFactorySupport createRepositoryFactory(EntityManager entityManager) {
        return new QuerydslPredicateProjectionRepositoryFactory(entityManager);
    }
}

3.QuerydslPredicateProjectionRepository здесь мы добавляем новые методы, которые используют проекции и т. д.

import com.querydsl.core.types.FactoryExpression;
import com.querydsl.core.types.Predicate;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;

import java.util.List;

public interface QuerydslPredicateProjectionRepository<T> {
    <Projection> Page<Projection> findAll(Predicate predicate, Pageable pageable, FactoryExpression<Projection> factoryExpression);
    <Projection> List<Projection> findAll(Predicate predicate, Sort sort, FactoryExpression<Projection> factoryExpression);
    <Projection> List<Projection> findAll(Predicate predicate, FactoryExpression<Projection> factoryExpression);
}

4.QuerydslPredicateProjectionRepositoryImpl здесь мы реализуем методы интерфейса репозитория.

import com.querydsl.core.QueryResults;
import com.querydsl.core.types.EntityPath;
import com.querydsl.core.types.FactoryExpression;
import com.querydsl.core.types.Predicate;
import com.querydsl.core.types.dsl.PathBuilder;
import com.querydsl.jpa.JPQLQuery;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.jpa.repository.support.CrudMethodMetadata;
import org.springframework.data.jpa.repository.support.JpaEntityInformation;
import org.springframework.data.jpa.repository.support.Querydsl;
import org.springframework.data.jpa.repository.support.QuerydslJpaPredicateExecutor;
import org.springframework.data.querydsl.EntityPathResolver;
import org.springframework.data.querydsl.SimpleEntityPathResolver;

import javax.persistence.EntityManager;
import java.util.List;

public class QuerydslPredicateProjectionRepositoryImpl<T> extends QuerydslJpaPredicateExecutor<T> implements QuerydslPredicateProjectionRepository<T> {
    private static final EntityPathResolver DEFAULT_ENTITY_PATH_RESOLVER = SimpleEntityPathResolver.INSTANCE;

    private final Querydsl querydsl;


    public QuerydslPredicateProjectionRepositoryImpl(JpaEntityInformation<T, ?> entityInformation, EntityManager entityManager) {
        this(entityInformation, entityManager, DEFAULT_ENTITY_PATH_RESOLVER);
    }

    public QuerydslPredicateProjectionRepositoryImpl(JpaEntityInformation<T, ?> entityInformation, EntityManager entityManager, EntityPathResolver resolver) {
        super(entityInformation, entityManager, resolver, null);

        EntityPath<T> path = resolver.createPath(entityInformation.getJavaType());
        PathBuilder<T> builder = new PathBuilder<T>(path.getType(), path.getMetadata());
        this.querydsl = new Querydsl(entityManager, builder);
    }

    public QuerydslPredicateProjectionRepositoryImpl(JpaEntityInformation<T, ?> entityInformation, EntityManager entityManager, EntityPathResolver resolver, CrudMethodMetadata metadata) {
        super(entityInformation, entityManager, resolver, metadata);
        EntityPath<T> path = resolver.createPath(entityInformation.getJavaType());
        PathBuilder<T> builder = new PathBuilder<T>(path.getType(), path.getMetadata());
        this.querydsl = new Querydsl(entityManager, builder);
    }

    @Override
    public <Projection> List<Projection> findAll(Predicate predicate, FactoryExpression<Projection> factoryExpression) {
        return createQuery(predicate).select(factoryExpression).fetch();
    }

    @Override
    public <Projection> List<Projection> findAll(Predicate predicate, Sort sort, FactoryExpression<Projection> factoryExpression) {
        JPQLQuery<Projection> query = createQuery(predicate).select(factoryExpression);
        querydsl.applySorting(sort, query);

        return query.fetch();
    }

    @Override
    public <Projection> Page<Projection> findAll(Predicate predicate, Pageable pageable, FactoryExpression<Projection> factoryExpression) {
        JPQLQuery<Projection> query = createQuery(predicate).select(factoryExpression);
        querydsl.applyPagination(pageable, query);
        querydsl.applySorting(pageable.getSort(), query);

        QueryResults<Projection> queryResults = query.fetchResults();
        return new PageImpl<>(queryResults.getResults(), pageable, queryResults.getTotal());
    }
}

5. Пример объекта

@Entity
public class Example extends Serializable{
    private static final long serialVersionUID = 1L;
    @Id
    @Column(name = "id")
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    protected Long id;
    @Column
    private String name;
    @Column
    private String surname;
    @Column
    private Integer year;

    public Example() {
    }
    public Long getId() {return id;}
    public void setId(Long id) {this.id = id;}
    public String getName() {return name;}
    public void setName(String name) {this.name = name;}
    public String getSurname() {return surname;}
    public void setSurname(String surname) {this.surname= surname;}
    public Integer getYear() {return year;}
    public void setSurname(Integer year) {this.year= year;}
}

6.Пример репозитория

@Repository
public interface ExampleRepository extends JpaRepository<Example, Long>, QuerydslPredicateProjectionRepository<Example> { }

7. Пример использования конфигурации:

@EnableJpaRepositories(repositoryFactoryBeanClass = QuerydslPredicateProjectionRepositoryFactoryBean.class)

типичное использование:

//get list of entities only with year field value set - memory consuming
List<Example> years = repository.findAll(predicate, Projections.fields(Example.class, QExample.example.year)); 
//get list of tuples - looks nicer - less memory consuming
List<Tuple> years = repository.findAll(predicate, Projections.tuple(QExample.example.year));
//get list of integers - nice :)
List<Integer> years = repository.findAll(predicate, Projections.constructor(Integer.class, QExample.example.year));
person valc    schedule 29.03.2019

Мне удалось добиться того же результата с помощью небольшого количества кода.

public class Queryable<T> extends QuerydslJpaPredicateExecutor<T> {

  private static final EntityPathResolver resolver = SimpleEntityPathResolver.INSTANCE;

  private final Querydsl querydsl;

  public Queryable(Class<T> domainClass, EntityManager entityManager) {
      this(JpaEntityInformationSupport.getEntityInformation(domainClass, entityManager), 
           entityManager);
  }

  private Queryable(JpaEntityInformation<T, ?> entityInformation,
      EntityManager entityManager) {
    super(entityInformation, entityManager, resolver, null);
    EntityPath<T> path = resolver.createPath(entityInformation.getJavaType());
    PathBuilder<?> builder = new PathBuilder<>(path.getType(), path.getMetadata());
    this.querydsl = new Querydsl(entityManager, builder);
  }

  public Page<T> findAll(Expression<T> expression, Predicate predicate, Pageable pageable) {
     JPQLQuery<?> countQuery = createQuery(predicate);
     JPQLQuery<T> query = querydsl.applyPagination(pageable,
                          createQuery(predicate).select(expression));
     List<T> dtos = query.fetch();
     return PageableExecutionUtils.getPage(dtos, pageable, countQuery::fetchCount);
  }
}

Использование:

@Repository
@Transactional
class UserDemoRepository
    private static final QUserDemo q = QUserDemo.userDemo;
    private static final QBean<UserDemo> PROJECTION = Projections.bean(UserDemo.class, 
            q.id, q.username);

    @PersistenceContext
    private EntityManager entityManager;
    public Page<UserDemo> findAll(Predicate predicate, Pageable pageable) {
        return new Queryable<UserDemo>(UserDemo.class, entityManager)
               .findAll(PROJECTION, predicate, pageable);
  }
}

(на основе https://stackoverflow.com/a/53960209/1833472)

person dpolivaev    schedule 19.07.2019
comment
красиво, очень просто и эффективно по сравнению с другими ответами. Я немного изменил этот подход, чтобы сделать новые методы доступными в существующих интерфейсах. Я создал настроенный BaseRepository, который сделал экземпляр EntityManager доступным для всех моих существующих интерфейсов репозитория. Затем я использовал методы по умолчанию для вызова метода findAll () из этих репозиториев, передав EntityManager из настроенного BaseRepository. - person Joakim; 21.08.2019
comment
Я могу подтвердить, что это решение работает и выбирает только прогнозируемые значения из базы данных. - person Sven Döring; 18.05.2020

1. CustomJpaRepositoryFactoryBean

import java.io.Serializable;

import javax.persistence.EntityManager;

import org.springframework.data.jpa.repository.support.JpaRepositoryFactoryBean;
import org.springframework.data.repository.Repository;
import org.springframework.data.repository.core.support.RepositoryFactorySupport;

public class CustomJpaRepositoryFactoryBean<T extends Repository<S, ID>, S, ID extends Serializable> extends JpaRepositoryFactoryBean<T, S, ID> {
    public CustomJpaRepositoryFactoryBean(Class<? extends T> repositoryInterface) {
        super(repositoryInterface);
    }

    protected RepositoryFactorySupport createRepositoryFactory(EntityManager entityManager) {
        return new CustomJpaRepositoryFactory(entityManager);
    }
}

2. CustomJpaRepositoryFactory

import static org.springframework.data.querydsl.QueryDslUtils.QUERY_DSL_PRESENT;

import javax.persistence.EntityManager;

import org.springframework.data.jpa.repository.support.JpaRepositoryFactory;
import org.springframework.data.jpa.repository.support.QueryDslJpaRepository;
import org.springframework.data.jpa.repository.support.SimpleJpaRepository;
import org.springframework.data.querydsl.QueryDslPredicateExecutor;
import org.springframework.data.repository.Repository;
import org.springframework.data.repository.core.RepositoryMetadata;

public class CustomJpaRepositoryFactory extends JpaRepositoryFactory {
    public CustomJpaRepositoryFactory(EntityManager entityManager) {
        super(entityManager);
    }

    @Override
    protected Class<?> getRepositoryBaseClass(RepositoryMetadata metadata) {
        if(QUERY_DSL_PRESENT) {
            Class<?> repositoryInterface = metadata.getRepositoryInterface();
            if(CustomQueryDslPredicateExecutor.class.isAssignableFrom(repositoryInterface)) {
                return CustomQueryDslJpaRepository.class;
            } else  if(QueryDslPredicateExecutor.class.isAssignableFrom(repositoryInterface)) {
                return QueryDslJpaRepository.class;
            }
        }
        return SimpleJpaRepository.class;
    }
}

3. CustomQueryDslJpaRepository

import java.io.Serializable;

import javax.persistence.EntityManager;

import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.repository.support.JpaEntityInformation;
import org.springframework.data.jpa.repository.support.QueryDslJpaRepository;
import org.springframework.data.jpa.repository.support.Querydsl;
import org.springframework.data.querydsl.EntityPathResolver;
import org.springframework.data.querydsl.SimpleEntityPathResolver;

import com.querydsl.core.QueryResults;
import com.querydsl.core.types.EntityPath;
import com.querydsl.core.types.FactoryExpression;
import com.querydsl.core.types.Predicate;
import com.querydsl.core.types.Projections;
import com.querydsl.core.types.dsl.PathBuilder;
import com.querydsl.jpa.JPQLQuery;

public class CustomQueryDslJpaRepository<T, ID extends Serializable> extends QueryDslJpaRepository<T, ID> implements CustomQueryDslPredicateExecutor<T> {
    private static final EntityPathResolver DEFAULT_ENTITY_PATH_RESOLVER = SimpleEntityPathResolver.INSTANCE;

    private final Querydsl querydsl;

    public CustomQueryDslJpaRepository(JpaEntityInformation<T, ID> entityInformation, EntityManager entityManager) {
        this(entityInformation, entityManager, DEFAULT_ENTITY_PATH_RESOLVER);
    }

    public CustomQueryDslJpaRepository(JpaEntityInformation<T, ID> entityInformation, EntityManager entityManager, EntityPathResolver resolver) {
        super(entityInformation, entityManager, resolver);

        EntityPath<T> path = resolver.createPath(entityInformation.getJavaType());
        PathBuilder<T> builder = new PathBuilder<T>(path.getType(), path.getMetadata());
        this.querydsl = new Querydsl(entityManager, builder);
    }

    public <DTO> Page<DTO> findAll(Predicate predicate, Pageable pageable, FactoryExpression<DTO> factoryExpression) {
        JPQLQuery<DTO> query = createQuery(predicate).select(factoryExpression);
        querydsl.applyPagination(pageable, query);
        querydsl.applySorting(pageable.getSort(), query);

        QueryResults<DTO> queryResults = query.fetchResults();
        return new PageImpl<>(queryResults.getResults(), pageable, queryResults.getTotal());
    }
}

4. CustomQueryDslPredicateExecutor

import com.querydsl.core.types.FactoryExpression;
import com.querydsl.core.types.Predicate;

import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.querydsl.QueryDslPredicateExecutor;

public interface CustomQueryDslPredicateExecutor<T> extends QueryDslPredicateExecutor<T> {
    <DTO> Page<DTO> findAll(Predicate predicate, Pageable pageable, FactoryExpression<DTO> factoryExpression);
}

5. пример

@EnableJpaRepositories(
    ...
    repositoryFactoryBeanClass = CustomJpaRepositoryFactoryBean.class
)


public interface ProductRepository extends JpaRepository<Product, Long> implements CustomQueryDslPredicateExecutor<Product> {
}
person Marcus Moon    schedule 23.07.2018
comment
Спасибо, сэр. Это была очень полезная ссылка, которая работала для моего требования иметь настраиваемый базовый репозиторий для подмножества моих репозиториев, которые должны поддерживать api предиката QueryDSL с NamedEntityGraphs - person bdumtish; 12.10.2020