2017-06-19 2 views
1

J'ai donc une requête de base de données jpa qui fonctionne, et renvoie tous les SiteBmdfBusinessDay de cette table et les relie à leur SiteBmdf correspondant en joignant sur leurs colonnes ID. Ce que je veux faire, c'est, au lieu d'obtenir tous les SiteBmdfBusinessDay, je veux juste obtenir ceux avec le plus récent businessDay.JPA ne sélectionne que les dates les plus récentes par article

Voici la classe SiteBmdfBusinessDay ...

@Entity 
public class SiteBmdfBusinessDay extends BaseEntity { 

private static final long serialVersionUID = 1L; 

private Long id; 
private SiteBmdf siteBmdf; 
private DateTime businessDay; 
private Long failedCount; 
private DateTime lastSuccessfulBusinessDay; 
private DateTime lastSuccessfulFullBusinessDay; 
private Ticket ticket; 

@Id 
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "site_bmdf_business_day_site_bmdf_business_day_id_seq") 
@SequenceGenerator(name = "site_bmdf_business_day_site_bmdf_business_day_id_seq", sequenceName = "site_bmdf_business_day_site_bmdf_business_day_id_seq", allocationSize = 1) 
@Column(name = "site_bmdf_business_day_id") 
public Long getId() { 
    return id; 
} 

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

@ManyToOne 
@JoinColumn(name = "site_bmdf_id") 
public SiteBmdf getSiteBmdf() { 
    return siteBmdf; 
} 

public void setSiteBmdf(SiteBmdf siteBmdf) { 
    this.siteBmdf = siteBmdf; 
} 

@Type(type = "org.jadira.usertype.dateandtime.joda.PersistentDateTime", parameters = { 
     @Parameter(name = "databaseZone", value = "UTC"), @Parameter(name = "javaZone", value = "UTC") }) 
public DateTime getBusinessDay() { 
    return businessDay; 
} 

public void setBusinessDay(DateTime businessDay) { 
    this.businessDay = businessDay; 
} 

public Long getFailedCount() { 
    return failedCount; 
} 

public void setFailedCount(Long count) { 
    this.failedCount = count; 
} 

@Type(type = "org.jadira.usertype.dateandtime.joda.PersistentDateTime", parameters = { 
     @Parameter(name = "databaseZone", value = "UTC"), @Parameter(name = "javaZone", value = "UTC") }) 
public DateTime getLastSuccessfulBusinessDay() { 
    return lastSuccessfulBusinessDay; 
} 

public void setLastSuccessfulBusinessDay(DateTime lastSuccessfulBusinessDay) { 
    this.lastSuccessfulBusinessDay = lastSuccessfulBusinessDay; 
} 

@Type(type = "org.jadira.usertype.dateandtime.joda.PersistentDateTime", parameters = { 
     @Parameter(name = "databaseZone", value = "UTC"), @Parameter(name = "javaZone", value = "UTC") }) 
public DateTime getLastSuccessfulFullBusinessDay() { 
    return lastSuccessfulFullBusinessDay; 
} 

public void setLastSuccessfulFullBusinessDay(DateTime lastSuccessfulFullBusinessDay) { 
    this.lastSuccessfulFullBusinessDay = lastSuccessfulFullBusinessDay; 
} 

@ManyToOne(cascade = CascadeType.ALL, fetch = FetchType.EAGER) 
@JoinColumn(name = "ticket_id") 
public Ticket getTicket() { 
    return ticket; 
} 

public void setTicket(Ticket ticket) { 
    this.ticket = ticket; 
} 
} 

Et la classe SiteBmdf ...

@Entity 
public class SiteBmdf extends BaseEntity { 

private static final long serialVersionUID = 1L; 

private Long id; 
private String site; 
private Long failureCount; 
private Set<SiteBmdfBusinessDay> businessDays; 
private List<SiteBmdfNote> notes; 
private Resolution resolution; 

@Id 
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "site_bmdf_site_bmdf_id_seq") 
@SequenceGenerator(name = "site_bmdf_site_bmdf_id_seq", sequenceName = "site_bmdf_site_bmdf_id_seq", allocationSize = 1) 
@Column(name = "site_bmdf_id") 
public Long getId() { 
    return id; 
} 

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

public String getSite() { 
    return site; 
} 

public void setSite(String site) { 
    this.site = site; 
} 

@Transient 
public Long getFailureCount() { 
    return failureCount; 
} 

public void setFailureCount(Long failureCount) { 
    this.failureCount = failureCount; 
} 

@JsonIgnore 
@OneToMany(mappedBy = "siteBmdf", orphanRemoval = true, cascade = CascadeType.ALL, fetch = FetchType.EAGER) 
public Set<SiteBmdfBusinessDay> getBusinessDays() { 
    return this.businessDays; 
} 

public void setBusinessDays(Set<SiteBmdfBusinessDay> businessDays) { 
    this.businessDays = businessDays; 
} 

