简体   繁体   English

如何使JPQL加入不能使结果翻倍

[英]How to make JPQL joins not double up results

I have a few entities for quoting items, using JPA/JPQL and Hibernate. 我有一些使用JPA / JPQL和Hibernate引用项目的实体。

A quote has many quote items, and each item can have 0 or more adjustments. 一个报价有很多报价项目,每个项目可以有0个或多个调整项。 Each adjustment is either a discount or surcharge, and either percentage (ie percentage of the price of the item) or amount (ie a fixed value) 每种调整都是折扣或附加费,或者是百分比(即商品价格的百分比)或金额(即固定值)

I'm trying to write a JPQL query that will give me the value of all quotes for a given region and user and time period, adding together the price of each quote item for the quote and the value of any adjustments. 我正在尝试编写一个JPQL查询,该查询将为我提供给定地区,用户和时间段的所有报价的值,并将报价的每个报价项目的价格与任何调整的值相加。 I nearly have it working but it's not quite right. 我差一点就可以了,但这不是很正确。

String query = "select q.user, q.contact.parent.region, sum( case " +
        " when a.type = :type_surcharge and a.amountType = :amount_type_percentage then ( a.amount * qi.price ) " +
        " when a.type = :type_surcharge and a.amountType = :amount_type_amount then ( a.amount ) " +
        " when a.type = :type_discount and a.amountType = :amount_type_percentage then ( -1 * a.amount * qi.price ) " +
        " else ( -1 * a.amount ) end ) + sum(qi.price) " +
        " from " + Quote.class.getName() + " q " +
        " left join q.quoteItems qi " +
        " left join qi.adjustments a " +
        " where q.date between :from and :to and q.contact.parent.region in :regions group by q.user, q.contact.parent.region";

The problem is that if a quote item has more than one adjustment, then that item appears multiple times in the sum(qi.price) calculation 问题是,如果报价项目具有多个调整项,则该项目在sum(qi.price)计算中会多次出现

Can anyone help me with a fix to this query to only include each quote item once in the sum? 谁能帮我解决此查询,使每个报价项只包括一次? ( sum( distinct qi.price ) doesn't work) I'm hoping it can be done in a single query if possible. sum( distinct qi.price )不起作用)我希望可以在单个查询中完成。

Edit: It's more wrong than I thought - the query returns null for the summed value if there are quotes present without adjustments, even though I am using left join (?). 编辑:这比我想象的要错误的多-即使存在使用左联接(?)的查询,如果存在没有调整的引号,查询的总和将返回null。 The only time the data is correct is if: - there is either 0 or 1 adjustments for an item - there is an adjustment for at least one item in all the quotes that pass the region/user/period filters. 唯一正确的数据是:-对一项进行0或1项调整-通过区域/用户/期间过滤器的所有报价中至少对一项进行调整。

Entities (simplified) 实体(简体)

@Entity
@DiscriminatorValue(AbstractCollateral.TYPE_QUOTE)
@Table(name="customer_quote", uniqueConstraints={@UniqueConstraint(name="quote_number_uk", columnNames={"number"})})
public class Quote extends AbstractCollateral<QuotedCarePack>
{
  ... other elements

  @OneToMany(fetch = FetchType.EAGER, mappedBy = "parent", cascade = CascadeType.ALL, orphanRemoval=true)
  protected Set<QuotedCarePack> quoteItems;

}

@Entity
@Inheritance(strategy=InheritanceType.JOINED)
@DiscriminatorColumn(name="collateral_type", discriminatorType=DiscriminatorType.STRING, length=10)
@Table(name="abstract_collateral")
public abstract class AbstractCollateral<T extends AbstractCollateralItem<?>> extends AbstractManagedData implements ContactInfo{

  private static final long serialVersionUID = 1L;

  public static final String TYPE_ORDER = "order";
  public static final String TYPE_QUOTE = "quote";

  @Column(name="collateral_type", insertable=true, updatable=false, length=10)
  protected String collateralType;

  @ManyToOne(fetch=FetchType.EAGER)
  @JoinColumn(name="customer_contact_id", nullable=false)
  protected CustomerContact contact;

  @ManyToOne(fetch=FetchType.EAGER)
  @JoinColumn(name="user_id", nullable=false)
  protected User user;
  /**
   * The date the collateral was made (not the sent date, necessarily)
   */
  @Column(name="creation_date", nullable=false)
  @Temporal(TemporalType.TIMESTAMP)
  protected Date date;
}

@Entity(name="quoted_care_pack")
public class QuotedCarePack extends AbstractCollateralItem<Quote> {

  private static final long serialVersionUID = 1L;

  @ManyToOne(fetch=FetchType.EAGER)
  @JoinColumn(name="quote_id", nullable=false)
  protected Quote parent;

  @OneToMany(fetch=FetchType.EAGER, cascade={CascadeType.PERSIST, CascadeType.MERGE})
  @JoinTable
  (
      name="quoted_care_pack_adjustment",
      joinColumns={ @JoinColumn(name="quoted_care_pack_id", referencedColumnName="id") },
      inverseJoinColumns={ @JoinColumn(name="adjustment_id", referencedColumnName="id", unique=true) }
  )
  protected Set<Adjustment> adjustments;
}

@Entity(name="adjustment")
public class Adjustment extends AbstractManagedData {

  private static final long serialVersionUID = 1L;

  @Column(name="amount")
  protected double amount;
  @Column(name="reason", length=100)
  protected String reason;
  @Column(name="type")
  @Enumerated(EnumType.STRING)
  protected AdjustmentType type;
  @Column(name="amount_type")
  @Enumerated(EnumType.STRING)
  protected AdjustmentAmountType amountType;
}

Unfortunately, JPA does not support subqueries in the select clause, which is what you need to do so that the joins to obtain the sum of all adjustments doesn't affect the sum of quoteItems. 不幸的是,JPA不支持select子句中的子查询,这是您需要执行的操作,以使获得所有调整总和的联接不会影响quoteItems的总和。

This leaves using native SQL, or executing two queries - one for the qi.price sum and another for the adjustments sum: 这样就可以使用本机SQL,或者执行两个查询-一个用于qi.price和,另一个用于调整和:

"select q.user, q.contact.parent.region, sum(qi.price) " +
        " from " + Quote.class.getName() + " q " +
        " left join q.quoteItems qi " +
        " where q.date between :from and :to and q.contact.parent.region in :regions group by q.user, q.contact.parent.region";

and

"select q.user, q.contact.parent.region, sum( case " +
        " when a.type = :type_surcharge and a.amountType = :amount_type_percentage then ( a.amount * qi.price ) " +
        " when a.type = :type_surcharge and a.amountType = :amount_type_amount then ( a.amount ) " +
        " when a.type = :type_discount and a.amountType = :amount_type_percentage then ( -1 * a.amount * qi.price ) " +
        " else ( -1 * a.amount ) end ) " +
        " from " + Quote.class.getName() + " q " +
        " left join q.quoteItems qi " +
        " left join qi.adjustments a " +
        " where q.date between :from and :to and q.contact.parent.region in :regions group by q.user, q.contact.parent.region";

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM