2010-11-07 93 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