使用Spring Data JPA和Querydsl进行bean/构造函数投影获取列的子集

39

我有一个如下的实体类:

@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,如何获取只有id和username属性填充的UserDemo的页面?我需要使用分页和搜索。简而言之,我想实现与...相同的结果
Page<UserDemo> findAll(Predicate predicate, Pageable pageable);

但只填充了有限的UserDemo字段。

7个回答

38

看起来目前使用自定义仓库实现是唯一的选择,直到Spring Data中有类似的东西可用。

我已经阅读了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);
    }
}

步骤三:创建一个自定义的存储库工厂来替换默认值。
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;
        }
    }
}

步骤四:使用自定义存储库工厂
使用注解
@EnableJpaRepositories(repositoryFactoryBeanClass=CustomQueryDslJpaRepositoryFactoryBean.class)

或者使用XML

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

注意:不要将自定义仓库接口和实现放置在与基本包相同的目录中。如果您已经放置了它们,请将其从扫描中排除,否则Spring将尝试为它们创建bean。
示例用法
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);
    }

}

当我尝试这样做时,我收到了一个Caused by: org.springframework.data.mapping.PropertyReferenceException: No property find found for type <MY_TYPE_NAME>的错误。为什么会这样呢?这里的<MY_TYPE_NAME>代表我的实体类型。 - ss_everywhere
3
好的,我经过一些尝试成功地解决了我的问题。我注意到你需要在自定义接口上注释@NoRepositoryBean,就像这个回答中提到的那样,该回答还参考了这篇文章:)。感谢你的帖子@Murali。非常有帮助。 - ss_everywhere
1
这个回答帮助我了解如何使用Projections指定包含实体的属性。 - elysch
对于任何进入此处的人,有一个Murali提出的功能请求,请访问https://jira.spring.io/browse/DATAJPA-393。 - NealeU
文档链接已过期,这似乎是新位置:http://docs.spring.io/spring-data/data-commons/docs/current/reference/html/#repositories.custom-implementations - Elijah Lofgren
显示剩余2条评论

9

对于更近期的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)

你有这个代码库吗?PROJ是指我的投影接口吗?我遇到了一些编译错误,请分享任何GitHub网站或其他地方供我参考... - surya
实际上,尽管我已经实现了 DSL 部分,但我得到的结果并不是预期的投影对象,而是整个对象。所以回到原点...如果您有关于查询 DSL + 投影的示例代码,请告诉我。 - surya
我正在使用1.4.0.RELEASE版本的Spring Boot。 - surya
@NealeU 在你的代码库中,count() 和 list(FactoryExpression) 是在3.7版本中实现的,我该如何在4.1.4版本中让它们正常工作? - techRunner
嗨@techRunner,你需要查看QueryDSL 4文档以获取新方法的信息,尽管你可能会在http://blog.anthavio.net/2015/11/upgrading-querydsl-3-to-4.html找到关键变化。 - NealeU

6

对于当前版本的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);
}

代码的其余部分保持不变。

4
我只需加入少量代码即可实现相同的结果。
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://dev59.com/nlQJ5IYBdhLWcg3wzJEP#53960209
请注意,您需要提供要翻译的内容。

与其他答案相比,这是一个非常简单且有效的方法。我稍微修改了这个方法,以便在现有接口中提供新的方法。我创建了一个自定义的BaseRepository,使EntityManager实例可用于所有现有的存储库接口。然后我使用默认方法从这些存储库调用findAll()方法,并传递来自定制BaseRepository的EntityManager。 - Joakim
我可以确认这个解决方案是可行的,并且只从数据库中选择了投影值。 - Sven Döring

2

我刚遇到了同样的问题。 简而言之 - 我们需要指定自定义的存储库工厂bean,告诉它将我们的自定义存储库作为另一个“片段”使用。 所以我重写了factory.getRepositoryFragments,包括一个自定义投影谓词实现(在我看来,这解决了问题No property found for type… custom Spring Data repository)。

根据所有先前的答案更新的代码:

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));

2
作为一种(虽然非常丑陋和低效的)解决方法,我只是从我的存储库中检索了一个包含实体的常规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


1
这将从数据库中提取所有列,如果您有很多列和/或行,则效率不高。 - bcody

1

1. CustomJpaRepositoryFactoryBean

自定义JpaRepositoryFactoryBean
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

自定义查询DSL谓词执行器
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> {
}

谢谢您先生。这是非常有用的参考资料,它满足了我的需求,为我需要支持QueryDSL谓词API和NamedEntityGraphs的子存储库创建了一个自定义基础存储库。 - bdumtish

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接