2010-03-10 7 views
8

Eine Sache, die ich bei der Verwendung einer Objektdatenbank wie db4o immer wieder sehr verwirrend fand, ist, wie Sie komplexe Migrationen handhaben sollen, die normalerweise von SQL/PL-SQL gehandhabt werden.Umgang mit Datenpflege in Objektdatenbanken wie db4o

Stellen Sie sich zum Beispiel vor, Sie hätten eine Tabelle in einer relationalen Datenbank namens my_users. Ursprünglich hatten Sie eine Spalte namens "full_name", jetzt, da Ihre Software in V2 ist, möchten Sie diese Spalte entfernen, teilen Sie die vollständigen Namen auf ein Leerzeichen und setzen Sie den ersten Teil in eine Spalte namens "first_name" und die zweite in einer Spalte Nachname_name. In SQL würde ich einfach die Spalten "first_name" und "second_name" auffüllen und dann die ursprüngliche Spalte namens "full_name" entfernen.

Wie würde ich das in etwas wie db4o tun? Schreibe ich ein Java-Programm, das nach allen Objekten von User.class sucht, wobei full_name auf null gesetzt wird, während first_name und last_name gesetzt werden? Wenn ich meinen nächsten SVN-Commit mache, wird es keine Feld/Bean-Eigenschaft geben, die full_name entspricht, wäre das ein Problem? Es sieht so aus, als würde ich es in einer Produktionsanwendung verwenden, in der sich mein "Schema" ändert. Ich möchte ein Skript schreiben, um Daten von Version x auf Version x + 1 zu migrieren und dann in Version x + 2 die Eigenschaften, die ich versuche, zu entfernen loswerden für Version x + 1, da ich kein Java-Skript schreiben kann, um Eigenschaften zu ändern, die nicht mehr Teil meines Typs sind.

Es scheint, dass ein Teil des Problems ist, dass ein RDBMS auf Basis eines einfachen String-basierten Namens ohne Berücksichtigung der Groß-/Kleinschreibung löst, in einer Sprache wie Java Typisierung ist komplizierter als diese, Sie können nicht auf a verweisen Eigenschaft, wenn das Getter/Setter/Feld kein Mitglied der zur Laufzeit geladenen Klasse sind, so dass Sie im Grunde 2 Versionen Ihres Codes im selben Skript haben müssen (hmm, benutzerdefinierte Klassenlader klingen wie ein Schmerz), haben Sie die neue Version von Die gespeicherte Klasse gehört zu einem anderen Paket (klingt chaotisch) oder verwendet die Version x + 1 x + 2-Strategie, die ich erwähnt habe (erfordert viel mehr Planung). Vielleicht gibt es eine offensichtliche Lösung, die ich nie aus den db4o-Dokumenten entnommen habe.

Irgendwelche Ideen? Hoffentlich macht das einen Sinn.

Antwort

10

Zuerst behandelt db4o die "einfache" scenarios like adding or removing a field automatically. Wenn Sie das Feld hinzufügen, wird für alle vorhandenen Objekte der Standardwert gespeichert. Wenn Sie ein Feld entfernen, befinden sich die Daten des vorhandenen Objekts immer noch in der Datenbank und Sie können weiterhin darauf zugreifen. Umbenennungsfeld usw. sind special 'refactoring'-calls.

Jetzt Ihr Szenario würden Sie so etwas tun:

  1. das Feld 'full_name' entfernen, fügen Sie die 'first_name' neue Felder und 'second_name'
  2. Iterate über alle ‚Address'-Objekte
  3. Zugriff auf das alte Feld über die 'StoredClass'-API
  4. Teilen, ändern, aktualisieren usw. den Wert. Setzen Sie die neuen Werte auf das neue Feld und speichern Sie das Objekt.

Nehmen wir an, wir haben eine 'Address'-Klasse. Das Feld 'full_name' wurde entfernt. Nun wollen wir es nach 'Vorname' und 'Nachname' kopieren. Dann könnte es so (Java) gehen:

