2012-09-24 56 views
1

我正在使用Tapestry5和Hibernate。我試圖建立一個使用從URL生成的動態限制的標準查詢。我的URL上下文被設計爲一個鍵/值對。使用URL鍵/值風格參數休眠查詢限制

www.mywebsite.com/make/ford/model/focus/year/2009 

我解碼參數,其次

private Map<String, String> queryParameters; 
private List<Vehicle> vehicles; 

    void onActivate(EventContext context) { 
      //Count is 6 - make/ford/model/focus/year/2009 
      int count = context.getCount(); 

      if (count > 0) { 
       int i; 
       for (i = 0; (i + 1) < count; i += 2) { 
        String name = context.get(String.class, i); 
        String value = context.get(String.class, i + 1); 

        example "make" 
        System.out.println("name " + name); 

        example "ford" 
        System.out.println("value " + value); 

        this.queryParameters.put(name, value); 
       } 
      } 

      this.vehicles = this.session.createCriteria(Vehicle.class) 
...add dynamic restrictions. 
     } 

我希望有人能幫助我弄清楚如何限制列表中動態地添加到我的查詢。我確信這已經完成了,所以如果有人知道一個帖子,這也會有幫助。謝謝

回答

1

我會假設你只是循環參數映射併爲每一對添加一個限制。

請注意,如果您不小心,這會爲您打開SQL注入攻擊。防止這種情況的最簡單方法是在添加到標準之前,根據已知的車輛屬性檢查鑰匙。

2

正如其他答案所說,但這裏更詳細地說明了。我認爲你的問題的癥結在於'告訴我如何添加限制'。無論如何,這是我的解釋。

您需要將每個限制解碼到它自己的字段中。

您需要知道每個字段的Java實體屬性名稱。

然後構建這兩個東西的Map,關鍵是已知的靜態Java實體屬性名稱,值是URL解碼數據(可能與類型轉換)。

private Map<String, Object> queryParameters; 
private List<Vehicle> vehicles; 

void onActivate(EventContext context) { 
     //Count is 6 - make/ford/model/focus/year/2009 
     int count = context.getCount(); 

     queryParameters = new HashMap<String,Object>(); 
     if (count > 0) { 
      int i; 
      for (i = 0; (i + 1) < count; i += 2) { 
       String name = context.get(String.class, i); 
       String value = context.get(String.class, i + 1); 

       Object sqlValue = value; 
       if("foobar".equals(name)) { 
        // sometime you don't want a String type for SQL compasition 
        // so convert it 
        sqlValue = UtilityClass.doTypeConversionForFoobar(value); 
       } else if("search".equals(name) || 
          "model".equals(name) || 
          "year".equals(name)) { 
        // no-op this is valid 'name' 
       } else if("make".equals(name)) { 
        // this is a suggestion depends on your project conf 
        name = "vehicleMake.name"; 
       } else { 
        continue; // ignore values we did not expect 
       } 
       // FIXME: You should validate all 'name' values 
       // to be valid and/or convert to Java property names here 

       System.out.println("name " + name); 
       System.out.println("value " + value); 

       this.queryParameters.put(name, sqlValue); 
      } 
     } 

     Criteria crit = this.session.createCriteria(Vehicle.class) 
     for(Map.Entry<String,Object> e : this.queryParameters.entrySet()) { 
      String n = e.getKey(); 
      Object v = e.getValue(); 
      // Sometimes you don't want a direct compare 'Restructions.eq()' 
      if("search".equals(n)) 
       crit.add(Restrictions.like(n, "%" + v + "%")); 
      else // Most of the time you do 
       crit.add(Restrictions.eq(n, v)); 
     } 

     this.vehicles = crit.list(); // run query 
    } 

https://docs.jboss.org/hibernate/orm/3.5/reference/en/html/querycriteria.html

看到上面不應該有SQL注入的風險,因爲「名」和「N」的部分應該是100%與已知良好列表驗證。 「value」和「v」正確轉義,就像使用SQL位置佔位符'?'一樣。

Ë& OE

+0

