Zum Inhalt springen

Revisionen von Datenbankeinträge wie realisieren?


Empfohlene Beiträge

Geschrieben

Hi Community,

ich entwickel gerade eine datenbankgestützte Anwendung, bei der es vor allem um die Eingabe, Ausgabe und Ändern von Datenbankeinträgen geht (nichts besonders aufwändig). Nun möchte ich eine Art Revision hinzufügen um ältere Einträge einsehen und wiederherstellen zu können.

Das Problem ist, dass manche Datensätze über Fremdschlüssel miteinander verknüpft sind, sonst würde ich einfach eine Spalte mit der Versionsnummer hinzufügen und die höchste auslesen. So müsste ich aber alle Fremdschlüssel aktualisieren, was ich aber vermeiden möchte.

Habt ihr vielleicht Ideen, wie ich das effizienter realisieren könnte?

Grüße,

pr0gg3r

Geschrieben

Aus der Hüfte heraus:

Leg zu den entsprechenden Tabellen "Shadowtables" an, also sowas wie "order" und "order_shadow" und sichere einfach per Trigger den Datensatz in der Shadowtabelle. Wenn Du das für alle relevanten Tabellen getan hast, brauchst Du Dir um kaskadierende Änderungen im Einzelfall keine Sorgen zu machen, die Trigger kümmern sich ja um die jeweiligen Änderungen.

Geschrieben

@ricardo1885:

Ich möchte und darf meine produktive Datenbank bestimmt nicht hier veröffentlichen ;) Im Grunde möchte ich jetzt auch keine Lösung für meine Datenbankstruktur, sondern allgemeine Vorschläge.

Aus der Hüfte heraus:

Leg zu den entsprechenden Tabellen "Shadowtables" an, also sowas wie "order" und "order_shadow" und sichere einfach per Trigger den Datensatz in der Shadowtabelle. Wenn Du das für alle relevanten Tabellen getan hast, brauchst Du Dir um kaskadierende Änderungen im Einzelfall keine Sorgen zu machen, die Trigger kümmern sich ja um die jeweiligen Änderungen.

Danke für den Vorschlag. Wenn ich das richtig verstanden habe, würde das dann so aussehen:

Wenn ich einen Eintrag update, wird der Eintrag von "order" in "order_shadow" kopiert und dann erst in "order" geändert. Die Shadow-Tabelle sieht soweit gleich aus (mit dem Unterschied, dass die PKs keinen PK darstellen, da es pro Eintrag (Revision) den gleichen PK gibt).

Mit Triggern wollte ich mich sowieso früher oder später einmal auseinandersetzen.

Geschrieben

Trigger und "Ersatztabellen" mit der Struktur der Originaltabelle sind für versionierte Historien das korrekte Stichwort. Bzw. würde ich es genau so umsetzen.

Sei dir aber bewusst, dass das bei einer anwachsenden Tabelle ohne Löschintervalle zu großen Datenmengen auf dem Server kommen kann. Da sollte man ggfs. direkt beim Trigger mit anbauen, um Datenmüll zu vermeiden.

Gruß, Goulasz

Geschrieben

Danke für den Vorschlag. Wenn ich das richtig verstanden habe, würde das dann so aussehen:

Wenn ich einen Eintrag update, wird der Eintrag von "order" in "order_shadow" kopiert und dann erst in "order" geändert. Die Shadow-Tabelle sieht soweit gleich aus (mit dem Unterschied, dass die PKs keinen PK darstellen, da es pro Eintrag (Revision) den gleichen PK gibt).

Mit Triggern wollte ich mich sowieso früher oder später einmal auseinandersetzen.

Jo. Die PK wären schlicht FKs.

Geschrieben

Sei dir aber bewusst, dass das bei einer anwachsenden Tabelle ohne Löschintervalle zu großen Datenmengen auf dem Server kommen kann. Da sollte man ggfs. direkt beim Trigger mit anbauen, um Datenmüll zu vermeiden.

Dazu ergänzend wäre es bei so etwas die Überlegung eine NoSQL Datenbank zu verwenden, da diese über die Entsprechenden Map-Algorithmen dafür ausgelegt sind sehr große Datenmengen zu verwalten. Bei NoSQL kann man die Historie in jedem Datensatz direkt mit speichern, so dass damit der Zugriff deutlich vereinfacht wird.

Geschrieben

