Spring / Roo Custom Finder - PullRequest
       19

Spring / Roo Custom Finder

2 голосов
/ 20 октября 2011

Я хочу создать собственный искатель (будучи ветераном веб-разработки, но в то же время полным новичком в 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;
}

Так что теперь я хочу, чтобы запрос POST к этому URL принимал отправку формы поиска актива, которая содержит поля для нескольких ключевых свойств объекта Asset.

(Прямо сейчас я кодирую этот искатель в своем контроллере представления, и я знаю, что в конечном итоге я хочу перенести это в класс модели. Сейчас я просто работаю здесь для удобства.Кроме того, я знаю, что мне не нужно давать полностью определенное имя объекта, я по-новому назвал мой контроллер тем же именем, что и модель, с которой он разговаривает, так что пока я не разберусь с этим, я работаю над этим.)

Я позаимствовал некоторый код у некоторых создателей 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 моей строки запроса таким образом?Мне бы не хотелось много знать о полях, которые мне здесь дают, и я должен быть в состоянии справиться с этим, у меня в основном не будет всех из них.Поэтому я предпочел бы просто строить свой запрос итеративно, а не декларативно, если это имеет смысл.

Как вы могли бы построить здесь оператор выбора?

РЕДАКТИРОВАТЬ (Решение):

@ 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-инъекцией, но это здорово, что он (почти) не должен знатьчто-нибудь о данных, которые ему дают, что он просто соберет запрос из полей, которые ему дали.Нужно знать, с какими полями он работает, с какими полями "как" и "равно", как обращаться с полями "min" и "max", которые определяют диапазон, и т. Д. Но это не так уж плохо.

Ответы [ 3 ]

2 голосов
/ 21 октября 2011

Ну, по старинке ... Стандарт сервлетов в Java определяет объект Request, где есть 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 голосов
/ 25 октября 2011

Я сделал комментарий, но я предпочитаю объяснить свой подход в ответе:

Я думаю, что лучше создать собственный искатель, как сказал Ралох ниже (используя код, сгенерированный Roo), используя POJO какпараметр вместо WebRequest, чтобы не было зависимостей с MVC.

Попробуйте создать POJO, например, AssetFilter, содержащий атрибуты, которые вы хотите использовать в искателе.

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

Контроллер создаст фильтр POJO с Запросом, и выВы можете использовать этот искатель везде, вы даже можете создать интеграционный тест для него.

0 голосов
/ 20 октября 2011

Spring roo может генерировать искатели для вас:

type:

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

finder list перечислит все искатели с одним атрибутом, вы можете установить количество атрибутовс необязательным параметром

...