如何在 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());
我有以下两个实体:
@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());