2010-11-07 91 views
0

我使用OpenJPA 1.2.x 我想創建漂亮的過濾功能。現在看起來很糟糕。也許有人有過練習,如果是的話,請建議它如何看起來更好。 這個想法是:我有一個實體事件。此實體有幾種類型(EventType,CommunicationType等等)。事件可以使用這些類型和日期範圍進行過濾。 問題是每個新的過濾標準都會導致方法中出現新的spagetti。證據消失,方法將變得難以維繫。JPQL多個連接,同時動態查詢建立

需要最佳實踐,請建議。 請參閱我的通心粉:

@Override 
@SuppressWarnings("unchecked") 
public List<Event> listEvents(Filter filter){ 
    List<Event> dayEvents = null; 

    StringBuilder sbQuery = new StringBuilder(128);  //dynamic query building 
    StringBuilder sbQueryJoins = new StringBuilder(128);  //joins of nested entities 
    HashMap<String, Object> queryParams = new HashMap<String, Object>(); //keeps namedParam=>value 
    boolean hasCondition = false;   //has any condition or not 

    /** replace #->JOINS<-# before creating JPQL Query object. */ 
    sbQuery.append("select evt from Event evt #->JOINS<-# where "); 

    /** 
    * Set date period. 
    * Attention, {@link Filter#getTimePeriod()} can't be null. 
    * */ 
    if(filter.getSelectedDate()!=null){ 
    DateUtil.Period period = new DateUtil.Period(filter.getTimePeriod(), filter.getSelectedDate()); 
    queryParams.put("startOfPeriod",period.getBegin()); 
    queryParams.put("endOfPeriod", period.getEnd()); 
    sbQuery.append(" (evt.beginDate >= :startOfPeriod and evt.beginDate <= :endOfPeriod) "); 
    hasCondition = true; 
    } 

    if(filter.getDepartmentId()!=null){ 
    throw new MethodNotImplementedException("Filtering on Event.departments is not implemented yet. Do not provide Deparments as filter parameters"); 
    } 

    if(filter.getEventCommunicationId()!=null && filter.getEventCommunicationId()!= Filter.ALL_EVENTS_OF_TYPE){ 
    if(hasCondition){ 
    sbQuery.append(" and "); 
    } 
    sbQueryJoins.append(" JOIN evt.eventCommunication evtCommunucation "); 
    sbQuery.append(" evtCommunucation.id = :eventCommunicationId "); 
    queryParams.put("eventCommunicationId", filter.getEventCommunicationId()); 
    hasCondition = true; 
    } 

    if(filter.getEventImportanceId()!=null && filter.getEventImportanceId()!= Filter.ALL_EVENTS_OF_TYPE){ 
    if(hasCondition){ 
    sbQuery.append(" and "); 
    } 
    sbQueryJoins.append(" JOIN evt.eventImportance evtImportance "); 
    sbQuery.append(" evtImportance.id = :eventImportanceId "); 
    queryParams.put("eventImportanceId", filter.getEventImportanceId()); 
    hasCondition = true; 
    } 

    if(filter.getEventTypeId()!=null && filter.getEventTypeId()!= Filter.ALL_EVENTS_OF_TYPE){ 
    if(hasCondition){ 
    sbQuery.append(" and "); 
    } 
    sbQueryJoins.append(" JOIN evt.eventType evtType "); 
    sbQuery.append(" evtType.id = :eventTypeId "); 
    queryParams.put("eventTypeId", filter.getEventTypeId()); 
    hasCondition = true; 
    } 

    // ordering 
    sbQuery.append(" order by evt.beginDate asc "); 

    sbQuery.replace(sbQuery.indexOf("#->"), sbQuery.indexOf("<-#")+3, sbQueryJoins.toString()); 
    LOG.trace("#listEvents -> Trying to execute JPQL query [{}] with params[{}]:",sbQuery, queryParams); 

    Query query = em.createQuery(sbQuery.toString()); 
    for(Map.Entry<String, Object> entry : queryParams.entrySet()){ 
    query.setParameter(entry.getKey(), entry.getValue()); 
    } 

    dayEvents = query.getResultList(); 
    return dayEvents; 
} 

回答

1

使用QueryDSL;既然你不使用JPA2,你需要一個專有的API。 QueryDSL也比JPA2標準產生更清潔的動態查詢,並且可以用於任何JPA實現。

0

不幸的是,沒有標準的JPA 1.0大有取代容易出錯的字符串連接方法。隨着JPA 2.0,一會通常有利於動態查詢但JPA 1.0的標準API超過JPQL,你就必須要麼:

  • 使用字符串連接
  • 使用您的供應商的專有API(如Hibernate的標準API,雖然我無法找到任何的OpenJPA直接當量)
  • 升級到JPA 2.0
+0

1.這是一個直接的方式來分割地獄:) – Sergey 2010-11-09 10:47:12

+0

哎呀,對不起,雙重職位。 2.不能使用專有,不能升級:( – Sergey 2010-11-09 10:48:01

+0

@Sergey:是的,1.不會給你高度可維護的代碼。你需要一些番茄醬嗎? – 2010-11-09 11:17:24

0

你應該看看Criteria4JPA。它適用於任何JPA版本,並且與所有實現兼容。它提供了以面向對象的方式構建接近類型安全的查詢的可能性。看看在examples像這樣的:

Criteria criteria = CriteriaUtils.createCriteria(entityManager, Person.class); 
criteria.add(Restrictions.between("age", 18, 30)); 
criteria.add(Restrictions.disjunction() 
    .add(Restrictions.eq("firstname", "Bill")) 
    .add(Restrictions.eq("firstname", "Christian")) 
); 
List<Person> result = criteria.list(); 

正如你看到的,你可以根據提供給您的方法過濾器上動態創建查詢。我們在大多數應用程序中使用Criteria4JPA來實現此類動態數據庫查詢操作。

+1

是否可以不必把字符串名稱你的字段在這個查詢中?我想要任何查詢是可重構的,如果擁有,並且與JPA2 Criteria,QueryDSL,LiQuidform – user383680 2010-11-08 11:48:57

+0

否,目前不可能。Criteria4JPA是Hibernate Criteria API的一個克隆,因此不支持這種類型100%的類型安全查詢。 – chkal 2010-11-08 15:31:30