2013-11-21 38 views
10

是否可以通過引用實體分組來選擇JPA中的數據?JPA GROUP BY實體 - 這可能嗎?

我的意思是:我有兩個實體 - 保險和參考多對一的車輛。保險實體有validitill字段(當然是車輛領域)。

我想選擇車輛,這是最新的保險。下面的查詢不起作用:

SELECT DISTINCT v.vehicle, 
       max(v.validTill) as lastValidTill 
FROM TraInsurance v 
    GROUP BY v.vehicle 
    ORDER BY lastValidTill 

上面的查詢失敗,錯誤:

ERROR: column "travehicle1_.id_brand" must appear in the GROUP BY clause or be used in an aggregate function 

這是因爲JPA將從參考車輛的所有字段進行查詢,而不是GROUP BY。這裏是我做錯了什麼嗎?或者也許這不可能做到這一點?

編輯:

TraInsurance實體

@Entity 
@Table(name = "TRA_INSURANCES", schema="public") 
@SequenceGenerator(name = "TRA_INSURANCES_SEQ", sequenceName = "TRA_INSURANCES_SEQ", allocationSize = 1) 
public class TraInsurance implements EntityInt, Serializable { 

    private static final long serialVersionUID = 1L; 

    @Id 
    @Column(name = "id", nullable = false) 
    @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "TRA_INSURANCES_SEQ") 
    private Long     id; 

    @NotNull 
    @ManyToOne 
    @JoinColumn(nullable = false, name = "id_vehicle") 
    private TraVehicle    vehicle; 

    @NotNull 
    @Column(name = "valid_from", nullable = false) 
    private Date     validFrom; 

    @Column(name = "valid_till", nullable = false) 
    private Date     validTill; 

    @NotNull 
    @ManyToOne 
    @JoinColumn(nullable = false, name = "id_company") 
    private Company     company; 

    @Column(name = "policy_no", nullable = true, length = 50) 
    private String     policyNumber; 

    @Column(name = "rate", nullable = true, precision = 12, scale = 2) 
    private BigDecimal    rate; 

    @Column(name = "discount_percent", nullable = true) 
    private Float     discountPercent; 

    @Column(nullable = true) 
    private String     description;  

    public TraInsurance() {} 

    public Long getId() { 
     return id; 
    } 

    public void setId(Long id) { 
     this.id = id; 
    } 

    public TraVehicle getVehicle() { 
     return vehicle; 
    } 

    public void setVehicle(TraVehicle vehicle) { 
     this.vehicle = vehicle; 
    } 

    public Date getValidFrom() { 
     return validFrom; 
    } 

    public void setValidFrom(Date validFrom) { 
     this.validFrom = validFrom; 
    } 

    public Date getValidTill() { 
     return validTill; 
    } 

    public void setValidTill(Date validTill) { 
     this.validTill = validTill; 
    } 

    public Company getCompany() { 
     return company; 
    } 

    public void setCompany(Company company) { 
     this.company = company; 
    } 

    public String getPolicyNumber() { 
     return policyNumber; 
    } 

    public void setPolicyNumber(String policyNumber) { 
     this.policyNumber = policyNumber; 
    } 

    public BigDecimal getRate() { 
     return rate; 
    } 

    public void setRate(BigDecimal rate) { 
     this.rate = rate; 
    } 

    public Float getDiscountPercent() { 
     return discountPercent; 
    } 

    public void setDiscountPercent(Float discountPercent) { 
     this.discountPercent = discountPercent; 
    } 

    public String getDescription() { 
     return description; 
    } 

    public void setDescription(String description) { 
     this.description = description; 
    } 

    @Override 
    public int hashCode() { 
     final int prime = 31; 
     int result = 1; 
     result = prime * result + ((id == null) ? 0 : id.hashCode()); 
     result = prime * result 
       + ((validFrom == null) ? 0 : validFrom.hashCode()); 
     result = prime * result + ((vehicle == null) ? 0 : vehicle.hashCode()); 
     return result; 
    } 

    @Override 
    public boolean equals(Object obj) { 
     if (this == obj) 
      return true; 
     if (obj == null) 
      return false; 
     if (!(obj instanceof TraInsurance)) 
      return false; 
     TraInsurance other = (TraInsurance) obj; 
     if (id == null) { 
      if (other.id != null) 
       return false; 
     } else if (!id.equals(other.id)) 
      return false; 
     if (validFrom == null) { 
      if (other.validFrom != null) 
       return false; 
     } else if (!validFrom.equals(other.validFrom)) 
      return false; 
     if (vehicle == null) { 
      if (other.vehicle != null) 
       return false; 
     } else if (!vehicle.equals(other.vehicle)) 
      return false; 
     return true; 
    } 

} 

回答

6

請明確使用在這種情況下,使用JOIN

SELECT ve, MAX(v.validTill) 
FROM TraInsurance v JOIN v.vehicle ve 
GROUP BY ve 
ORDER BY MAX(v.validTill) 
1

如果你通過了GROUP BY內的實體,Hibernate會自動將它的ID到底層數據庫的SQL轉換。另外,GROUP BY中的值必須存在於SELECT子句中。因此,可以不選擇整個對象,而選擇其ID,然後從那些ID中,可以再次檢索該對象。

SELECT DISTINCT v.vehicle.id, max(v.validTill) 
FROM TraInsurance v 
GROUP BY v.vehicle.id 
ORDER BY max(v.validTill) 

如果是需要時間和DB命中檢索車輛從它們的ID對象,你可以選擇所有的在SELECT 的車輛屬性,並把他們在GROUP BY。然後,您可以從這些屬性構造對象而不訪問數據庫。

+0

是的,我知道 - 那樣,這是可能的。我只是想知道 - 如果有可能通過select by group by來檢索整個實體,謝謝 – robson

+0

當然,這是可能:),只選擇它:'SELECT DISTINCT v FROM ....' –

+0

@AndreiI我的意思是 - 車輛實體,而不是保險。我在頂部給出的例子並不像我在那裏描述的那樣工作。 – robson

3

顯然JPA規範允許但至少Hibernate的實現不支持它(見HHH-2436HHH-1615)。

+0

解釋我的問題,謝謝 – robson