An welcher Stelle validieren?

Hallo,

habe eine mittelgroße Anwendung und bin mir im Unklaren wie ich etwas handeln soll.

Habe eine DB auf die ich per JDBC zugreife.

Habe einen ServiceLayer, der das JDBC handelt.

Habe eine GUI in der ich Eingaben einlese und an diesen ServiceLayer weitergebe. Rückgabe ist dann ein Java Bean, bzw. POJO.

Multiple Client Architektur mit lediglich der Datenbank als Server/Integrationsschicht.

Nun möchte ich Daten in die Datenbank einfügen. Datensatz besteht aus ID long (PK), Name String. Der Name soll unique sein.
Service Layer liefert mir dann ein JavaBean mit den Daten zurück.

An welcher Stelle mache ich die Validierung, hauptsächlich wegen UNIQUE am geschicktesten?

  1. GUI macht Request ob Name bereits vergeben ist. Also zusätzliche Methode im ServiceLayer und verbietet dann das Speichern.

  2. ServiceLayer macht zusätzlichen Request und prüft ob Unique.
    2a. Daraufhin Exception werfen.
    2b. Daraufhin null zurückgeben.
    2c. Daraufhin das bereits vorhandene Objekt zurückgeben.

  3. ServiceLayer macht insert,
    3a. Exception wird gefangen und ausgewertet.
    3b. Exception wird weitergeworfen.

  4. StoredProcedures
    StoredProcedure checkt auf unique, speichert und oder liefert dann die Werte von neuem bzw. bereits vorhandenem Objekt zurück.
    GUI schaut ob dieses Objekt vorhanden ist.

4 macht am meisten Sinn, möchte allerdings so wenig StoredProcedures wie möglich haben.
1 + 2 Zusätzliche Request bergen immer das Risiko von Concurrency-Problemen, sowie zusätzlichen Abfragen. Wenn ich nun noch Locking einsetzen muss, dann dreh ich noch komplett hohl.

3a. Aufwendig und wenn eine DB ihre Fehlermeldungen noch internationalisiert, nicht handhabbar.
3b. Sehr, sehr unschön, eigentlich ein NOGO.

Frage mich nicht warum kein ORM zum Einsatz kommt. Das ist Teufelszeug und nicht meine Entscheidung.

Wo ist mein Denkfehler? Was meint ihr?

Besten Dank und einen schönen Mittag

Ich mache die Überprüfungen immer in der GUI, da ich dort am einfachsten die Fehlermeldungen bilden (die ja manchmal komplizierter sind, als “Name bereits vorhanden”) und ggf auch gleich die beteiligten Felder hervorheben kann.

Da ich mich darauf in meiner Applikationslogik verlasse, könnten aus anderen Quellen als der GUI potenziell invalide Objekte ihren Weg ins System finden. Ein saubere Weg währe wohl eine Validierungsmethode für jede Entität anzubieten:
public Set<EmployeeError> validate(Employee employee)

Die GUI könnte in diesem Fall die einzelnen Fehler auf die (interantionalisierten) Fehlermeldungen mappen und die entsprechenden Eingabe-Elemente hervorheben.

Bisher hat mir die direkte Validierung in der GUI aber immer gereicht.

Hängt vom Geld und von der Zeit ab:

Wenn genug Kohle vorhanden ist:

  1. Sofort in der GUI - mit guten, klaren Fehlermeldungen

und dann redundant

  1. in der Middleware/Service Layer (wegen anderer Clients)

und dann redundant

  1. in der Datenbank selbst (Constraints, SP)

aber das ist ein Witz - das synchron halten und pflegen ist ein Alptraum.

Andere Parameter sind:

  1. ist dein Programm das einzige, das auf das Backend zugreift?

  2. wie fähig/kompetent sind die Benutzer

Aus Karrieregründen würde ich die GUI empfehlen - das ist es, was die Anwender sehen (die wollen nicht denken und jeden Kack eintippen) - und wonach du beurteilt wirst. Die tollen Fehlerabfragen im Hintergrund und anderen Schichten sehen die nicht, merken nichts davon, da kriegst du keinen Blumentopf dafür. Muss man halt im Lastenheft festhalten, was gewünscht wird.

Eine Datenbank-Exception wieder zurückübersetzen in eine klare Fehlermeldung für den Anwender ist oft recht kompliziert.