Ich kenne NoSQL-Datenbanken bzw. habe MongoDB und CouchDB jeweils privat installiert und ein wenig getestet/angeschaut, ich bin mir jedoch nicht sicher, ob das nur ein vorübergehender Hype bzw. Trend ist oder ob sich NoSQL-Datenbanken tatsächlich etablieren werden. Im Unternehmenseinsatz besteht natürlich auch immer die Frage, wie sieht die vorhandene Infrastruktur aus, wo liegt das Knowhow der Projektmitarbeiter etc., da kann man nicht einfach mal kurz die Datenbank hinter der Software ändern ;) Aber trotzdem danke für die Vorschläge, ich hatte es mir für einen Moment auch überlegt gehabt, aufgrund der starren Strukturen und des massiven Aufwandes (bestehende Software anpassen usw.) aber dann doch wieder verworfen.

Geschrieben
Ich kenne NoSQL-Datenbanken bzw. habe MongoDB und CouchDB jeweils privat installiert und ein wenig getestet/angeschaut, ich bin mir jedoch nicht sicher, ob das nur ein vorübergehender Hype bzw. Trend ist oder ob sich NoSQL-Datenbanken tatsächlich etablieren werden.

Da stimme ich zwar zu, aber gegenüber RDBMS Strukturen sind natürlich die Map-Reduce-Algorithmen nicht zu ignorieren

Im Unternehmenseinsatz besteht natürlich auch immer die Frage, wie sieht die vorhandene Infrastruktur aus, wo liegt das Knowhow der Projektmitarbeiter etc., da kann man nicht einfach mal kurz die Datenbank hinter der Software ändern ;)

Das ist ein sehr wichtiges Argument. Ich kann durchaus für beide Seiten sprechen, da ich aktuell beides benutze. In einem Projekt konnte ich durch den Einsatz einer NoSQL Datenbank den Code um 80% reduzieren und wesentlich generischer aufbauen, was dadurch zu viel einfacheren Strukturen führt (Webapplikation). In einem anderen Projekt habe ich ein klassisches RDBMS verwendet, da hier die Strukturen dies angeboten haben.

Ich denke man muss dies von Fall zu Fall individuell überdenken.

Geschrieben

In einem Projekt konnte ich durch den Einsatz einer NoSQL Datenbank den Code um 80% reduzieren und wesentlich generischer aufbauen, was dadurch zu viel einfacheren Strukturen führt (Webapplikation).

Die bisherige Anwendung (ein paar tausend Zeilen Code) basiert auf SQL (mit Augenmerk auf MySQL und MSSQL). Ich kann den Code zwar anpassen bzw. teilweise neu schreiben und würde auch die Ressourcen (Zeit) dafür bekommen. Was ich aber nicht möchte ist, dass mir nachher die Datenbank eine zu große Hürde darstellt. Bei relationelen SQL-Datenbanken weiß ich, was ich erwartet und wie man das Meiste realisieren kann. Das Schlimmste wäre jetzt, wenn ich die Entscheider von NoSQL überzeuge, die Umsetzung dann aber nicht mehr so funktioniert, wie ich mir das vorstelle, also ich ständig Probleme habe, weil ich auf eine andere DB setze. Das möchte ich verhindern. Auf der anderen Seite ist eine Reduzierung des Codes um 80% natürlich ein überzeugendes Argument.

Ich denke man muss dies von Fall zu Fall individuell überdenken.

Der große Vorteil von NoSQL-Datenbanken besteht soweit ich weiß darin, dass sich Daten speichern lassen, die sich voneinander unterscheiden. Ich weiß jetzt nicht, in wie fern das auf mich zutrifft. Das Szenario des Projektes kann mich sich recht typisch vorstellen: "Was bisher mit Excel verwaltet wird, soll nun über ein Programm realisiert werden" plus weitere Funktionen (Rechte, Revisionen, Felder die ausgefüllt werden müssen, mögliche Querverknüpfungen je nach Auswahl etc.). Das heißt, meine Struktur ist "starr". Dann geht doch der Vorteil von NoSQL verloren?

Geschrieben
Das Schlimmste wäre jetzt, wenn ich die Entscheider von NoSQL überzeuge, die Umsetzung dann aber nicht mehr so funktioniert, wie ich mir das vorstelle, also ich ständig Probleme habe, weil ich auf eine andere DB setze. Das möchte ich verhindern. Auf der anderen Seite ist eine Reduzierung des Codes um 80% natürlich ein überzeugendes Argument.

Vielleicht hier noch als Ergänzung, es ging bei mir darum ein "Datengrab" zu schaffen, d.h. wir haben eine Datenbank in der nicht gelöscht werden soll und gleichzeitig eine Historie der Änderungen für jeden Datensatz gespeichert wird. Unsere Wahl ist auf MongoDB gefallen, da wird ebenso zu den Daten auch noch Binärinformationen in jedem Datensatz mitführen müssen.

