如何在 QueryDSL 中只获取满足一对多关系条件的列?

How to fetch only columns that satisfy the condition in one-to-many relationship in QueryDSL?

我有以下两个实体:

@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
@Entity
@Table(name = "affiliate_programs")
@SequenceGenerator(name = AbstractEntity.GENERATOR, sequenceName = "affiliate_programs_seq", allocationSize = 1)
public class AffiliateProgram extends AbstractAuditableDeletableEntity {

    private static final int DESCRIPTION_LENGTH = 512;

    @Column(nullable = false)
    private String title;

    @OneToMany(mappedBy = "affiliateProgram", fetch = FetchType.LAZY)
    private Set<AffiliateProgramStatistics> statistics;

    public enum SortType implements ISortType {
        ID(QAffiliateProgram.affiliateProgram.id),
        TITLE(QAffiliateProgram.affiliateProgram.title),

        @Getter
        private ComparableExpressionBase[] expressions;

        SortType(final ComparableExpressionBase... expressions) {
            this.expressions = expressions;
        }
    }
}
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
@Entity
@Table(name = "affiliate_programs_statistics")
@SequenceGenerator(name = AbstractEntity.GENERATOR, sequenceName = "affiliate_programs_statistics_seq", allocationSize = 1)
public class AffiliateProgramStatistics extends AbstractAuditableEntity {

    @ManyToOne(fetch = FetchType.LAZY, optional = false, cascade = CascadeType.ALL)
    private AffiliateProgram affiliateProgram;

    @Enumerated(EnumType.STRING)
    private EventType eventType;

    private LocalDate date;

    public enum EventType {
        MERCHANTS,
        PRIORITY_MERCHANTS,
        COUPONS,
        CLICKS
    }
}

我试图仅从 AffiliateProgramStatistics 中获取与 SQL 条件匹配的列。我的 SQL 查询如下所示:

select *
from affiliate_programs ap
         left join affiliate_programs_statistics aps on ap.id = aps.affiliate_program_id
where ap.deleted = false and aps.date between '2020-07-20' and '2020-08-20';

这个查询的结果正是我需要的 - 我只得到未标记为已删除的列和日期在所需日期之间的列。

我尝试在 QueryDSL 中编写该查询,这就是我想出的:

@Repository
@RequiredArgsConstructor
public class AffiliateProgramsCustomRepositoryImpl implements AffiliateProgramsCustomRepository {

    private final EntityManager entityManager;

    @Override
    public Page<AffiliateProgram> search(final AffiliateProgramSearchForm form) {
        final QAffiliateProgram affiliateProgram = QAffiliateProgram.affiliateProgram;
        final QAffiliateProgramStatistics affiliateProgramStatistics = QAffiliateProgramStatistics.affiliateProgramStatistics;
        final JPAQuery<AffiliateProgram> query = new JPAQuery<AffiliateProgram>(entityManager)
                .distinct()
                .from(affiliateProgram)
                .leftJoin(affiliateProgram.statistics, affiliateProgramStatistics)
                .where(AffiliateProgramsRepositoryHelper.getPredicates(form))
                .orderBy(AffiliateProgramsRepositoryHelper.getOrders(form.getSorting()))
                .limit(form.getLimit())
                .offset(form.getOffset());

        return AffiliateProgramsRepositoryHelper.pageBy(query, form);
    }
}
public class AffiliateProgramsRepositoryHelper extends RepositoryHelper {

    public static Predicate[] getPredicates(final AffiliateProgramSearchForm form) {
        final QAffiliateProgramStatistics affiliateProgramStatistics = QAffiliateProgramStatistics.affiliateProgramStatistics;
        final List<Predicate> predicates = new ArrayList<>();
        final String formattedQuery = form.getFormattedQuery();

        if (!isNullOrEmpty(formattedQuery)) {
            predicates.add(affiliateProgramStatistics.affiliateProgram.title.likeIgnoreCase(formattedQuery));
        }

        predicates.add(affiliateProgramStatistics.date.between(form.getFrom(), form.getTo()));
        predicates.add(affiliateProgramStatistics.affiliateProgram.deleted.isFalse());

        return predicates.toArray(new Predicate[0]);
    }
}

但是这样的结果并不令人满意。如果 AffiliateProgramStatistics 中的至少一列与 between() 条件匹配,它将从 table 中提取与 tje leftJoin() 条件匹配的每一列。

如何才能只获取我需要的列?

P.S。 Hibernate 生成以下查询:

Hibernate: 
select distinct 
  affiliatep0_.id as id1_0_, 
  affiliatep0_.created_date_time as created_2_0_, 
  affiliatep0_.last_modified_date_time as last_mod3_0_, 
  affiliatep0_.deleted as deleted4_0_, 
  affiliatep0_.clicks_count as clicks_c5_0_, 
  affiliatep0_.coupons_count as coupons_6_0_, 
  affiliatep0_.description as descript7_0_, 
  affiliatep0_.merchants_count as merchant8_0_, 
  affiliatep0_.priority_merchants_count as priority9_0_, 
  affiliatep0_.priority_order as priorit10_0_, 
  affiliatep0_.title as title11_0_ 
from affiliate_programs affiliatep0_ 
  inner join affiliate_programs_statistics statistics1_ on affiliatep0_.id=statistics1_.affiliate_program_id 
  cross join affiliate_programs affiliatep2_ 
  where statistics1_.affiliate_program_id=affiliatep2_.id 
    and (statistics1_.date between ? and ?) 
    and affiliatep2_.deleted=? 
  order by affiliatep0_.title desc nulls last limit ?

如果我在控制台中 运行 它完美地工作并且只获取我需要的数据

JPA 从 2.1 开始支持 JPQL 中的 ON 子句,并且 QueryDSL 能够在查询中生成该 ON 子句。 Hibernate 有一个 ON 子句的先行者,其形式是现已弃用的 WITH 子句。 ON子句可以用在更多场合

只需在应应用它的连接后立即使用 .on(Predicate)

    final JPAQuery<AffiliateProgram> query = new JPAQuery<AffiliateProgram>(entityManager)
            .distinct()
            .from(affiliateProgram)
            .leftJoin(affiliateProgram.statistics, affiliateProgramStatistics)
            .on(AffiliateProgramsRepositoryHelper.getPredicates(form))
            .orderBy(AffiliateProgramsRepositoryHelper.getOrders(form.getSorting()))
            .limit(form.getLimit())
            .offset(form.getOffset());