jpa - Is there a way to eager fetch a lazy relationship through the Predicate API in QueryDSL? -
i using querydslpredicateexecutor
spring data jpa project, , facing need eager fetch lazy relation. know can use native jpa-ql query in repository interface, or used jpaqlquery query dsl, intrigued if possible in order facilitate building queries future needs.
i had similar problem had fetch join collection while using predicates , querydslpredicateexecutor.
what did create custom repository implementation add method allowed me define entities should fetched.
don't daunted amount of code in here, it's simple , need few changes use on application
this interface of custom repository
@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); } }
implementation of custom repository
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(); } }
factory create custom repositories, replacing default querydsljparepository
public class joinfetchcapablequerydsljparepositoryfactorybean<r extends jparepository<t, i>, t, extends serializable> extends jparepositoryfactorybean<r, t, i> { protected repositoryfactorysupport createrepositoryfactory(entitymanager entitymanager) { return new joinfetchcapablequerydsljparepositoryfactory(entitymanager); } private static class joinfetchcapablequerydsljparepositoryfactory<t, 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; } } }
last step change jpa configuration uses factory instead of default one:
<jpa:repositories base-package="com.mycompany.repository" entity-manager-factory-ref="entitymanagerfactory" factory-class="com.mycompany.utils.spring.data.joinfetchcapablequerydsljparepositoryfactorybean" />
then can use service layer this:
public page<eticket> list(eticketsearch eticket, pageable pageable) { return eticketrepository.findall(like(eticket), pageable, joindescriptor.leftjoin(qeticket.eticket.order)); }
by using joindescriptor able specify want join based on service needs.
i able murali's response here: spring data jpa , querydsl fetch subset of columns using bean/constructor projection please take look.
Comments
Post a Comment