Da ich letztendlich mit JSON Strukturen arbeite habe ich eine Parentklasse, in der die Informationen, die bei allen Datensätzen gleich sind, abgelegt und dann durch eine Ableitung nur spezialisiert sind. Das ganze noch über Array-Access Ableitung erweitert und somit kann ich mit den Datensätzen arbeiten wie mit Arrays. Da die Datensätze selbst kaum vernetzt sind, also kaum Abhängigkeiten aufweisen bringt NoSQL da definitiv eine gute Performance. Ich kann meine Daten als eine Art ungeordnete Menge betrachten.

Das Szenario des Projektes kann mich sich recht typisch vorstellen: "Was bisher mit Excel verwaltet wird, soll nun über ein Programm realisiert werden" plus weitere Funktionen (Rechte, Revisionen, Felder die ausgefüllt werden müssen, mögliche Querverknüpfungen je nach Auswahl etc.). Das heißt, meine Struktur ist "starr".

Da Du hier Excel ansprichst, rate ich mal zu Multidimensionale Datenbank

Die Frage ist, wie sind Deine Daten inhaltlich strukturiert

Geschrieben

Da ich letztendlich mit JSON Strukturen arbeite habe ich eine Parentklasse, in der die Informationen, die bei allen Datensätzen gleich sind, abgelegt und dann durch eine Ableitung nur spezialisiert sind.

Das ist für mich interressant. Ich arbeite ähnlich, nur leite ich in meinem Programm anders ab als ich es in der Datenbank darstelle.

Zum Beispiel habe ich per Quellcode eine Parentkalsse "Abteilungen", davon leite ich "Schichtarbeit" und "Gleitzeitarbeit" und davon wieder "Schichtarbeit->Werkstatt", "Gleitzeitarbeit->Entwicklungsabteilung1", "Gleitzeitarbeit->Verwaltung" usw. ab. In der Datenbank habe ich aber nur eine Datenbank "Abteilungen" mit einer Spalte "arbeitszeitmodell" (ENUM(Gleitzeit, Schichtzeit)). Kann ich nun mit NoSQL und JSON daher gehen und die Datenstrukturen wie in meinem Klassenmodell ableiten? Dann bräuchte ich eigentlich ja nur einen Wrapper, der mit die Klasse in JSON umwandelt und wieder zurück (ich vermute, das machst du mit deiner Parentklasse?) und brauche mich nicht weiter um die Datenbankkommunikation kümmern (wie bisher, indem ich aus jeder Klasse andere Tabellen und Spalten abfrage)?

Da Du hier Excel ansprichst, rate ich mal zu Multidimensionale Datenbank

Die Frage ist, wie sind Deine Daten inhaltlich strukturiert

Danke, werde mich damit auseinandersetzen.

Geschrieben
Dann bräuchte ich eigentlich ja nur einen Wrapper, der mit die Klasse in JSON umwandelt und wieder zurück (ich vermute, das machst du mit deiner Parentklasse?) und brauche mich nicht weiter um die Datenbankkommunikation kümmern (wie bisher, indem ich aus jeder Klasse andere Tabellen und Spalten abfrage)?

Nicht ganz. Ich habe eine Connectionklasse, die eben die Verbindung als Singleton Struktur aufbaut und mit der meine anderen Klassen arbeiten (das Singleton ist immer Session bezogen). Die Parentklasse repräsentiert einen Datensatz, also ein abstraktes Modell des Datensatzes, in dem z.B. Felder wie owner, history etc existieren. Es definiert so etwas wie die Requirements eines Datensatzes und eben die Zugriffsrechte. Davon leite ich dann konkret ab und kann dann sagen, dass z.B. ein OU-Datensatz so etwas wie eine Adresse hat usw. In der OU Klasse ist dann z.B. hinterlegt in welche Collection physisch geschrieben wird (mittels statischem Property).

Damit erhalte ich eben ein sehr flexibels Modell, denn ich leite immer von einer Basisklasse ab, bei mir DatasetItem. Diese Klasse kümmert sich immer um die Kommunikation mit dem Datenbaninterface und sichert mir generelle Eigenschaften des Datensatzes wie Zugriffsberechtigungen. Die Ableitung spezifiziert dann lediglich weitere Teile und ich muss nur genau diese implementieren, aber nur so, dass ich aus den public Strukturen der abgeleiteten Klasse die Daten an die protected Strukturen der DatasetItem Klasse reiche.

