Dies ist mehr ein Design als eine Implementierungsfrage und es wird lange dauern, also ertragen Sie mit mir. Es wird am besten mit einem Beispiel erklärt:
Nehmen wir an, ich habe eine Geschäftseinheit namens Produkt mit einer Reihe von Eigenschaften ( Name , Preis , Hersteller , etc ...).
Es wird durch eine Schnittstelle ( Product ) und eine Implementierung ( ProductImpl , in Hibernate gemappt) sowie eine grundlegende CRUD-Serviceschnittstelle ( ProductService ) und Implementierung dargestellt ( ProductServiceImpl ).
Product und ProductService sind als API verfügbar, ihre Implementierungen jedoch nicht.
Ich möchte eine List-Methode findProducts (QueryCriteria-Kriterien) zu ProductService hinzufügen, die eine Liste von Produkten zurückgibt, die bestimmte Kriterien erfüllen. Die Anforderungen sind:
product.price gt 50.0
) product.vendor.name = "Oracle"
) order by product.vendor.name desc, product.price asc"
) product.vendor.name = "Microsoft"
eingestellt ist, sollte die Abfrage in (2) oben eine leere Ergebnismenge erzeugen. Die Frage ist daher, wie sollte die QueryCriteria Schnittstelle aussehen, die von einer solchen Methode verwendet wird? Ich kann mir 3 Lösungen vorstellen und ich mag keines von beiden:
Irgendwelche Kommentare zur Gültigkeit der oben genannten Ansätze oder - Daumen drücken - einfache elegante Lösungen, die mir nicht in den Sinn gekommen sind, würden sehr geschätzt werden.
P.S. In meinem speziellen Fall sind alle API-Clients intern und "semi-trusted" - das heißt, ich kümmere mich nicht so sehr um jemanden, der versucht, etwas zu brechen, als mit schlechter Programmierung, was zu einem kartesischen Produkt von 5 Tabellen führt :-) Es wäre schön, eine Lösung zu finden, die der API-Exposition gegenüber der Öffentlichkeit standhält.
Die tatsächliche Lösung, die ich implementiert habe, verwendet einen hybriden Ansatz.
Methoden, die wohldefinierte Abfragen verwenden (z. B. Methoden, die intern von anderen Diensten verwendet werden, vordefinierte Berichte usw.) haben eine Signatur, die der findBy-Methode von HibernateTemplate ähnelt:
%Vor% Dabei ist QueryParameters
eine Komfortklasse, um benannte Parameter explizit anzugeben oder sie aus einer Bean zu übernehmen. Beispielverwendung ist:
oder
%Vor%Der Zugriff auf solche Methoden ist auf "vertrauenswürdigen" Code beschränkt; Natürlich verwendete Abfragen müssen offensichtlich in Hibernate-Mappings definiert werden. Filter werden in Abfragen integriert oder als Sitzungsfilter definiert. Die Vorteile sind sauberer Code (kein Criteria-ähnliches Zeug über eine halbe Seite verteilt) und klar definierte HQL (einfacher zu optimieren und gegebenenfalls mit dem Cache umzugehen).
Methoden, die für die Benutzeroberfläche verfügbar sind oder anderweitig dynamischer sein müssen, verwenden Suche Schnittstelle von Hibernate-Generic-DAO -Projekt. Es ähnelt in etwa der DetachedCriteria von Hibernate, hat aber mehrere Vorteile:
Es kann erstellt werden, ohne an eine bestimmte Entität gebunden zu sein. Es ist eine große Sache für mich, weil Entity-Schnittstelle (Teil der API für Benutzer sichtbar) und Implementierung (POJO in Hibernate zugeordnet) sind zwei verschiedene Klassen und die Implementierung ist nicht verfügbar für Benutzer zur Kompilierzeit.
Es ist eine gut durchdachte offene Schnittstelle; ganz im Gegensatz zu DetachedCriteria, von dem es fast unmöglich ist, etwas zu extrahieren (ja, ich weiß, dass DC dafür nicht entworfen wurde, aber immer noch)
Integrierte Paginierung / Ergebnisse mit Gesamtanzahl / Bündel anderer kleiner Feinheiten.
Keine expliziten Verbindungen zu Hibernate (obwohl ich mich persönlich nicht wirklich darum kümmere; ich werde Hibernate nicht plötzlich fallen lassen und morgen mit EclipseLink gehen); Es sind sowohl Hibernate- als auch generische JPA-Implementierungen verfügbar.
Filter können zur Suche auf der Serviceseite hinzugefügt werden; Das ist, wenn die Entitätsklasse ebenfalls angegeben wird. Die einzige Sache, die fehlt, ist quick-fail auf der Clientseite, wenn ein ungültiger Eigenschaftenname angegeben ist und der durch das Schreiben meiner eigenen ISearch / IMutableSearch-Implementierung gelöst werden kann, aber ich bin noch nicht dazu gekommen.
Option eins: Wenn es möglich ist, Ihre API zu erweitern, schlage ich vor, Ihre API "reicher" zu machen - fügen Sie weitere Methoden hinzu, wie zum Beispiel einige unten, damit Ihr Service natürlicher klingt. Es kann schwierig sein, Ihre API größer zu machen, ohne dass sie aufgebläht erscheint. Wenn Sie jedoch einem ähnlichen Namensschema folgen, wird es natürlich erscheinen.
%Vor%Das Kombinieren der Ergebnisse (das Anwenden mehrerer Einschränkungen) könnte als etwas für die Clients übrig bleiben, nachdem sie das Ergebnis mithilfe von CollectionUtils und Prädikaten erhalten haben. Sie könnten sogar ein paar gängige Prädikate für die Konsumenten Ihrer API erstellen, nur um nett zu sein. CollectionUtils.select () macht Spaß.
Option Zwei: Wenn es nicht möglich ist, die API zu erweitern, ist Ihr drittes Geschoss das, mit dem ich gehen würde.
- Schreiben Sie meine eigene QueryCriteria-Schnittstelle / Implementierung, die hinter den Kulissen entweder DetachedCriteria oder HQL bildet ...
Sie könnten versuchen, einen DSL-Ansatz für die Benennung anzuwenden, indem Sie etwas verwenden, das dem Builder-Muster ähnelt, um die Dinge lesbarer und natürlicher klingen zu lassen. Das wird in Java mit all den Punkten und Parens ein bisschen ungeschickt, aber vielleicht etwas wie:
%Vor%Option drei: Kombinieren Sie die beiden Ansätze und geben Sie ein restriktionsähnliches Kriterium zusammen mit einer reichhaltigeren API an. Diese Lösungen wären insofern sauber, als sie die Details der Hibernate-Implementierung vor dem Verbraucher Ihrer API verbergen. (Nicht dass irgendjemand jemals daran denken würde, von Hibernate wegzuwechseln.)
Hmm - interessante Frage.
Nachdem Sie darüber nachgedacht haben, ist das Schreiben Ihrer eigenen Kriterienschnittstelle wahrscheinlich der richtige Weg. Es bindet Sie nicht an eine Implementierung und verringert die Sicherheitsbedenken.
Auch abhängig davon, wie viele Objekte betroffen sind, sollten Sie den gesamten Satz von Produkten zurückgeben (mit den erforderlichen Filtern), dann müssen Sie den Endbenutzer Filter mit Lambdaj oder ähnlichem anwenden lassen. Siehe:
Es ist nie eine gute Idee, solche Implementierungsdetails offenzulegen. Von da an bist du an diese Bibliothek gebunden. Schlimmer noch, jede Änderung der Bibliothek wird eine Änderung Ihres Dienstes bedeuten. Irgendwelche Sicherheitsüberlegungen zurückgelassen ...
Was ist mit den Bean-Eigenschaftsnamen, die in Kriterien verwendet werden (ein Tripel des Eigenschaftsnamens, Enum mit weniger, gleichen und mehr und Wert). Mit einem Bean-Wrapper in Ihrem Modell können Sie dies in ein Hibernate-Kriterium umwandeln.
Es ist auch möglich, diese Eigenschaftsnamen nach einer Modelländerung in eine neue Version umzuwandeln.
Hibernate ist ein Low-Level-Infrastruktur-Framework und sollte als solches hinter den Kulissen verborgen bleiben. Wenn Ihre Anwendung nächsten Monat aus irgendeinem Grund in ein anderes ORM-Framework wechseln muss, wird Ihre API nutzlos sein. Die Verkapselung selbst zwischen Schichten innerhalb derselben Anwendung ist von entscheidender Bedeutung.
Nachdem ich all dies gesagt habe, sollte Ihre Methode eine Abstraktion der Informationen erhalten, die Sie für die Suche benötigen. Ich rate Ihnen, eine enum von Product
s Feldern zu erstellen und eine oder zwei einfache Versionen von Restriction zu implementieren.
Die Parameter der Methode können eine Liste von Gleichheitsbeschränkungen, eine weitere Liste relativer Einschränkungen und natürlich ein Bestellkennzeichen (einer der Enum-Werte plus ein Flag für asc / desc) sein.
Dies ist nur eine allgemeine Richtung, ich hoffe, ich habe meinen Standpunkt deutlich gemacht = 8 -)
Ich denke Abfrage nach Beispiel würde funktionieren wirklich gut hier.