In meinem Wicket + JPA/Hibernate + Spring-Projekt basiert ein Großteil der Funktionalität auf der Posteingangsseite, wo Benutzer mithilfe vieler Filteroptionen (nicht alle davon müssen verwendet werden) den Satz einschränken können von Objekten, mit denen sie arbeiten möchten. Ich habe mich gefragt, was die beste Strategie ist, um diese Filterung zu implementieren? In der alten Version dieser Anwendung wurde die Suchabfrage mit Verkettungszeichenfolgen erstellt, die SQL-Bedingungen enthalten. Kürzlich las ich über die neue Criteria-API, die JPA zur Verfügung stellt - würden Sie dies empfehlen, wenn Sie mit dem Suchstring arbeiten? Und wie verbindet sich dies mit der DAO-Ebene - wird die Suchabfrage nicht mithilfe der Kriterien-API in der Business-Ebene erstellt, ein Bruch in der Schichtentrennung?Komplexe Suchabfragen JPA
Antwort
Zum Filtern von Abfragen wie Sie beschreiben, empfehle ich definitiv die Verwendung der Hibernate oder JPA-Kriterien-API wegen der Unterstützung für bedingte Abfragen. Normalerweise setze ich den Konstruktionscode für die Kriterien in meine DAOs und übergebe alle erforderlichen (möglicherweise Null) Argumente dort.
Hier ist ein Beispiel DAO-Methode aus einem Beispiel der Autovermietung Anwendung des Hibernate Kriterien API:
public List<VehicleRentalContract> list(Long contractID,
String customerNameOrID, Date date,
String vehicleDescriptionOrRegistration) {
Criteria criteria = getSession().createCriteria(
VehicleRentalContract.class);
// contractID filter
if (contractID != null && contractID != 0) {
criteria.add(Restrictions.eq("id", contractID));
}
// customerNameOrID filter
if (customerNameOrID != null && customerNameOrID.length() > 0) {
try {
Long customerID = Long.parseLong(customerNameOrID);
criteria.add(Restrictions.eq("customer.id", customerID));
} catch (NumberFormatException e) {
// assume we have a customer name
String customerNameQuery = "%" + customerNameOrID.trim() + "%";
criteria.createAlias("customer", "customer").add(
Restrictions.or(Restrictions.like("customer.firstName",
customerNameQuery), Restrictions.like(
"customer.lastName", customerNameQuery)));
}
}
// date filter
if (date != null) {
criteria.add(Restrictions.and(
Restrictions.le("rentalPeriod.startDate", date),
Restrictions.ge("rentalPeriod.endDate", date)));
}
// vehicleDescriptionOrRegistration filter
if (vehicleDescriptionOrRegistration != null
&& vehicleDescriptionOrRegistration.length() > 0) {
String registrationQuery = "%"
+ Vehicle
.normalizeRegistration(vehicleDescriptionOrRegistration)
+ "%";
String descriptionQuery = "%"
+ vehicleDescriptionOrRegistration.trim() + "%";
criteria.createAlias("vehicle", "vehicle").add(
Restrictions.or(Restrictions.like("vehicle.registration",
registrationQuery), Restrictions.like(
"vehicle.description", descriptionQuery)));
}
List<VehicleRentalContract> contracts = criteria.list();
return contracts;
}
Der Create Aufruf verwendet werden kann, wo Sie sich ein in SQL beitreten müssen.
sogar ich bevorzuge die Verwendung von Kriterien über HQL und SQL, für mich Grund wird Modularität und auch Leistung sein, denn wenn das Projekt in die Produktion kommt, ist das Hauptproblem, das wir Leistung ist, weder HQL noch SQL können Kriterien konkurrieren Performance.
Hinzufügen zu oben Die DAO-Schicht ist für den Zugriff auf die Daten erstellt, und diese Schicht sollte so klar wie Glas ohne komplexe Codierung oder Geschäftslogik sein, aber im Falle von Kriterien muss man eine Logik schreiben (Kriterien erstellen) um einen besseren und besseren Zugang zum Objekt zu erreichen. Aus meiner Sicht gibt es also keinen Grund, so viel Logik in die DAO-Ebene zu bringen.
Ich glaube nicht, dass eine High-Level-API SQL übertreffen könnte, weil sie schließlich als SQL-Abfragen endet. –
zwei Ansätze:
1 .. auf welchem je Art der Filterung müssen Sie Sie in der Lage sein, dies acheive zum Beispiel durch die Suche Indexieren Sie alle Objekte mit Lucene und verwenden Sie dann Suchabfragen, um die Filterung durchzuführen. zB Aufbau einer Abfrage wie:
Titel: "Der richtige Weg" & mod_date: [20020101 TO 20030101]
See: http://lucene.apache.org/java/2_4_0/queryparsersyntax.html
2 .. Oder mit Kriterien ...
würde ich das neue typsichere Kriterien api von Hibernate verwenden:
Anstatt eine Methode, die eine sehr große Kriterien aufbaut, würde ich versuchen, die Logik alle frei stehend Kriterien zu trennen out -
Mit einer dieser beiden Kombination würden Sie in der Lage zu bauen Kriterien leicht aufstellen.
Ein anderer Ort, um nach Inspiration zu suchen, sind die dynamischen Grals-Finder. Dies ist im Wesentlichen das, was Sie auf statische Weise erreichen möchten.
http://www.grails.org/doc/1.0.x/guide/single.html#5.4.1 Dynamische Finders
Wenn Sie wirklich wollen, eine vollständige Trennung von Schichten Sie eine einfache Grammatik implementieren könnte. Dann analysieren Sie dies, um die relevanten Kriterien zu erstellen. Dies würde eine Änderung der zugrunde liegenden Kriterienimplementierungen ermöglichen. Ob dies angebracht ist, hängt davon ab, wie entscheidend diese Abstraktion für Sie ist.
+1 für Grundkonzept. Obwohl man sich über die Verwendung des customerNameOrID-Parameters streiten könnte. Es sieht so aus, als würde man ein "oder" in die ansonsten "und" -bezogenen Parameter verschachteln. Dies kann beliebig komplex werden, wenn ein Fall eintritt, in dem die Datentypen gleich sind. In diesen Fällen ist es wahrscheinlich am besten, Überladungen mit verschiedenen Parametersätzen zu erstellen. –
@Adriaan Koster Ihre Liste von Argumenten kann durch eine Klasse ersetzt werden, die Ihre Suche abbildet, wie ** VehicleRentalContractCriteria ** –
@Martin 'customerNameOrID' kommt von einem Texteingabefeld, in das der Benutzer entweder einen Teil des Kunden eingeben kann Name oder eine Kundennummer. Ich sehe nicht genau, wie Sie vorschlagen, das verschachtelte OR zu vereinfachen, bitte zeigen Sie uns. –