Durch die Überladung der Array-Access Methoden brauche ich auch kein echtes MVC Pattern, denn ich habe einen Singleton Renderer, dieser bekommt ein solches Objekt und ich kann mittels instanceof prüfen zu welcher Klasse es gehört und kann direkt die Inhalte der Felder holen. Der Renderer hat dann z.B. einen Renderer für XML, einen für HTML usw. und diese bekommen dann nur ein Key-Value Array mit Daten. Analog funktioniert das Einfügen in die Datenbank, ich zerlege die ankommenden Daten anhand ihres Types (XML, HTML, ...) und erhalte ein Array, das Array wird dann der passenden Datenklasse einfach in der form dataclass::create( $array ) übergeben und ich bekomme das Datenobjekt zurück, was gleichzeitig in der Datenbank erzeugt wurde.

Zusätzlich habe ich ein Caching, denn erst der Dtor schreib physisch die Änderungen in die Datenbank. Die Parentklasse merkt sich intern, ob Eigenschaftes des Datensatzes geändert wurden und im Dtor wird dann über das Datenbankinterface wenn nötig die Verbindung aufgebaut und die Änderungen geschrieben. Dort wird dann auch die Historie erzeugt. Somit habe ich kein explizites schreiben.

Geschrieben

Hallo flashpixx,

erst einmal vielen Dank für deine Mühe und Zeit, die du in deine Antworten investierst!

Nicht ganz. Ich habe eine Connectionklasse, die eben die Verbindung als Singleton Struktur aufbaut und mit der meine anderen Klassen arbeiten (das Singleton ist immer Session bezogen). Die Parentklasse repräsentiert einen Datensatz, also ein abstraktes Modell des Datensatzes, in dem z.B. Felder wie owner, history etc existieren. Es definiert so etwas wie die Requirements eines Datensatzes und eben die Zugriffsrechte. Davon leite ich dann konkret ab und kann dann sagen, dass z.B. ein OU-Datensatz so etwas wie eine Adresse hat usw. In der OU Klasse ist dann z.B. hinterlegt in welche Collection physisch geschrieben wird (mittels statischem Property).

Damit erhalte ich eben ein sehr flexibels Modell, denn ich leite immer von einer Basisklasse ab, bei mir DatasetItem. Diese Klasse kümmert sich immer um die Kommunikation mit dem Datenbaninterface und sichert mir generelle Eigenschaften des Datensatzes wie Zugriffsberechtigungen. Die Ableitung spezifiziert dann lediglich weitere Teile und ich muss nur genau diese implementieren, aber nur so, dass ich aus den public Strukturen der abgeleiteten Klasse die Daten an die protected Strukturen der DatasetItem Klasse reiche.

Ich vermute, meine Lösung unterscheidet sich garnicht so sehr von deiner. Ich habe wie du eine Klasse, dessen Objekte eine Tabelle in der Datenbank repräsentiert. Dieser Tabelle füge ich die Felder/Spalten der Datenbank einem Member (ArrayList<Field>) hinzu (DBTable tabelle = new DBTable("tabellenname"); tabelle.addField(new FieldPK()); tabelle.addField(new FieldString("name", 255);). Die ganzen SQL-befehle (CRUD) bastel ich dann über meine Elternkalsse "Field" (von der sich FieldPK, FieldString, FieldInt, FieldDatetime, usw. ableitet) zusammen und führe sie über eine Datenbankklasse aus. Daraus kann ich alle notwendigen Dinge ableiten und wenn weitere Felder benötigt werden, erstelle ich einfach eine neue Unterklasse und füge die entsprechenden Parameter hinzu (Lieferwagen haben noch eine Ladefläche, Geschäftswagen sind bestimmten Personen zugeordnet, während Poolfahrzeuge keine Zuordnung zu Mitarbeitern haben). Wenn ich dich richtig verstanden habe, machst du das ähnlich, nur dass du mit Key-Value-Arrays arbeitest.

In meiner Tabelle "firmenautos" (pk_auto, ... ladeflaeche, fk_mitarbeiter, ...) habe ich dann aber das "Problem", dass diese Daten nicht tatsächlich mit den Klassen übereinstimmen (ein Geschäftswagen hat keine Ladefläche, in der DB wird diese aber trotzdem repräsentiert (0)).

Wenn ich das in NoSQL dann so realisiere:


{

	"id": "....",

	"_rev": "...",

	"marke": "vw",

	"modell": "Caddy",

	"kennzeichen: "ab-cd-1234",

	"ladeflaeche": 22.3

	"buchungen":

		[

			{

				"mitarbeiter": "...",

				"von": "2013-12-12T12:00",

				"bis": "2013-12-14T14:00",

				"Grund": "Abholung Material"

			},

			{

				"mitarbeiter": "...",

				"tag": "2013-12-15"

			}

		]

	}

}


{

	"id": "...",

	"_rev": "...",

	"marke": "Audi",

	"modell": "A6",

	"kennzeichen: "ab-cd-5678",

	"mitarbeiter": "..."

}

