2013-05-08 22 views
7

Tôi đang sử dụng QueryDslPredicateExecutor từ dự án JPA dữ liệu mùa xuân và tôi đang phải đối mặt với nhu cầu mong muốn tìm nạp mối quan hệ lười biếng. Tôi biết rằng tôi có thể sử dụng truy vấn JPA-QL nguyên gốc trong giao diện Repository, hoặc thậm chí sử dụng JPAQLQuery từ DSL truy vấn, nhưng tôi bị hấp dẫn nếu điều này thậm chí có thể để tạo điều kiện truy vấn xây dựng cho các nhu cầu trong tương lai.Có cách nào háo hức tìm kiếm mối quan hệ lười biếng thông qua API Predicate trong QueryDSL không?

+0

Ý của bạn là gì nếu điều này thậm chí có thể xảy ra? 'QueryDslPredicateExecutor' cung cấp một tập con nhỏ của chức năng Querydsl, để biết thêm bạn cần tự viết các truy vấn. –

+0

Vâng, tôi biết điều đó, nhưng tôi đang cố nghĩ cách tránh điều đó và sử dụng API Predicate, để xây dựng các truy vấn phức tạp hơn, cho phép tôi tìm nạp các mối quan hệ theo ý muốn, nối thêm các vị từ khác, do đó không phải tạo phương thức với truy vấn được chú thích cho một mục đích cụ thể trong giao diện Kho lưu trữ. –

Trả lời

11

Tôi gặp sự cố tương tự khi tôi phải tìm nạp một Bộ sưu tập trong khi sử dụng Predicates và QueryDslPredicateExecutor.

Điều tôi đã làm là tạo triển khai kho lưu trữ tùy chỉnh để thêm phương thức cho phép tôi xác định các thực thể sẽ được tìm nạp.

Đừng để bị nản lòng bởi số lượng mã ở đây, nó thực sự rất đơn giản và bạn sẽ cần phải làm rất ít thay đổi để sử dụng nó trên ứng dụng của bạn

Đây là giao diện của tùy chỉnh kho

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

Thực hiện các kho lưu trữ tùy chỉnh

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 để tạo ra các kho tùy chỉnh, thay thế mặc định 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; 
     } 
    } 
} 

Bước cuối cùng là thay đổi cấu hình JPA để nó sử dụng nhà máy này thay vì mặc định:

<jpa:repositories base-package="com.mycompany.repository" 
         entity-manager-factory-ref="entityManagerFactory" 
         factory-class="com.mycompany.utils.spring.data.JoinFetchCapableQueryDslJpaRepositoryFactoryBean" /> 

Sau đó, bạn có thể sử dụng nó từ lớp dịch vụ của bạn như thế này:

public Page<ETicket> list(ETicketSearch eTicket, Pageable pageable) { 
    return eticketRepository.findAll(like(eTicket), pageable, JoinDescriptor.leftJoin(QETicket.eTicket.order)); 
} 

Bằng cách sử dụng JoinDescriptor bạn sẽ có thể để xác định những gì bạn muốn tham gia dựa trên nhu cầu dịch vụ của bạn.

Tôi đã có thể thực hiện điều này nhờ phản hồi của Murali tại đây: Spring Data JPA and Querydsl to fetch subset of columns using bean/constructor projection Vui lòng xem qua.

+1

Cảm ơn câu trả lời của bạn. Mặc dù tôi đã kết thúc làm việc với phương pháp Tiêu chí, tôi sẽ thử nghiệm phương pháp này trên một dự án nhỏ hơn mà tôi có để thử nghiệm các công cụ. Cảm ơn sự nỗ lực của bạn ... –

+0

Xin chào, cảm ơn câu trả lời. Có thể mở rộng JoinDescriptor để bao gồm onetomany (tức là SetPath) thay vì chỉ EntityPath không? –

+0

@AngelVillalain "Mặc dù tôi đã kết thúc làm việc với phương pháp Tiêu chí" - Bạn có thể giải thích cách bạn làm cho nó hoạt động không? – Pranjal

Các vấn đề liên quan