pr0gg3r Geschrieben 9. Dezember 2013 Geschrieben 9. Dezember 2013 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 Zitieren
R1I9C8H5I Geschrieben 9. Dezember 2013 Geschrieben 9. Dezember 2013 Zu deinem Denkansatz wäre ein Diagram des Datenmodells hilfreich... in fremde Köpfe lässt sich nur schwer reinsehen... :cool: Zitieren
lilith2k3 Geschrieben 9. Dezember 2013 Geschrieben 9. Dezember 2013 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. Zitieren
pr0gg3r Geschrieben 10. Dezember 2013 Autor Geschrieben 10. Dezember 2013 @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. Zitieren
Goulasz Geschrieben 10. Dezember 2013 Geschrieben 10. Dezember 2013 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 Zitieren
pr0gg3r Geschrieben 10. Dezember 2013 Autor Geschrieben 10. Dezember 2013 Alles klar, das hat mir soweit weiter geholfen. Ich danke euch! Zitieren
lilith2k3 Geschrieben 10. Dezember 2013 Geschrieben 10. Dezember 2013 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. Zitieren
flashpixx Geschrieben 10. Dezember 2013 Geschrieben 10. Dezember 2013 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. Zitieren
lilith2k3 Geschrieben 10. Dezember 2013 Geschrieben 10. Dezember 2013 Ich streu mal buntes hinzu: Apache CouchDB MongoDB RavenDB - 2nd generation document database Zitieren
Goulasz Geschrieben 10. Dezember 2013 Geschrieben 10. Dezember 2013 Ich streu nochmal nen etwas anderen Ansatz rein: Sarah Mei » Why You Should Never Use MongoDB Data Vault Modeling - Wikipedia, the free encyclopedia Zitieren
pr0gg3r Geschrieben 10. Dezember 2013 Autor Geschrieben 10. Dezember 2013 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. Zitieren
flashpixx Geschrieben 11. Dezember 2013 Geschrieben 11. Dezember 2013 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. Zitieren
pr0gg3r Geschrieben 11. Dezember 2013 Autor Geschrieben 11. Dezember 2013 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? Zitieren
flashpixx Geschrieben 11. Dezember 2013 Geschrieben 11. Dezember 2013 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 Zitieren
pr0gg3r Geschrieben 12. Dezember 2013 Autor Geschrieben 12. Dezember 2013 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. Zitieren
flashpixx Geschrieben 12. Dezember 2013 Geschrieben 12. Dezember 2013 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. Zitieren
pr0gg3r Geschrieben 12. Dezember 2013 Autor Geschrieben 12. Dezember 2013 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. Zitieren
lilith2k3 Geschrieben 12. Dezember 2013 Geschrieben 12. Dezember 2013 Wenn Du uns noch verrätst, mit welcher Sprache die Anwendung realisiert werden soll, vielleicht, ließe sich Dir noch ein wenig helfen: e.g. unter Java: Spring Data MongoDB Zitieren
pr0gg3r Geschrieben 12. Dezember 2013 Autor Geschrieben 12. Dezember 2013 Wenn Du uns noch verrätst, mit welcher Sprache die Anwendung realisiert werden soll, vielleicht, ließe sich Dir noch ein wenig helfen: e.g. unter Java: Spring Data MongoDB Ja, es handelt sich um Java. Danke für den Link, werde ich mir anschauen. Zitieren
flashpixx Geschrieben 12. Dezember 2013 Geschrieben 12. Dezember 2013 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. Zitieren
pr0gg3r Geschrieben 12. Dezember 2013 Autor Geschrieben 12. Dezember 2013 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. Zitieren
flashpixx Geschrieben 12. Dezember 2013 Geschrieben 12. Dezember 2013 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. Zitieren
pr0gg3r Geschrieben 12. Dezember 2013 Autor Geschrieben 12. Dezember 2013 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"? Zitieren
flashpixx Geschrieben 13. Dezember 2013 Geschrieben 13. Dezember 2013 (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 13. Dezember 2013 von flashpixx Zitieren
lilith2k3 Geschrieben 13. Dezember 2013 Geschrieben 13. Dezember 2013 (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 13. Dezember 2013 von lilith2k3 Zitieren
Empfohlene Beiträge
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.