有没有办法通过 QueryDSL 中的谓词 API 急切地获取惰性关系?
Posted
技术标签:
【中文标题】有没有办法通过 QueryDSL 中的谓词 API 急切地获取惰性关系?【英文标题】:Is there a way to eager fetch a lazy relationship through the Predicate API in QueryDSL? 【发布时间】:2013-05-03 04:44:36 【问题描述】:我正在使用来自 Spring Data JPA 项目的QueryDslPredicateExecutor
,并且我正面临着急切获取惰性关系的需要。我知道我可以在 Repository 接口中使用本机 JPA-QL 查询,甚至可以使用 Query DSL 中的 JPAQLQuery,但我很感兴趣,如果这甚至可以为将来的需求构建查询提供便利。
【问题讨论】:
如果这可能是什么意思?QueryDslPredicateExecutor
提供了一小部分 Querydsl 功能,更多需要您自己编写查询。
是的,我知道,但我试图想办法避免这种情况并使用 Predicate API 来构建更复杂的查询,允许我随意获取关系,附加其他谓词,因此不必在 Repository 接口中为特定目的创建带有注释查询的方法。
【参考方案1】:
我遇到了类似的问题,我必须在使用 Predicates 和 QueryDslPredicateExecutor 时获取加入集合。
我所做的是创建一个自定义存储库实现,以添加一个允许我定义应获取的实体的方法。
不要被这里的代码量吓倒,它实际上非常简单,您只需做很少的更改即可在您的应用程序中使用它
这是自定义仓库的界面
@NoRepositoryBean
public interface JoinFetchCapableRepository<T, ID extends Serializable> extends JpaRepository<T, ID>, QueryDslPredicateExecutor<T>
Page<T> findAll(Predicate predicate, Pageable pageable, JoinDescriptor... joinDescriptors);
JoinDescriptor
public class JoinDescriptor
public final EntityPath path;
public final JoinType type;
private JoinDescriptor(EntityPath path, JoinType type)
this.path = path;
this.type = type;
public static JoinDescriptor innerJoin(EntityPath path)
return new JoinDescriptor(path, JoinType.INNERJOIN);
public static JoinDescriptor join(EntityPath path)
return new JoinDescriptor(path, JoinType.JOIN);
public static JoinDescriptor leftJoin(EntityPath path)
return new JoinDescriptor(path, JoinType.LEFTJOIN);
public static JoinDescriptor rightJoin(EntityPath path)
return new JoinDescriptor(path, JoinType.RIGHTJOIN);
public static JoinDescriptor fullJoin(EntityPath path)
return new JoinDescriptor(path, JoinType.FULLJOIN);
自定义存储库的实现
public class JoinFetchCapableRepositoryImpl <T, ID extends Serializable> extends QueryDslJpaRepository<T, ID> implements JoinFetchCapableRepository<T, ID>
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 JoinFetchCapableRepositoryImpl(JpaEntityInformation<T, ID> entityInformation, EntityManager entityManager)
this(entityInformation, entityManager, DEFAULT_ENTITY_PATH_RESOLVER);
public JoinFetchCapableRepositoryImpl(JpaEntityInformation<T, ID> entityInformation, EntityManager entityManager, EntityPathResolver resolver)
super(entityInformation, entityManager, resolver);
this.path = resolver.createPath(entityInformation.getJavaType());
this.builder = new PathBuilder<>(path.getType(), path.getMetadata());
this.querydsl = new Querydsl(entityManager, builder);
@Override
public Page<T> findAll(Predicate predicate, Pageable pageable, JoinDescriptor... joinDescriptors)
JPQLQuery countQuery = createQuery(predicate);
JPQLQuery query = querydsl.applyPagination(pageable, createFetchQuery(predicate, joinDescriptors));
Long total = countQuery.count();
List<T> content = total > pageable.getOffset() ? query.list(path) : Collections.<T> emptyList();
return new PageImpl<>(content, pageable, total);
protected JPQLQuery createFetchQuery(Predicate predicate, JoinDescriptor... joinDescriptors)
JPQLQuery query = querydsl.createQuery(path);
for(JoinDescriptor joinDescriptor: joinDescriptors)
join(joinDescriptor, query);
return query.where(predicate);
private JPQLQuery join(JoinDescriptor joinDescriptor, JPQLQuery query)
switch(joinDescriptor.type)
case DEFAULT:
throw new IllegalArgumentException("cross join not supported");
case INNERJOIN:
query.innerJoin(joinDescriptor.path);
break;
case JOIN:
query.join(joinDescriptor.path);
break;
case LEFTJOIN:
query.leftJoin(joinDescriptor.path);
break;
case RIGHTJOIN:
query.rightJoin(joinDescriptor.path);
break;
case FULLJOIN:
query.fullJoin(joinDescriptor.path);
break;
return query.fetch();
工厂创建自定义存储库,替换默认的 QueryDslJpaRepository
public class JoinFetchCapableQueryDslJpaRepositoryFactoryBean<R extends JpaRepository<T, I>, T, I extends Serializable>
extends JpaRepositoryFactoryBean<R, T, I>
protected RepositoryFactorySupport createRepositoryFactory(EntityManager entityManager)
return new JoinFetchCapableQueryDslJpaRepositoryFactory(entityManager);
private static class JoinFetchCapableQueryDslJpaRepositoryFactory<T, I extends Serializable> extends JpaRepositoryFactory
private EntityManager entityManager;
public JoinFetchCapableQueryDslJpaRepositoryFactory(EntityManager entityManager)
super(entityManager);
this.entityManager = entityManager;
protected Object getTargetRepository(RepositoryMetadata metadata)
return new JoinFetchCapableRepositoryImpl<>(getEntityInformation(metadata.getDomainType()), entityManager);
protected Class<?> getRepositoryBaseClass(RepositoryMetadata metadata)
return JoinFetchCapableRepository.class;
最后一步是更改 jpa 配置,使其使用此工厂而不是默认工厂:
<jpa:repositories base-package="com.mycompany.repository"
entity-manager-factory-ref="entityManagerFactory"
factory-class="com.mycompany.utils.spring.data.JoinFetchCapableQueryDslJpaRepositoryFactoryBean" />
然后你可以像这样从你的服务层使用它:
public Page<ETicket> list(ETicketSearch eTicket, Pageable pageable)
return eticketRepository.findAll(like(eTicket), pageable, JoinDescriptor.leftJoin(QETicket.eTicket.order));
通过使用 JoinDescriptor,您将能够根据您的服务需求指定要加入的内容。
感谢 Murali 在这里的回复:Spring Data JPA and Querydsl to fetch subset of columns using bean/constructor projection 请看一下。
【讨论】:
感谢您的回答。虽然我最终使用了 Criteria 方法,但我将在一个较小的项目上测试这种方法,我有一个用于测试东西的项目。感谢您的努力... 您好,感谢您的回答。是否可以扩展 JoinDescriptor 以包含 onetomany(即 SetPath)而不仅仅是 EntityPath? @AngelVillalain “虽然我最终使用了 Criteria 方法” - 你能解释一下你是如何让它工作的吗? @Pranjal,对不起,那是很久以前的事了,我现在不记得细节了。我会尝试,如果我记得我会更新答案。对不起!【参考方案2】:弹簧数据has introduced JPA Entity Graph support。当心那是does not currently work with graphs that are traversed via EmbeddedIds。
【讨论】:
当被问到这个问题时,没有对 JPA Entity Graph 的支持,确切地说它仍在开发中。但是现在可以使用了,我认为这个问题应该将其视为一种解决方案。以上是关于有没有办法通过 QueryDSL 中的谓词 API 急切地获取惰性关系?的主要内容,如果未能解决你的问题,请参考以下文章
为 oneTomany 基于关系的查询编写 queryDSL 谓词查询
如何使用 BooleanBuilder (QueryDSL) 为可选的 OnetoOne JPA/Hibernate 关系建模谓词?