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.
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