Ich frage mich bei sowas immer, wo das “Wissen” über die Preconditions/Constraints hingehört. Eine GUI sollte imho nicht wissen, dass z.B. der Name einer Person not null und not empty sein muss. Das würde ich eher in der Entity-Klasse Person verorten. Die GUI muss nur in der Lage sein, die Meldung über eine Constraintverletzung gut anzuzeigen. Ich selbst bin da mit javax.validation recht weit gekommen. Uniqueness ist der Grenzfall. Die Anforderung kommt aus der Definition der Entity-Klassen, sinnvoll prüfen kann man aber nur im zentralen Datenbestand (hier also der DB).

Nein, weder Zeit noch Geld ist vorhanden.

YMMD - War vielleicht auch ein Grund warum ich eine Gefühlte Ewigkeit warten durfte, bevor ich den vorhandenen Source sehen durfte. Ein Tolles Programm von guten Programmierern entwickelt. Aber leider ohne Architekten und auf eine ganz andere Domäne ausgelegt. Anfangs als reines Anzeigeprogramm konzipiert, dass die Daten komplett cachet und nun zu einer interaktiven Anwendung umgebaut werden soll. So nebenher versteht sich.

  1. Ja, das Programm schon. Allerdings nicht unbedingt die einzige Instanz. An dieser Stelle könnte man aber evtl. darüber hinwegsehen, da dies selten vorkommt. Habe aber andererseits selbst schon erlebt, dass der MSSQL-Server die UNIQUE-CONSTRAINTS bei richtigem Timing (manuell geklickt) nicht gebacken bekommt.

  2. Solala. Business-User halt.

Lastenheft? Ich mach Scrum. Nee, ist kein Witz.

Nillehammer.
Warum nur sagst du dies erst jetzt. Danke für den Hinweis, die Validierung zu den Entitys zu packen. Manchmal sieht man den Baum vor lauter Trees nicht.
Habe bisher die Validierung, an anderen Stellen, über die GUI gemacht.

javax.validation ist leider JEE6 und somit out of scope, da Java SE. Nachinstallieren ist wohl vorerst nicht möglich. Mache die Regeln leider nicht.

Manchmal fühle ich mich echt wie in die Steinzeit zurückversetzt.

Das ist absurd, hast du schon mal von monster.de gehört?

Von reiner Anzeige zu interaktiver, mehrbenutzerfähiger Anwendung - mit der ganzen Problematik von Locks, Transaktionen, etc. - so nebenbei, das ist schon sehr ambitioniert.

Macht nichts, das ist das letzte Aufgebot. Fremdschlüssel, UNIQUE und andere Constraints, die in T-SQL leicht zu realisieren sind - die musst du verwenden. Alles andere ist stümperhaft. Du wirst so oder so beim gleichzeitigen Zugriff vieler User irgendwann mal komische Datensätze haben, die machen Arbeit genug.

Eine Unique-Bedingung in der GUI abzuprüfen ist eigentlich Luxus:

Weil:

USER A versuchts, zur Prüfung eine Abfrage

USER B versuchts, zur Prüfung eine Abfrage

USER A erhält OK und macht INSERT

USER B erhält OK und erhält Fehlermeldung wegen Constraint-Verletzung

Wie gesagt, Luxus - aber schon allein deshalb sinnvoll, weil man vor dem INSERT gleich mit auf potenzielle Duplikate prüfen kann…

*** Edit ***

Ich frage mich das schon gar nicht mehr, das ist eine Frage nach der Logik, und um die geht es ja nur selten.

Vor dem Bildschirm sitzt ein Benutzer, der auf keinen Fall eine Fehlermeldung oder einen roten Kreis mit einem weißen Kreuz drin angezeigt bekommen soll. Weil der dann zum Telefon greift. Also muss die GUI bis zum Exzess versuchen, alle Fehler schon vor der Eingabe auszuschließen oder nur solche Fehlermeldungen zu produzieren, die auch der dümmste Benutzer kapiert.

O-Ton Benutzer : Hier steht “Verletzung der Eindeutigkeitsbedingung…” was soll ich jetzt machen

Antwort: “Das geht nicht weil, …, man muss…weil da kein doppelter Wert sein darf, das haben die DA OBEN so beschlossen”

O-Ton Benutzer: “Ach so alles klar, danke”

Warte ein paar Wochen oder Monate, repeat.

Mit z. B. hibernate validator kannst du das auch in Java SE nutzen. Was spricht dagegen?

Erstmal, Validierung muss sein (Datenkonsistenz, security…). Es ist egal, ob der Benutzer sie sehen soll oder nicht. Aber Du hast schon Recht. Validierung ist nur die Halbe Miete bei GUIs, die den User weitest möglich unterstützen sollen. Bei sowas wie JFormattedTextfield, DatePickers mit eingeschränkten Datums etc. “leckt” dann doch wieder etwas von dem “Wissen über Constraints” in die GUI rein. Ganz vermeiden lässt sich das wohl nicht…