這似乎正是我所期待的。我還有一個問題。在我的示例url中,我使用「make」作爲名稱,但實際上它不是我實體中的名稱。它實際上是「vehicleMake.name」。我想保持URL參數乾淨,只是「make」,我想知道是否應該在代碼中使用make,也許在枚舉或其他東西中引用vehicleMake.name。我不確定如何做到這一點。非常感謝你的幫助。 –

+0

編輯的想法嘗試'vehicleName.name' ..也更正使用'crit.list()'調用 –

1

另一種選擇是從名稱/值對建立一個對象來創建一個示例查詢:

Vehicle vehicle = new Vehicle(); 
int count = context.getCount(); 
int i; 
for (i = 0; (i + 1) < count; i += 2) { 
    String name = context.get(String.class, i); 
    String value = context.get(String.class, i + 1); 
    // This will call the setter for the name, passing the value 
    // So if name is 'make' and value is 'ford', it will call vehicle.setMake('ford') 
    BeantUtils.setProperty(vehicle, name, value); 
} 

// This is using a Hibernate example query: 
vehicles = session.createCriteria(Vehicle.class).add(Example.create(vehicle)).list(); 

更多信息,請參見BeanUtils.setPropertyExample Queries

這假定您只允許每個屬性一個值,並且查詢參數正確映射到屬性名稱。可能還有轉換問題需要考慮,但我認爲setProperty處理常見問題。

+0

你可以在你的例子中顯示更多,我有點困惑與Beantutils和例如發生了什麼。創建。感謝Brian。 –

+0

添加了一些評論和鏈接,以幫助更清楚地說明。 –

1

如果它們是查詢參數,則應將它們視爲查詢參數而不是路徑參數。你的網址應該是這樣的:

www.mywebsite.com/vehicles?使=福特&模型=專注&年= 2009

,你的代碼應該是這個樣子:

public class Vehicles { 

@ActivationRequestParameter 
private String make; 

@ActivationRequestParameter 
private String model; 

@ActivationRequestParameter 
private String year; 

@Inject 
private Session session; 

@OnEvent(EventConstants.ACTIVATE) 
void activate() { 
    Criteria criteria = session.createCriteria(Vehicle.class); 

    if (make != null) criteria.add(Restrictions.eq("make", make)); 
    if (model != null) criteria.add(Restrictions.eq("model", model)); 
    if (year != null) criteria.add(Restrictions.eq("year", year)); 

    vehicles = criteria.list(); 
} 

}

假設你正在使用的網格組件來顯示車輛我會強烈建議使用HibernateGridDataSource而不是在「activate」事件處理程序中進行查詢。

public class Vehicles { 

    @ActivationRequestParameter 
    private String make; 

    @ActivationRequestParameter 
    private String model; 

    @ActivationRequestParameter 
    private String year; 

    @Inject 
    private Session session; 

    @OnEvent(EventConstants.ACTIVATE) 
    void activate() { 
    } 

    public GridDataSource getVehicles() { 
     return new HibernateGridDataSource(session, Vehicles.class) { 
      @Override 
      protected void applyAdditionalConstraints(Criteria criteria) { 
       if (make != null) criteria.add(Restrictions.eq("make", make)); 
       if (model != null) criteria.add(Restrictions.eq("model", model)); 
       if (year != null) criteria.add(Restrictions.eq("year", year)); 
      } 
     }; 
    } 
} 
+0

我想首先感謝這個優秀的例子!我想使用路徑參數與查詢參數相反的原因純粹是出於搜索引擎優化的原因。我總是認爲乾淨的URL減號符號更適合搜索引擎優化,但我可能是錯誤的,因爲沒有人真的知道谷歌算法。也許你可以對此發表評論。你也可以解釋HibernateGridDataSource?這是我見過的第一個。非常感謝。 –

+0

Grid組件的「source」參數通常是一個List或一個數組,但它也可以帶一個GridDataSource。 HibernateGridDataSource將爲您處理排序和分頁,並且它將從數據庫中僅引入將要顯示的元素。它比「criteria.list();」更有效率,特別適用於大型數據集。 http://tapestry.apache.org/current/apidocs/org/apache/tapestry5/corelib/components/Grid.html – ascandroli