Kann ich darauf verzichten, die Felder tatsächlich immer in der DB anlegen zu müssen. Zusätzlich müsste ich mir nicht die Queries zusammenbasteln (können mit mehreren Joins dann schon mal komplex werden) und hätte die Revisionen gleich mit dabei. Also ist es doch tatsächlich eine Überlegung wert.

Geschrieben

Wenn ich das in NoSQL dann so realisiere:


{

	"id": "....",

	"_rev": "...",

	"marke": "vw",

	"modell": "Caddy",

	"kennzeichen: "ab-cd-1234",

	"ladeflaeche": 22.3

	"buchungen":

		[

			{

				"mitarbeiter": "...",

				"von": "2013-12-12T12:00",

				"bis": "2013-12-14T14:00",

				"Grund": "Abholung Material"

			},

			{

				"mitarbeiter": "...",

				"tag": "2013-12-15"

			}

		]

	}

}


{

	"id": "...",

	"_rev": "...",

	"marke": "Audi",

	"modell": "A6",

	"kennzeichen: "ab-cd-5678",

	"mitarbeiter": "..."

}

Kann ich darauf verzichten, die Felder tatsächlich immer in der DB anlegen zu müssen. Zusätzlich müsste ich mir nicht die Queries zusammenbasteln (können mit mehreren Joins dann schon mal komplex werden) und hätte die Revisionen gleich mit dabei. Also ist es doch tatsächlich eine Überlegung wert.
Ja, wobei ich halt "_rev" = "history" benannt habe und dort ist dann im Grunde der komplette Datensatz wieder drin und "id" heißt bei allen MongoDB Collections "_id", d.h. der PK heißt auf jeder Collection gleich. Ein Trick durch die Arrayüberladung ist, dass ich automatisch Felder, die nicht existieren "null" setze, d.h. wenn ich mein Datensatzobjekt habe und dort $item["blub"] mache und das Feld "blub" nicht existiert wird immer null geliefert, damit habe ich keine Exception, dass ein Key nicht existiert, da ich die Existenz implizit mache. Es verschlankt den Code, da ich nicht erst prüfen muss, ob das Feld existiert und welcher Wert drin steht. Ich kann halt direkt auf den Wert prüfen. Deine Struktur mittels Feldklasse würde ich nicht unter Java machen, weil Du blähst den Code damit sehr auf. Bei MongoDB kannst Du direkt aus dem JSON Objekt ein Javaobjekt machen (siehe JSONObject) und dieses würde ich durch eine allgm Wrapperstruktur kapseln, die Du dann z.B. von HashMap ableiten kannst, damit kannst Du auf die Felder via Key zugreifen. OOP ist zwar schön, aber zuviel OOP bläht auf und leider tendiert man in Java gerne dazu. Je weniger Code Du schreiben musst, um so besser, denn Du erzeugst weniger Fehler und man kann sich leichter einlesen. Ich brauche durch die Arraystruktur nicht so etwas wie "getName" oder "setName", sondern ich mache nur "$item["name"]". Die Überprüfung ob da wirkliche ein String drin ist, geschieht dann in der überladenen ArrayAccess Methode und diese reicht die Daten an die Parentklasse nach der Überprüfung weiter. Ich habe somit immer _nur eine_ Methode um Daten zu holen bzw. zu setzen und das für alle Entities der Datenbank. Für jede Entity habe ich eine Klasse, aber die Klassen sind deutlich kleiner, weil sie wirklich nur den Code enthalten müssen, der für die Entity relevant ist. Irgendwelche Feld- oder Strukturklassen gibt es bei mir nicht. Jede Entityklasse hat eine statische Eigenschaft "collectionname", in der der Collectionname hinterlegt ist, ebenso eine statische Eigenschaft, ob die Historie im Dtor erzeugt werden soll oder nicht, mein Dtor sieht dann so aus:

	if ($this->m_ismodified)

	{

		if (static::$ml_history)

			$this->ma_data["history"] = $this->ma_data;


		$this->ma_data["modificationtime"] = time();

		Database::save( static::$mc_collectionname, $this->ma_data );

	}

ma_data cachet den Inhalt des Datensatzes und der Rest sollte klar sein.

