【问题标题】:Spring Data JPA and Querydsl to fetch subset of columns using bean/constructor projectionSpring Data JPA 和 Querydsl 使用 bean/构造函数投影获取列的子集
【发布时间】:2013-08-20 11:06:19
【问题描述】:

我有一个实体类如下:

@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 如何获取仅填充了 idusername 属性的 UserDemo 页面?我需要使用分页和搜索。简而言之,我想达到与

相同的结果
Page<UserDemo> findAll(Predicate predicate, Pageable pageable);

UserDemo 的字段有限。

【问题讨论】:

    标签: jpa spring-data-jpa querydsl


    【解决方案1】:

    我只需要一点代码就能达到同样的效果。

    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

    【讨论】:

    • 很好,与其他答案相比非常简单有效。我稍微修改了这种方法,以使新方法在现有接口中可用。我创建了一个自定义的 BaseRepository,它使 EntityManager 实例可用于我所有现有的存储库接口。然后我使用默认方法从这些存储库中调用 findAll() 方法,从自定义 BaseRepository 传递 EntityManager
    • 我可以确认此解决方案有效,并且仅从数据库中选择投影值。
    【解决方案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这里我们实现repository接口方法

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

    【讨论】:

      【解决方案3】:

      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> {
      }
      

      【讨论】:

      • 谢谢先生。这是非常有用的参考资料,它满足了我的要求,即为我需要支持带有 NamedEntityGraphs 的 QueryDSL 谓词 api 的存储库子集拥有一个自定义基础存储库
      【解决方案4】:

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

      【讨论】:

      • 这将从数据库中获取所有列,如果您有很多列和/或行,这是低效的。
      【解决方案5】:

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

      其余代码保持不变。

      【讨论】:

        【解决方案6】:

        对于 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 站点或任何我可以参考的地方...
        • 实际上在我实施之后,虽然 dsl 部分工作,但我得到的结果不是投影而是整个对象。所以回到第一方..如果您在某处有用于查询 dsl + 投影的示例代码,请告诉我
        • 我使用的是 1.4.0.RELEASE spring boot 版本
        • @NealeU 在您的存储库中 impl count() 和 list(FactoryExpression) 在 3.7 中,如何在 4.1.4 中进行这项工作?
        • 嗨 @techRunner,您必须查看 QueryDSL 4 文档以了解新方法,尽管您可能会在 blog.anthavio.net/2015/11/upgrading-querydsl-3-to-4.html 找到关键更改。
        【解决方案7】:

        看起来自定义存储库实现是目前要走的路,直到在 spring 数据中提供类似的东西。

        我已经通过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);
        }
        

        第二步:中间接口的实现

        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" />
        

        注意:不要将自定义存储库接口和实现与基本包放在同一目录中。如果您正在放置,则将它们排除在扫描之外,否则 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);
            }
        
        }
        

        【讨论】:

        • FactoryExpression 是自定义类还是预定义的。
        • FactoryExpression 是 Querydsl 中 Projections 的基类。 querydsl.com/static/querydsl/3.2.0/apidocs/com/mysema/query/…
        • 当我尝试这个时,我得到了一个Caused by: org.springframework.data.mapping.PropertyReferenceException: No property find found for type &lt;MY_TYPE_NAME&gt;。这是为什么? 当然代表我的实体类型。
        • 好吧,经过一番尝试,我能够解决我的问题。我注意到您需要使用@NoRepositoryBean 注释自定义界面,如this answer 中所述,这也引用了这篇文章:)。感谢您的帖子@Murali。很有帮助。
        • This one 帮助我了解如何使用Projections 指定包含实体的属性。
        猜你喜欢
        • 1970-01-01
        • 2021-06-01
        • 1970-01-01
        • 2021-03-22
        • 2019-04-20
        • 1970-01-01
        • 2012-11-09
        • 2018-11-05
        • 2011-12-25
        相关资源
        最近更新 更多