@OneToMany(mappedBy = "siteBmdf", orphanRemoval = true, cascade = CascadeType.ALL, fetch = FetchType.LAZY) 
public List<SiteBmdfNote> getNotes() { 
    Collections.sort(notes); 
    return notes; 
} 

public void setNotes(List<SiteBmdfNote> notes) { 
    this.notes = notes; 
} 

@ManyToOne 
@JoinColumn(name = "resolutionId") 
public Resolution getResolution() { 
    return resolution; 
} 

public void setResolution(Resolution resolution) { 
    this.resolution = resolution; 
} 
} 

Et voici la méthode de requête en cours ....

@Override 
public PageImpl<SiteBmdfBusinessDay> bmdfSitePaged(UiBuildCriteria criteria) { 

    CriteriaBuilder builder = entityManager.getCriteriaBuilder(); 
    CriteriaQuery<SiteBmdfBusinessDay> query = builder.createQuery(SiteBmdfBusinessDay.class); 
    Root<SiteBmdfBusinessDay> root = query.from(SiteBmdfBusinessDay.class); 

    if (!criteria.getSearch().getPredicateObject().isEmpty()) { 
     List<Predicate> predicates = predicateBuilderService.buildPredicates(builder, root, criteria.getSearch()); 
     query.where(builder.and(predicates.toArray(new Predicate[] {}))); 
    } 

    query.select(root); 

    builder.max(root.<Long>get("businessDay")); 

    // get the count for PageImpl 
    Long total = this.count(criteria); 

    // Set the order by 
    List<CustomSort> defaultSorts = new ArrayList<CustomSort>(
      Arrays.asList(
       new CustomSort("businessDay", Boolean.TRUE), 
       new CustomSort("siteBmdf.site", Boolean.FALSE) 
      )); 

    List<Order> orders = sortOrderBuilderService.buildSort(builder, root, criteria.getSort(), defaultSorts); 

    query.orderBy(orders); 

    List<SiteBmdfBusinessDay> content = entityManager.createQuery(query).setFirstResult(criteria.getPagination().getStart()) 
      .setMaxResults(criteria.getPagination().getNumber()).getResultList(); 

    return new PageImpl<>(content, criteria.getPageRequest(), total); 
} 

Donc, comme je l'ai dit, les résultats actuels sont juste tous les éléments de la table SiteBmdfBusinessDay avec leur site correspondant Bmdfs. Je voudrais seulement les SiteBmdfBusinessDays avec le plus récent businessDay pour chaque site.

Par exemple, si j'ai la table site_bmdf_business_day:

site_bmdf_business_day_id | site_bmdf_id | business_day 
1       | 1   | 6/1/2011 
2       | 2   | 6/1/2011 
3       | 1   | 6/6/2011 
4       | 3   | 6/6/2011 

J'voudrais seulement pour afficher:

site_bmdf_business_day_id | site_bmdf_id | business_day 
2       | 2   | 6/1/2011 
3       | 1   | 6/6/2011 
4       | 3   | 6/6/2011 

Je sais que je vais besoin de quelque chose le long des lignes de builder.max (racine .get ("businessDay")) mais je ne sais pas comment l'implémenter.

Toute aide ici serait grandement appréciée.

Notez que this question est similaire, mais pas exactement ce que je cherche car toutes les réponses sont en SQL et j'ai besoin de quelque chose dans jpa ici.

Répondre

1

Pour ce type de requêtes, je réduis généralement à sql (sans pagination et autres limitations), puis inverser la construire dans un querybuilder. Cela rend le code plus propre IMO. Qu'est-ce que vous cherchez est quelque chose comme

select a.* from SiteBmdfBusinessDay a 
inner join 
(select site_bmdf_id, max (business_day) as business_day 
    from SiteBmdfBusinessDay group by site_bmdf_id) b 
on (a.site_bmdf_id = b.site_bmdf_id) and (a.business_day = b.business_day) 

Cela suppose qu'il ya au plus une entrée par jour, par site_bmdf_id. Ceci est, pour autant que je sache, non faisable en tant que constructeur, cependant, vous pouvez mettre cela comme une requête de création et cela fonctionne très bien (parce que le résultat est juste une entité normale.) Cependant, c'est un très requête prohibitive de performance, en supposant que vous avez une quantité décente de données. Sauf si vous avez un petit ensemble de données, il est probablement préférable de tout prendre, puis filtrer par programme les doublons (que vous devriez pouvoir faire en temps O (n).)

+0

Bien que SQL fonctionne vraiment, alors merci vous pour ça! Cependant, je ne vais pas marquer cela comme résolu pour le moment, au cas où quelqu'un aurait une idée qui fonctionne comme un constructeur, ce dont j'ai vraiment besoin. La chose ici est que, bien que cet ensemble de données spécifique ne devienne pas trop grand, je voudrais utiliser cette implémentation dans des endroits où la table peut être proche de 1M lignes, donc je ne peux pas tirer tout l'ensemble et l'analyser. –

+0

Hélas, mon patron aime le résultat original de toute façon, donc je marque cela comme résolu! –