ObjectSet<Address> addresses = db.query(Address.class); 
    StoredField metaInfoOfField = db.ext().storedClass(Address.class).storedField("full_name", String.class); 
    for (Address address : addresses) { 
     String fullName = (String)metaInfoOfField.get(address); 
     String[] splitName = fullName.split(" "); 
     address.setFirstname(splitName[0]); 
     address.setSurname(splitName[1]); 
     db.store(address); 
    } 

Wie Sie vorgeschlagen, würden Sie migrations Code für jede Version den Höckern schreiben. Da ein Feld nicht mehr zu Ihrer Klasse gehört, müssen Sie mit der 'StoredField'-API wie oben darauf zugreifen.

Sie können eine Liste aller 'gespeicherten' Klassen mit ObjectContainer.ext().storedClasses() erhalten. Mit StoredClass.getStoredFields() können Sie eine Liste aller Geschäftsfelder erhalten, ohne dass das Feld in Ihrer Klasse nicht mehr existiert. Wenn eine Klasse nicht mehr existiert, können Sie die Objekte noch abrufen und über die 'GenericObject'-Klasse darauf zugreifen.

Update: Für komplexere Szenarien, in denen eine Datenbank über mehrere Versionsschritte migriert werden muss.

Zum Beispiel sieht es in der Version v3 das Adress-Objekt völlig anders aus. Das 'migration-script' für v1 zu v2 hat also nicht mehr die benötigten Felder (Vorname und Name in meinem Beispiel). Ich denke, es gibt mehrere Möglichkeiten, damit umzugehen.

  1. (Annahme von Java für diese Idee. Natürlich gibt es eine Entsprechung in. NET). Sie könnten den Migrationsschritt zu Groovy-script machen. So stört jedes Skript nicht das andere. Dann definieren Sie 'Klassen' die benötigten Klassen für die Migration dort. Jede Migration hat also eigene Migrationsklassen. Mit aliases binden Sie Ihre Groovy-Migrations-Klassen an die tatsächlichen Java-Klassen.
  2. Erstellen von Refactoring-Klassen für komplexe Szenarien. Binden Sie auch diese Klassen mit aliases.
2

Ich nehme hier ein bisschen einen wilden Schuss, weil ich nicht zu viele Daten in meinem Leben umgestaltet habe.

Sie machen einen seltsamen Vergleich: Wenn Sie die db "hot-migrieren" möchten, müssten Sie wahrscheinlich die x+1, x+2 Versionierung Ansatz Sie beschrieben, aber ich weiß es nicht wirklich - ich würde nicht weiß nicht, wie man das mit SQL macht, da ich kein db-Experte bin.

Wenn Sie jedoch "kalt" migrieren, können Sie dies in einem Schritt tun, indem Sie ein neues Objekt aus den alten Daten instanziieren, das neue Objekt speichern und das alte Objekt für jedes Objekt im Geschäft löschen. Siehe db4o reference.

Aber ehrlich gesagt: Der gleiche Prozess in einem RDBMS ist auch kompliziert, weil Sie Constraint-Checks (und möglicherweise Trigger, etc.) deaktivieren müssen, um die Operation tatsächlich auszuführen - vielleicht nicht in dem von Ihnen bereitgestellten Beispiel. aber für die meisten realen Fälle. Schließlich ist die Saitenteilung so einfach, dass es wenig Gewinn gibt.

In SQL würde ich einfach bevölkern "first_name" und "second_name" Spalten

Ja, mit einem einfachen String Split-Betrieb, können Sie einfach das tun. In einem typischen Refactoring-Szenario strukturieren Sie Objekte jedoch basierend auf großen und komplizierten Regelsätzen, die in SQL möglicherweise nicht einfach ausgedrückt werden können, komplexe Berechnungen oder externe Datenquellen benötigen.

Um das zu tun, müssten Sie auch Code schreiben.

Schließlich sehe ich nicht zu viel Unterschied in den beiden Prozessen. Sie müssen immer mit Live-Daten vorsichtig sein, und Sie werden in beiden Fällen sicher ein Backup machen. Refactoring macht Spaß, aber Beharrlichkeit ist schwierig, daher ist es in jedem Fall eine Herausforderung, sie zu synchronisieren.