@lilith2k3: Spring ist zwar sicherlich eine nette Sache, aber man tendiert häufig mit solchen Bibliotheken zu komplexen Interfaces. Ich bin kein Freund von diesen Technologien. Man sollte zuerst eine ganz klare Definition für die Daten entwickeln und exakt die Schnittstellen und die Grenzen des Systems beschreiben und danach den Code entwickeln. Je schärfer man die Grenzen zieht um so besser. Alles was nicht definiert wurde, wird nicht codiert. Die ganzen Frameworks erzeugen oft Probleme, da sie meist nur 50-60% der Anforderungen erfüllen und man dann die restlichen Prozent händisch eh dran stricken muss, dann muss man die Version des Frameworks und des eigenen Codes abstimmen, was ebenfalls fehleranfällig ist. Für mich ist der Einsatz eines Frameworks häufig ein Zeichen, dass der Entwickler keine Lust hatte eine saubere Architektur zu entwickeln und stattdessen diese Aufgabe an das "Framework outsourced".

Allerdings kann man ein Framework in die Architektur richtig integrieren, wenn man wie oben gesagt die Grenzen sauber definiert hat und das Framework dann an diesen Grenzen einsetzt. Zuerst muss man das System so vollständig wie möglich am Besten durch einen iterativen Prozess scharf beschreiben. Entsprechende Frameworks mit ihrem Einsatzziel können mit definiert werden. Danach beginnt man die Umsetzung macht aber klare Abschnitte, nach denen man Refactored und ggf Fehler in der vormals definierten Spezifikation korrigiert.

Geschrieben

Danke nochmal für eure Antworten. Ich werde mich die nächsten Tage ausführlicher mit NoSQL und vor allem CouchDB beschäftigen. Letztendlich liegt die Lösung an mir und bisher scheint das nur Vorteile zu bieten. Ich möchte auf der anderen Seite auch nicht allzu euphorisch sein, denn wenn es später Probleme gibt, liegt es schließlich auch an mir ;)

Für jede Entity habe ich eine Klasse, aber die Klassen sind deutlich kleiner, weil sie wirklich nur den Code enthalten müssen, der für die Entity relevant ist. Irgendwelche Feld- oder Strukturklassen gibt es bei mir nicht.

Ich versuche über meine Unterklassen die häufigsten Fälle abzudecken. Bisher habe ich mich danach gerichtet, was mir SQL an Datentypen hergibt. Hier kann ich, wenn ich z.B. ein INSERT- oder UPDATE-Query zusammenstelle, einfach mein FieldPK weglasen, da man PKs nicht selbst bestimmen bzw. ändern kann/soll. In der Ausgabe (SELECT) möchte ich ggf. trotzdem den PK mit auflisten, weshalb das so realisiert wurde. Im Grunde möchte ich da auch Validierungen machen, was für Werte angegeben werden dürfen. Zum Beispiel, dass tatsächlich nur E-Mail-Adressen oder URLs eingegeben wurden. Mittlerweile mache ich das aber über Regex, welchen ich der Obeklasse (Field) als Member hinzugefügt habe und einfach per Setter setzen kann. Eine Notwendigkeit der Unterklassen sehe ich vor allem beim Formular für die Dateneingabe und -bearbeitung: ein Boolean wird anders ausgegeben (Checkbox) als ein Join (Select) oder ein String (Text) oder Text (Textarea) oder ein Datum. Bei dem Join muss ich außerdem erst noch die verzweigten Daten laden. Solche Dinge werden von meinen Field*-Klassen hauptsächlich erledigt.

Geschrieben
Eine Notwendigkeit der Unterklassen sehe ich vor allem beim Formular für die Dateneingabe und -bearbeitung: ein Boolean wird anders ausgegeben (Checkbox) als ein Join (Select) oder ein String (Text) oder Text (Textarea) oder ein Datum. Bei dem Join muss ich außerdem erst noch die verzweigten Daten laden. Solche Dinge werden von meinen Field*-Klassen hauptsächlich erledigt.

Das ist doch schon ein Designfehler, denn Deine Datenklasse hat mit der Ausgabe nichts zu tun. Die Datenklasse kümmert sich nur darum, dass die Daten inhaltlich korrekt sind und eine Renderer-Klasse kümmert sich um die Darstellung. Z.B. ist die Darstellung in einem XML Dokument anders als in einem HTML Dokument. In Deinem Fall müsste die Datenklasse alle diese Unterscheidungen implementieren.

Die Datenklasse kümmert sich nur um die semantisch korrekte Darstellung der Daten z.B. eine Länge darf nicht negativ sein und sie sollte weitere Abhängigkeiten, die sie hat, auflösen können.

Eine Rendererklasse bekommt dann eine Datenobjekt und sorgt dann dafür, wie dieses Datenobjekt dargestellt wird. Wenn man klassisch MVC macht, dann hat man noch einen Controller, der eben ggf Eingabedaten validiert.

Ich versuche über meine Unterklassen die häufigsten Fälle abzudecken.

