Ein Teil meines Programms ruft Daten aus vielen Tabellen und Spalten in meiner Datenbank zur Verarbeitung ab. Einige der Spalten sind möglicherweise vorhanden null
, aber im aktuellen Verarbeitungskontext ist dies ein Fehler.
Dies sollte "theoretisch" nicht passieren. Wenn dies der Fall ist, deutet dies auf fehlerhafte Daten oder einen Fehler im Code hin. Die Fehler haben je nach Feld unterschiedliche Schweregrade null
. dh für einige Felder sollte die Verarbeitung gestoppt und jemand benachrichtigt werden, für andere sollte die Verarbeitung fortgesetzt werden dürfen und nur jemand benachrichtigt werden.
Gibt es gute Architektur- oder Designprinzipien, um mit den seltenen, aber möglichen null
Einträgen umzugehen?
Die Lösungen sollten mit Java implementiert werden können, aber ich habe das Tag nicht verwendet, da ich denke, dass das Problem etwas sprachunabhängig ist.
Einige Gedanken, die ich selbst hatte:
Verwenden von NOT NULL
Am einfachsten wäre es, eine NOT NULL-Einschränkung in der Datenbank zu verwenden.
Was aber, wenn das ursprüngliche Einfügen der Daten wichtiger ist als dieser spätere Verarbeitungsschritt? Für den Fall, dass die Einfügung ein null
in die Tabelle einfügt (entweder aufgrund von Fehlern oder vielleicht sogar aus einem gültigen Grund), möchte ich nicht, dass die Einfügung fehlschlägt. Nehmen wir an, dass viele weitere Teile des Programms von den eingefügten Daten abhängen, jedoch nicht von dieser bestimmten Spalte. Daher würde ich lieber den Fehler im aktuellen Verarbeitungsschritt als im Einfügeschritt riskieren. Deshalb möchte ich keine NOT NULL-Einschränkung verwenden.
Naiv abhängig von NullPointerException
Ich könnte die Daten einfach so verwenden, als ob ich erwarte, dass sie immer vorhanden sind (und das sollte wirklich der Fall sein), und die resultierenden NPEs auf einer angemessenen Ebene abfangen (z. B. damit die Verarbeitung des aktuellen Eintrags stoppt, aber nicht der gesamte Verarbeitungsfortschritt ). Dies ist das Prinzip "schnell scheitern" und ich bevorzuge es oft. Wenn es zumindest ein Fehler ist, bekomme ich eine protokollierte NPE.
Aber dann verliere ich die Fähigkeit, zwischen verschiedenen Arten fehlender Daten zu unterscheiden. Zum Beispiel könnte ich einige fehlende Daten weglassen, aber für andere sollte die Verarbeitung gestoppt und ein Administrator benachrichtigt werden.
Überprüfen auf null
vor jedem Zugriff und werfen benutzerdefinierte Ausnahmen
Mit benutzerdefinierten Ausnahmen kann ich anhand der Ausnahme die richtige Aktion festlegen. Dies scheint also der richtige Weg zu sein.
Aber was ist, wenn ich vergesse, es irgendwo zu überprüfen? Außerdem überfülle ich meinen Code dann mit Nullprüfungen, die nie oder selten erwartet werden (und daher definitiv nicht Teil des Geschäftslogikflusses sind).
Wenn ich mich für diesen Weg entscheide, welche Muster eignen sich am besten für den Ansatz?
Alle Gedanken und Kommentare zu meinen Ansätzen sind willkommen. Auch bessere Lösungen jeglicher Art (Muster, Prinzipien, bessere Architektur meines Codes oder meiner Modelle usw.).
Bearbeiten:
Es gibt eine weitere Einschränkung, da ich ein ORM verwende, um die Zuordnung von DB zu Persistenzobjekt durchzuführen, sodass Nullprüfungen auf dieser Ebene nicht funktionieren würden (da dieselben Objekte in Teilen verwendet werden, in denen die Null keinen Schaden anrichtet). . Ich habe dies hinzugefügt, weil in den bisher gegebenen Antworten beide diese Option erwähnt haben.
Antworten:
Ich würde die Nullprüfungen in Ihren Mapping-Code einfügen, in dem Sie Ihr Objekt aus der Ergebnismenge erstellen. Dadurch wird die Überprüfung an einem Ort durchgeführt, und Ihr Code kann nicht die Hälfte der Verarbeitung eines Datensatzes durchlaufen, bevor ein Fehler auftritt. Abhängig von der Funktionsweise Ihres Anwendungsflusses möchten Sie möglicherweise die Zuordnung aller Ergebnisse als Vorverarbeitungsschritt durchführen, anstatt jeden Datensatz einzeln zuzuordnen und zu verarbeiten.
Wenn Sie ein ORM verwenden, müssen Sie alle Ihre Nullprüfungen durchführen, bevor Sie jeden Datensatz verarbeiten. Ich würde eine
recordIsValid(recordData)
Methode vom Typ "Typ" empfehlen . Auf diese Weise können Sie (erneut) die gesamte Nullprüfungs- und andere Validierungslogik an einem Ort aufbewahren. Ich würde die Nullprüfungen definitiv nicht mit dem Rest Ihrer Verarbeitungslogik vermischen.quelle
Es klingt so, als wäre das Einfügen einer Null ein Fehler, aber Sie haben Angst, diesen Fehler beim Einfügen zu erzwingen, weil Sie keine Daten verlieren möchten. Wenn ein Feld jedoch nicht null sein sollte, sondern verloren geht, gehen Daten verloren . Daher besteht die beste Lösung darin, sicherzustellen, dass Nullfelder nicht fälschlicherweise gespeichert werden.
Erzwingen Sie zu diesem Zweck, dass die Daten in dem einen autorisierenden, permanenten Repository für diese Daten, der Datenbank, korrekt sind. Fügen Sie dazu nicht null Einschränkungen hinzu. Dann schlägt Ihr Code möglicherweise fehl, aber diese Fehler benachrichtigen Sie sofort über Fehler, sodass Sie Probleme beheben können, die bereits zu Datenverlust führen. Jetzt, da Sie Fehler leicht identifizieren können, testen Sie Ihren Code und testen Sie ihn zweimal. Sie können Fehler beheben, die zu Datenverlust führen, und dabei die nachgelagerte Verarbeitung der Daten erheblich vereinfachen, da Sie sich keine Gedanken über Nullen machen müssen.
quelle
In Bezug auf diesen Satz in der Frage:
Ich habe dieses Zitat immer geschätzt (mit freundlicher Genehmigung dieses Artikels ):
Grundsätzlich gilt: Es klingt so, als würden Sie das Postelsche Gesetz befürworten : "Seien Sie konservativ in dem, was Sie senden, seien Sie liberal in dem, was Sie akzeptieren." Obwohl dieses "Robustheitsprinzip" theoretisch großartig ist, führt es in der Praxis zu Software, die zumindest langfristig - und manchmal auch kurzfristig - nicht robust ist. (Vergleiche Eric Allmans Artikel The Robustness Principle Reconsidered , der eine sehr gründliche Behandlung des Themas darstellt, obwohl er sich hauptsächlich auf Anwendungsfälle von Netzwerkprotokollen konzentriert.)
Wenn Sie Programme, die nicht ordnungsgemäß Daten in die Datenbank eingefügt werden, werden diese Programme gebrochen und Notwendigkeit werden fixiert . Durch das Papier über das Problem wird es nur noch schlimmer. Dies ist das Software-Engineering-Äquivalent dazu, dass ein Süchtiger seine Sucht fortsetzen kann.
Pragmatisch gesehen müssen Sie jedoch manchmal zulassen, dass "gebrochenes" Verhalten zumindest vorübergehend fortgesetzt wird, insbesondere im Rahmen eines nahtlosen Übergangs von einem lockeren, gebrochenen Zustand in einen strengen, korrekten Zustand. In diesem Fall möchten Sie einen Weg finden, um das erfolgreiche Einfügen der falschen Einfügungen zu ermöglichen, aber dennoch zulassen, dass sich der "kanonische" Datenspeicher immer in einem korrekten Zustand befindet . Es gibt verschiedene Möglichkeiten, dies zu tun:
Eine Möglichkeit, all diese Probleme zu umgehen, besteht darin , eine API-Ebene einzufügen, die Sie zwischen Programmen, die Schreibvorgänge ausführen, und der tatsächlichen Datenbank steuern .
Es klingt so, als ob ein Teil Ihres Problems darin besteht, dass Sie nicht einmal alle Stellen kennen, an denen falsche Schreibvorgänge generiert werden - oder dass es einfach zu viele davon gibt, als dass Sie sie aktualisieren könnten. Das ist ein beängstigender Zustand, aber er hätte niemals entstehen dürfen.
Sobald Sie mehr als eine Handvoll Systeme erhalten, die Daten in einem kanonischen Produktionsdatenspeicher ändern dürfen, werden Sie in Schwierigkeiten geraten: Es gibt keine Möglichkeit, etwas an dieser Datenbank zentral zu verwalten . Besser wäre es, so wenig Prozessen wie möglich Schreibvorgänge zu erlauben und diese als "Gatekeeper" zu verwenden, die die Daten vorverarbeiten können, bevor sie nach Bedarf eingefügt werden. Der genaue Mechanismus hierfür hängt wirklich von Ihrer spezifischen Architektur ab.
quelle
" Gibt es gute Architektur- oder Designprinzipien, um mit den seltenen, aber möglichen Null-Einträgen umzugehen? "
Einfache Antwort - ja.
ETL
Führen Sie eine Vorabverarbeitung durch, um sicherzustellen, dass die Daten von ausreichender Qualität sind, um in die Datenbank aufgenommen zu werden. Alles in der Drop-Datei sollte zurückgemeldet werden und alle sauberen Daten können in die Datenbank geladen werden.
Als jemand, der sowohl Wilderer (Entwickler) als auch Game Keeper (DBA) war, weiß ich aus bitterer Erfahrung, dass Dritte ihre Datenprobleme nur dann lösen können, wenn sie dazu gezwungen werden. Sich ständig nach hinten zu beugen und Daten zu massieren, ist ein gefährlicher Präzedenzfall.
Mart / Repository
In diesem Szenario werden Rohdaten in die Repository-Datenbank übertragen, und anschließend wird eine bereinigte Version in die Mart-Datenbank übertragen, auf die Anwendungen dann Zugriff haben.
Standardwerte
Wenn Sie sinnvolle Standardwerte auf Spalten anwenden können, sollten Sie dies tun, obwohl dies einige Arbeit erfordern kann, wenn es sich um eine vorhandene Datenbank handelt.
Früh scheitern
Es ist verlockend, Datenprobleme einfach am Gateway zur Anwendung, Berichtssuite, Benutzeroberfläche usw. zu beheben. Ich rate Ihnen dringend, sich nicht nur darauf zu verlassen. Wenn Sie ein anderes Widget in die Datenbank einbinden, treten möglicherweise wieder dieselben Probleme auf. Beheben Sie die Probleme mit der Datenqualität.
quelle
Wann immer Ihr Anwendungsfall es erlaubt, NULL sicher durch einen guten Standardwert zu ersetzen, können Sie die Konvertierung in den
SELECT
SQL-Anweisungen mitISNULL
oder durchführenCOALESCE
. Also stattman kann schreiben
Dies funktioniert natürlich nur, wenn das ORM es erlaubt, die select-Anweisungen direkt zu bearbeiten oder veränderbare Vorlagen für die Generierung bereitzustellen. Man sollte sicherstellen, dass keine "echten" Fehler auf diese Weise maskiert werden. Wenden Sie sie daher nur an, wenn das Ersetzen durch einen Standardwert genau das ist, was Sie im Fall von NULL wollen.
Wenn Sie die Datenbank und das Schema ändern können und Ihr Datenbanksystem dies unterstützt, können Sie den spezifischen Spalten eine Standardwertklausel hinzufügen, wie von @RobbieDee vorgeschlagen. Dies erfordert jedoch auch das Ändern der vorhandenen Daten in der Datenbank, um zuvor eingefügte NULL-Werte zu entfernen, und die Möglichkeit, anschließend zwischen korrekten und unvollständigen Importdaten zu unterscheiden.
Aus eigener Erfahrung weiß ich, dass die Verwendung von ISNULL überraschend gut funktionieren kann. In der Vergangenheit musste ich eine Legacy-Anwendung warten, bei der die ursprünglichen Entwickler vergessen hatten, vielen Spalten NOT NULL-Einschränkungen hinzuzufügen, und wir konnten diese Einschränkungen später nicht einfach hinzufügen aus irgendwelchen Gründen. In 99% aller Fälle war 0 als Standard für Zahlenspalten und die leere Zeichenfolge als Standard für Textspalten völlig akzeptabel.
quelle
Das OP geht von einer Antwort aus, die Geschäftsregeln mit datenbanktechnischen Details verbindet.
Dies sind alles Geschäftsregeln. Die Geschäftsregeln kümmern sich nicht um Null an sich. Nach allem, was es weiß, könnte die Datenbank null haben, 9999, "BOO!" ... Es ist nur ein weiterer Wert. Dass in einem RDBMS null interessante Eigenschaften und einzigartige Verwendungen aufweist, ist umstritten.
Das einzige, was zählt, ist, was "Nullheit" für die gegebenen Geschäftsobjekte bedeutet ...
Ja.
Das Auslösen einer Ausnahme beim Abrufen von Daten ist nicht sinnvoll.
Die Frage ist "soll ich 'schlechte' Daten speichern"? Es hängt davon ab, ob:
quelle
Es gibt viele Möglichkeiten, mit Nullen umzugehen, daher wechseln wir von der Datenbankebene zur Anwendungsschicht.
Datenbankebene
Sie können Nullen verbieten ; obwohl es hier unpraktisch ist.
Sie können einen Standard pro Spalte konfigurieren :
insert
, so dass nicht explizit null Einsetzen abdecktinsert
diese Spalte fälschlicherweise übersehen wurdeSie können einen Trigger so konfigurieren , dass beim Einfügen die fehlenden Werte automatisch berechnet werden:
insert
Abfrageebene
Sie können Zeilen überspringen, in denen eine Unannehmlichkeit
null
vorliegt:Sie können einen Standardwert in der Abfrage angeben:
Hinweis: Das Instrumentieren jeder Abfrage ist nicht unbedingt ein Problem, wenn Sie über eine automatisierte Methode zum Generieren verfügen.
Anwendungsschicht
Sie können die Tabelle vorab auf Verboten prüfen
null
:Sie können die Verarbeitung unterbrechen, wenn Sie auf ein Verbotenes stoßen
null
:null
und welche nichtSie können die Zeile überspringen, wenn Sie auf ein Verbotenes stoßen
null
:null
und welche nichtSie können eine Benachrichtigung senden, wenn Sie auf eine verbotene Benachrichtigung stoßen
null
, entweder einzeln oder stapelweise. Dies ist eine Ergänzung zu den anderen oben beschriebenen Methoden. Am wichtigsten ist jedoch "Was dann?". Insbesondere wenn Sie erwarten, dass die Zeile gepatcht wird und erneut verarbeitet werden muss, müssen Sie möglicherweise sicherstellen, dass Sie bereits verarbeitete Zeilen von Zeilen unterscheiden können, die benötigt werden erneut verarbeitet werden.In Anbetracht Ihrer Situation würde ich die Situation bei der Bewerbung behandeln und entweder kombinieren:
Ich würde dazu neigen , wenn möglich nur zu überspringen , um irgendwie einen gewissen Fortschritt zu garantieren, insbesondere wenn die Verarbeitung Zeit in Anspruch nehmen kann.
Wenn Sie die übersprungenen Zeilen nicht erneut verarbeiten müssen, sollte es ausreichen, sie einfach zu protokollieren. Eine am Ende des Prozesses gesendete E-Mail mit der Anzahl der übersprungenen Zeilen ist eine angemessene Benachrichtigung.
Andernfalls würde ich eine Beistelltabelle für die Zeilen verwenden, die repariert (und erneut verarbeitet) werden sollen. Diese Beistelltabelle kann entweder eine einfache Referenz (ohne Fremdschlüssel) oder eine vollständige Kopie sein: Letzteres ist, auch wenn es teurer ist, erforderlich, wenn Sie nicht die Zeit haben, das
null
Problem zu beheben, bevor Sie die Hauptdaten bereinigen müssen.quelle
Nullen können bei der Übersetzung oder Zuordnung von Datenbanktypen zu Sprachtypen behandelt werden. In C # finden Sie beispielsweise eine generische Methode, die für jeden Typ null für Sie behandelt:
Oder wenn Sie eine Aktion ausführen möchten ...
Und dann werden wir in der Zuordnung, in diesem Fall zu einem Objekt vom Typ "Sample", für jede der Spalten null behandeln:
Schließlich können alle Zuordnungsklassen basierend auf der SQL-Abfrage oder den beteiligten Tabellen automatisch generiert werden, indem die SQL-Datentypen betrachtet und in die sprachspezifischen Datentypen übersetzt werden. Dies tun viele ORMs automatisch für Sie. Beachten Sie, dass einige Datenbanktypen möglicherweise keine direkte Zuordnung haben (georäumliche Spalten usw.) und möglicherweise eine spezielle Behandlung erfordern.
quelle