Also ich persönlich würde, gerade wenn ich unter Zeitdruck stehen würde, mir die Datenbank zu nutze machen : wenn dort eine Spalte als UNIQUE gesetzt ist sollte beim Versuch einen Wert einzutragen den es so schon mal gibt von der DB ein Fehler kommen und das INSERT (und eigentlich auch UPDATE) verhindert werden. Wenn es der DB-Server allerdings selbst nicht auf die Reihe bekommt bei parallelen Zugriffen die Daten konsistent zu halten (zumindest so verstehe ich deine Aussage) würde ich mir Gedanken machen ob die genutzte DB überhaupt für meine Zwecke verwendbar ist. Ansonsten : beim INSERT/UPDATE auf Fehlermeldung prüfen und ggf weiterverarbeiten. Wenn man das ganze “korrekt” mehrschichtig aufbaut hat man ja schon eine Trennung zwischen GUI und Logic sowie einen weg wie die Logic die GUI über updates informieren kann. Ich würde jetzt bei Fehlschlag in der Logic halt eine Info an die GUI übergeben. Dabei kann man sicher verschiedene Info-Klassen nutzen oder halt die GUI muss die übergebene Info selbst parsen, da bin ich nicht so drin.

Das die GUI selbst prüft (auch wenn man es halt aufteilt) kann sehr schnell zu den angemerkten Fehlern führen und man muss letzten Endes doch eine Fehlerbehandlung durchführen. Dann wäre eine vorherige Prüfung wirklich eher “zusätzlicher Luxus”.

Validierung ohne Fehlerbehandlung?
Wie soll das gehen?
Dem user eine SQL Exception mit dem text Unique Constraint Violation zeigen ist nicht das gelbe vom Ei.

Wenn es doch nur etwas gäbe das grundsätzlich in der GUI, in den Services und in der Persistenz (JPA) funktionieren würde… hm… ach ja, JSR 303, wie von cmrudolph angesprochen.

Kein ORM nehmen und dann noch nach Best Practices fragen?
Mutig :slight_smile:

Oh maki. Wenn du wüsstest…
Ich habe nicht damit angefangen. Ich bin kein Entscheidungsträger, nur programmiersklave.
Sobald ich mit irgendwas komme werde ich mit Fragen bombardiert, ob das wirklich nötig ist und nicht einfacher gelöst werden kann.
Ob die Abhängigkeit wirklich benötigt wird.“früher” hat man das ja auch nicht gebraucht. Das ganze klappt schon, man muss nur wollen und sich nicht so dämlich anstellen.

Der beste Tipp ist wohl von Bleiglanz. Auch wenn mich das zutiefst traurig stimmt, da das ganze echt Potential hat.

Dann werde ich wohl erstemal weiterstümpern.

Vielen Dank noch für die hilfreichen Hinweise, vielleicht lässt sich ja doch noch was richten.

Bleiglanz hat auf jedenfall recht, unique constrainst braucht man immer auf DB Ebene, auch mit ORM, ausser man will die ganze Tabelle sperren… :wink:

JSR 303 bietet sich an wenn es um non-null und andere validierungen geht welche sich zb auf andere felder oder gar andere entitäten beziehen, aber eindeutigkeit muss immer auch von der DB sichergestellt werden, ausser bei einem 1-Benutzersystem :wink:

Ansonsten mal überlegen ob man nicht auch mit den Füssen abstimmen sollte

@Bleiglanz : Was ist oben jeweils mit “dann redundant” gemeint?

An zwei Stellen das gleiche machen, die gleiche Information

0 < Alter < 200

ist in der GUI (bei der Textbox), in der Middleware (z.B. einem POJO), in der Datenbank (dem SQL-Schema) enthalten, bzw. “physikalisch” hinterlegt.

Logisch richtig, in der Praxis ein Desaster.

[quote=Bleiglanz]ist in der GUI (bei der Textbox), in der Middleware (z.B. einem POJO), in der Datenbank (dem SQL-Schema) enthalten, bzw. “physikalisch” hinterlegt.

Logisch richtig, in der Praxis ein Desaster.[/quote]
Auch da fand ich die Kombination aus Hibernate und javax.validation (Hibernate Validator) recht gut. GUI-Frameworks werten die Validation-Constraints teilweise aus, Hibernate übersetzt sie in SQL-Constraints. Damit pflegt man sie nur noch an einer Stelle (den Entities). Aber natülich auch hier Einschränkungen. Bis das ganze rund ist, kann es sich schon zu einem kleinen “Desaster” entwickeln.