Das klingt mir sehr danach, dass Deine Architektur fehlerhaft ist, denn Du versucht mit "Unterklassen" "häufige Fälle" abzudecken. Mir scheint Du das das Prinzip von Vererbung nicht richtig verstanden, denn eine abgeleitete Klasse stellt eine Spezialisierung dar, "häufige Fälle" dagegen eine Generalisierung, d.h. das kann nicht richtig funktionieren, da Du hier ja genau versucht ein Komplement zu implementieren.

Das von mir vorgestellte Konzept ist sehr generisch, aber benötigt entsprechende Kenntnisse in OOP, d.h. wenn Du OOP nicht wirklich sicher beherrschst, solltest Du die zuerst nacharbeiten. Es ist absolut fahrlässig, wenn Du die (theoretischen) Konzepte nicht beherrscht und dann versuchst mit einer neuen Technologie das ganze zu lösen, das wird letztendlich zu extrem ineffizienter Arbeit & Code führen. Ich habe aufgrund dieses Posts das Gefühl, dass Du große Designfehler in Deiner bestehenden Anwendung hast und diese wirst Du mit einem NoSQL Ansatz nicht lösen können.

Geschrieben

Die Datenklasse kümmert sich nur um die semantisch korrekte Darstellung der Daten z.B. eine Länge darf nicht negativ sein und sie sollte weitere Abhängigkeiten, die sie hat, auflösen können.

Eine Rendererklasse bekommt dann eine Datenobjekt und sorgt dann dafür, wie dieses Datenobjekt dargestellt wird. Wenn man klassisch MVC macht, dann hat man noch einen Controller, der eben ggf Eingabedaten validiert.

Ich hatte die Wahl zwischen "Klassenname überprüfen" und je nachdem eine andere Ausgabe zu machen oder eben einfach mein Field*.getHTMLFormElement() aufzurufen. Damit ich nicht an zwei Stellen gleichzeitig arbeiten muss, wenn ich ein neues Field* hinzufüge, habe ich mich für letzteres entschieden. Du hast recht, ich sollte lieber einer Renderklasse "irgendwas" übergeben und diese entscheidet dann selber, um was es sich handelt. Das gibt dann neue Probleme, zB mache ich aus einem Array eine Liste mit Checkboxen oder Radioboxen? Dann muss ich hierfür ja wieder eine eigene Klasse erstellen, die vom Typ Array abgeleitet ist und aufgrund dessen die Entscheidung treffen.

Das klingt mir sehr danach, dass Deine Architektur fehlerhaft ist, denn Du versucht mit "Unterklassen" "häufige Fälle" abzudecken. Mir scheint Du das das Prinzip von Vererbung nicht richtig verstanden, denn eine abgeleitete Klasse stellt eine Spezialisierung dar, "häufige Fälle" dagegen eine Generalisierung, d.h. das kann nicht richtig funktionieren, da Du hier ja genau versucht ein Komplement zu implementieren.

Ich habe mein allgemeines Feld, das einen Namen beinhaltet, welcher der Tabellenspalte in der Datenbank entspricht und ein Label, welches ich in der Ausgabe verwende, beinhaltet. Das soll schließlich jedes Feld haben. Dann habe ich davon abgeleitet mein Feld für Integer, welches einen Min- und Max-Wert haben kann, mein Feld für String, welches einen Member für die Anzahl der Zeichen hat usw. Ich habe diese Lösung gewählt, weil ich dann einfach meine ArrayList<Field> (als Member zu jeder Tabellenausgabe) habe und dort dann möglichen Field*reinschmeißen kann. Fand ich bisher eigentlich auch garnicht so doof:


DBTable mitarbeiter = new DBTable("mitarbeiter", "alle Mitarbeiter");


FieldString vorname = new FieldString("vorname", "Vorname");

vorname.setLength(10);

vorname.setRequired(true);

mitarbeiter.addField(vorname);


FieldString nachname = new FieldString("nachname", "Nachname");

nachname.setRequired(true);

mitarbeiter.addField(nachname);


FieldInt sollzeit = new FieldInt("sollzeit", "Soll-Zeit");

sollzeit.setMin(4);

sollzeit.setMax(10);

sollzeit.setRequired(true);

mitarbeiter.addField(sollzeit);


mitarbeiter.addField(new FieldBoolean("istAbteilungsleiter", "Leiter einer Abteilung"));

So sieht ca. mein Konzept aus, alles andere wird automatisch generiert (CRUD). Ich habe mir auch überlegt, statt der Oberklasse ein Interface zu nehmen. Dann kann ich meiner ArrayList<Object> aber alles anfügen, was ich verhindern wollte. Aber wäre das aus OOP-Sicht dann "besser"?

