2011-10-20 5 views
2

Je souhaite créer un moteur de recherche personnalisé (en tant que vétéran du développement web, mais en tant que débutant complet sur Java, Spring, AND Roo).Recherche personnalisée de printemps/roo

La première méthode ci-dessous provient de ma classe de contrôleurs de vue "Asset". C'est ce qui se passe lors de votre premier accès à la page d'administration des actifs - il revient avec une vue et un lot des actifs actuellement actifs. Il fonctionne très bien:

@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; 
} 

Alors maintenant, je veux avoir une demande « POST » à cette URL accepter la soumission du formulaire de recherche d'actifs, qui contient des champs pour plusieurs des principales propriétés de l'objet actif.

(À l'heure actuelle, je suis en train de coder ce finder dans mon contrôleur de vue, et je sais finalement que je vais vouloir le faire descendre à la classe de modèle pour l'instant je ne fais que travailler par commodité. Je sais que je n'ai pas besoin de donner le nom complet de l'objet, j'ai appelé mon contrôleur avec le même nom que le modèle auquel il parle, donc jusqu'à ce que je sorte ça, je travaille autour.)

J'ai emprunté un code de quelques trouveurs Roo I généré, et a fini avec:

@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; 
} 

Ainsi, les questions sont les suivantes:

1) Existe-t-il un moyen d'obtenir une collection de mes paramètres plutôt que de les incorporer dans la signature de la méthode? Parce que je déteste ça.

2) Y a-t-il un moyen d'itérer sur mes paramètres et de générer la clause WHERE de ma chaîne de requête de cette façon? Je voudrais ne pas avoir à en savoir beaucoup sur les domaines qui me sont donnés ici - et je dois être capable de gérer que je ne les aurai pas tous. Je préfère donc construire ma requête de manière itérative plutôt que déclarative, si cela a du sens.

Comment allez-vous construire l'instruction select ici?

EDIT (Solution):

@ madth3 m'a orienté dans la bonne direction ici. Voici ce que j'ai fini avec, que je suis assez fier:

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; 
} 

Il doit évidemment être paramétrés pour éviter les attaques par injection SQL, mais il est cool qu'il (presque) ne pas savoir quoi que ce soit au sujet les données qui lui sont données, qu'il va juste assembler une requête à partir des champs qui lui ont été donnés. Il a besoin de savoir dans quels champs il fait quoi - avec quels champs sont "like" ou "equal", comment gérer les champs "min" et "max" qui définissent une plage, etc. Mais ce n'est pas si grave.

+0

Je pense qu'il est préférable de créer un finder personnalisé comme Raloh dit ci-dessous (en utilisant le code généré par Roo) en utilisant un POJO comme paramètre au lieu de WebRequest, afin de ne pas avoir de dépendances avec le MVC. – jbbarquero

Répondre

2

Eh bien, aller à l'ancienne ... La norme Servlets Java définit l'objet demande où est la carte (hachage, dictionnaire) avec les paramètres, vous pouvez donc y accéder quelque chose comme

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 
} 
+0

Nice. Je vais essayer ça aujourd'hui. –

0

roo Spring peut générer les viseurs pour vous:

Type:

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

finder list listera tous finder avec un attribut, vous pouvez définir le nombre d'attributs avec un paramètre optionnel

+0

C'est vrai, mais les trouveurs qu'il génère requièrent tous les champs. Et si vous les insérez dans votre modèle et supprimez la validation, ils meurent sur les tentatives d'accès aux champs qui ne sont pas là. D'une manière ou d'une autre, il semble qu'un code personnalisé soit nécessaire. Je ne suis pas troublé par ça en particulier, c'est juste dans une langue que je ne connais pas, alors ... –

+0

Ok, je vois. Mais vous pouvez jeter un coup d'oeil sur le code généré et l'utiliser comme une impression bleue pour ce que vous devez mettre en œuvre. – Ralph

+0

Oh très certainement. –

2

J'ai fait un commentaire, mais je préfère expliquer mon approche dans un répondre:

Je pense qu'il est préférable de créer un chercheur personnalisé comme Raloh dit ci-dessous (en utilisant le code généré par Roo) en utilisant un POJO comme paramètre au lieu de WebRequest, afin de ne pas avoir de dépendances avec le MVC. Essayez de créer un POJO, AssetFilter, par exemple, contenant les attributs que vous souhaitez utiliser dans le moteur de recherche.

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

Le contrôleur va créer le filtre POJO avec la demande, et vous pouvez utiliser ce viseur partout, vous pouvez même créer un test d'intégration pour elle.

Questions connexes