【问题标题】:Spring/roo custom finderSpring/roo 自定义查找器
【发布时间】:2011-10-20 15:32:20
【问题描述】:

我想创建一个自定义查找器(我是一名 Web 开发老手,但完全是 Java、Spring 和 Roo 的初学者)。

下面的第一个方法来自我的“资产”视图控制器类。这是您第一次访问资产管理页面时发生的事情——它返回一个视图和一批当前活动的资产。它工作正常:

@RequestMapping("/assets")
public ModelAndView listAssets() {
    ModelAndView mav = new ModelAndView();
    mav.setViewName("admin/asset");
    Collection<com.myapp.model.Asset> assets = com.myapp.model.Asset.findAllAssets() ;
    mav.addObject("assets", assets);    
    return mav;
}

所以现在我想让该 URL 的“POST”请求接受资产搜索表单的提交,其中包含资产对象的几个关键属性的字段。

(现在我正在我的视图控制器中编写那个查找器,我知道最终我想把它推到模型类中。现在我只是为了方便起见在这里工作。另外,我知道我不必给出对象的完全限定名称,我刚刚将我的控制器命名为与它正在与之交谈的模型相同的名称,所以在我解决这个问题之前,我正在解决它。)

我从我生成的一些 Roo 查找器中借用了一些代码,最终得到:

@RequestMapping(value = "/assets", method = RequestMethod.POST)
public ModelAndView searchAssets(
            @RequestParam("category") String category,
            @RequestParam("year") String year,
            @RequestParam("manufacturer") String manufacturer,
            @RequestParam("drive_type") String driveType,
            @RequestParam("subcategory") String subcategory,
            @RequestParam("serial") String serial,
            @RequestParam("listing_type") String listingType,
            @RequestParam("hours") String hours,
            @RequestParam("model") String model,
            @RequestParam("mileage") String mileage  ) {
    ModelAndView mav = new ModelAndView();
    mav.setViewName("admin/asset");


    EntityManager em = com.myapp.model.Asset.entityManager();
    TypedQuery<Asset> q = em.createQuery("SELECT o FROM Asset AS o ", Asset.class);
    Collection<Asset> assets =  q.getResultList();
    mav.addObject("assets", assets);

    return mav;
}

所以问题是:

1) 有没有办法获取我的参数集合,而不是将它们烘焙到方法签名中?因为我有点讨厌那样。

2) 有没有办法迭代我的参数并以这种方式生成我的查询字符串的 WHERE 子句?我不想对我在这里获得的领域了解太多——而且我需要能够处理我大部分情况下不会拥有所有这些领域。因此,如果有意义的话,我宁愿以迭代方式而不是声明方式构建我的查询。

您将如何在此处构建 select 语句?

编辑(解决方案):

@madth3 在这里为我指明了正确的方向。这是我最终得到的结果,我对此感到非常自豪:

public static TypedQuery<Asset> findAssetsByWebRequest( WebRequest request) {

    ArrayList<String> wheres = new ArrayList<String>();

    Iterator<String> i = request.getParameterNames();

    while (i.hasNext()) {
        String fieldname = i.next();
        String value = request.getParameter(fieldname);

        if (value.length() == 0) {
            continue;
        }

        if (fieldname.equals("manufacturer") || fieldname.equals("model") ) {

            value = value.replace('*', '%');
            if (value.charAt(0) != '%') {
                value = "%" + value;
            }
            if (value.charAt(value.length() - 1) != '%') {
                value = value + "%";
            }
            wheres.add(" o." + fieldname + " LIKE '" + value + "'");
        }
        else if (fieldname.contains("min")) {
            fieldname = fieldname.replace("min", "").toLowerCase();
            wheres.add(" o." + fieldname + " >= '" + value + "' ");
        }
        else if (fieldname.contains("max")) {
            fieldname = fieldname.replace("max", "").toLowerCase();
            wheres.add(" o." + fieldname + " <= '" + value + "' ");

        }
        else {
            wheres.add(" o." + fieldname + " = '" + value + "' ");
        }
    }


    String query = "SELECT o FROM Asset AS o ";
    if (wheres.size() > 0) {
        query += " WHERE ";
        for (String clause: wheres) {
            query += clause + " AND "; 
        }
        query += " 1 = 1 ";
    }


    EntityManager em = Asset.entityManager();
    TypedQuery<Asset> q = em.createQuery(query, Asset.class);

    return q;
}

显然需要对其进行参数化以避免 SQL 注入攻击,但它(几乎)不需要知道任何有关提供给它的数据的信息,它只会从它所在的字段中组装一个查询,这很酷给定的。它确实需要知道它在处理哪些字段——哪些字段是“like”与“equals”,如何处理定义范围的“min”和“max”字段等。但这还不错。

【问题讨论】:

  • 我认为最好使用 POJO 作为参数而不是 WebRequest,如下所述(使用 Roo 生成的代码)创建自定义查找器,以免与 MVC 有依赖关系。跨度>

标签: java spring spring-mvc spring-roo


【解决方案1】:

好吧,走老路…… Java 中的 Servlets 标准定义了对象请求,其中存在带有参数的 Map(哈希、字典),因此您可以访问它们,例如

public ModelAndView searchAssets(HttpRequest request) {
    StringBuilder builder = new StringBuilder();
    for (String param : request.getParameterNames()) {
        value = request.getParameter(param);
        builder.append(param);
        builder.append("="); // or LIKE
        builder.append("\'" + value "\'");
        builder.append(" AND ");
    }
    // deleting the last " AND "
    builder.delete(builder.length-5, builder.length);
    // In the entity create a method that executes a query with the string
    // If the parameter names are column names then you'd have to use a nativeQuery
    // You'd have to look it up in JPA
   List<Asset> list = Asset.search(builder.toString());
   // put the list in the ModelAndView
}

【讨论】:

  • 不错。我今天会试试看。
【解决方案2】:

我发表了评论,但我更愿意在回答中解释我的方法:

我认为最好使用 POJO 作为参数而不是 WebRequest 来创建一个自定义查找器,正如 Raloh 下面所说的(使用 Roo 生成的代码),以便不依赖于 MVC。

尝试创建一个 POJO,例如 AssetFilter,其中包含您要在查找器中使用的属性。

public static TypedQuery<Asset> findAssetsByFilter(AssetFilter filter) {
    If (filter.getManufacturer()!=null) where.append(" AND ...")
}

Controller 将使用 Request 创建 POJO 过滤器,您可以在任何地方使用此查找器,甚至可以为其创建集成测试。

【讨论】:

    【解决方案3】:

    Spring roo 可以为你生成查找器:

    类型:

    finder list --class ~.domain.MyClass   
    finder add --finderName findMyClassByWhatEver
    

    finder list 将列出所有具有一个属性的查找器,您可以使用可选参数设置属性的数量

    【讨论】:

    • 没错,但它生成的查找器需要每个字段。如果您将它们推入模型并删除验证,它们会在尝试访问不存在的字段时死亡。一种或另一种方式,看起来需要一些自定义代码。我对此并不特别担心,只是用一种我不懂的语言,所以......
    • 好的,我明白了。但是您可以查看生成的代码并将其用作您需要实现的蓝图。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-01-05
    相关资源
    最近更新 更多