Geschrieben (bearbeitet)
Du hast recht, ich sollte lieber einer Renderklasse "irgendwas" übergeben und diese entscheidet dann selber, um was es sich handelt. Das gibt dann neue Probleme, zB mache ich aus einem Array eine Liste mit Checkboxen oder Radioboxen? Dann muss ich hierfür ja wieder eine eigene Klasse erstellen, die vom Typ Array abgeleitet ist und aufgrund dessen die Entscheidung treffen.

Wieso das? Das ist doch völlig inkonsistent. Die Renderer Klasse weiss, was Sie mit den Daten anfangen kann, sie bekommt einfach das Datenobjekt übergeben und holt sich aus dem Datenobjekt das was sie braucht.

Ich habe mein allgemeines Feld, das einen Namen beinhaltet, welcher der Tabellenspalte in der Datenbank entspricht und ein Label, welches ich in der Ausgabe verwende, beinhaltet. Das soll schließlich jedes Feld haben. Dann habe ich davon abgeleitet mein Feld für Integer, welches einen Min- und Max-Wert haben kann, mein Feld für String, welches einen Member für die Anzahl der Zeichen hat usw. Ich habe diese Lösung gewählt, weil ich dann einfach meine ArrayList<Field> (als Member zu jeder Tabellenausgabe) habe und dort dann möglichen Field*reinschmeißen kann. Fand ich bisher eigentlich auch garnicht so doof:

Das ist total gruselig. Der Code z.B.


mitarbeiter.addField(vorname);

würde implizieren, dass Du auf Datenbankebene eine Spalte "vorname" hinzugfügst, denn mitarbeiter ist vom Typ DBTable. Das ist von der Benennung und Struktur völlig inkonsistent und unverständlich.

Wenn Du MVC machen willst, dann stellt der Controller die Verbindung zwischen Datenmodell und View dar und wenn man das ordentlich macht, dann kann man hier einen abstraktes Binding zwischen Datenmodell & View erzeugen.

Das was Du machst ist für mich "ganz übles Coding", das ich eher einem Azubi noch zugestehe. Wenn ich den Code sehe, dann zeigt das für mich, dass Du nicht wirklich verstanden hast wie OOP funktioniert, siehe http://www.approximity.com/rubybuch/node61.html insbesondere Kapselung und Abstraktion sind bei Dir nicht erkennbar, denn Du mischst Strukturen. Im Grunde ist es bei Dir eine funktionale Programmierung, die Du mit OOP versucht hast auf zu hübschen. Sorry für die harte Kritik, aber ohne ein sinnvolles Architekturkonzept mit entsprechendem OOP-Design wirst Du immer Probleme haben. Du solltest erstmal bei den Designfragen beginnen und erst danach die Technologie auszuwählen.

Bearbeitet von flashpixx
Geschrieben (bearbeitet)

Du hast vollkommen Recht: Für mich liegt der Nutzen von Spring darin, mir boilerplate abzunehmen. Und gerade Java ist voll von boilerplate und ceremony code. Spring ersetzt keine Architektur und es hilft auch keine zu erstellen: aber es hilft sie umzusetzen. Gerade Spring-Data (in meinem Fall hauptsächlich JPA) hilft, sich auf das Wesentliche, sprich die Businesslogik zu konzentrieren.

Insofern steht das Framework an letzter Stelle.

Ich wollte es nur erwähnen, da es nicht jeder kennt:)

Bearbeitet von lilith2k3

Dein Kommentar

Du kannst jetzt schreiben und Dich später registrieren. Wenn Du ein Konto hast, melde Dich jetzt an, um unter Deinem Benutzernamen zu schreiben.

Gast
Auf dieses Thema antworten...

×   Du hast formatierten Text eingefügt.   Formatierung wiederherstellen

  Nur 75 Emojis sind erlaubt.

×   Dein Link wurde automatisch eingebettet.   Einbetten rückgängig machen und als Link darstellen

×   Dein vorheriger Inhalt wurde wiederhergestellt.   Editor leeren

×   Du kannst Bilder nicht direkt einfügen. Lade Bilder hoch oder lade sie von einer URL.

Fachinformatiker.de, 2024 by SE Internet Services

fidelogo_small.png

Schicke uns eine Nachricht!

Fachinformatiker.de ist die größte IT-Community
rund um Ausbildung, Job, Weiterbildung für IT-Fachkräfte.

Fachinformatiker.de App

Download on the App Store
Get it on Google Play

Kontakt

Hier werben?
Oder sende eine E-Mail an

Social media u. feeds

Jobboard für Fachinformatiker und IT-Fachkräfte

×
×
  • Neu erstellen...