Datenmigration mit mongoDB und Spring Boot

Jeder Entwickler muss früher oder später Daten in eine Datenbank migrieren. In unserem Projekt verwenden wir mongoDB als Datenbank. Wir haben die Datenmigration auf verschiedene Arten angegangen:


  • schrieb js-Skripte und lief direkt in der Datenbank
  • verwendet Mongobee - ein Tool für automatische Migrationen


    Mongobee funktionierte einwandfrei, bis wir auf eine Situation stießen, in der wir ein neues Feld mit einem eindeutigen Index hinzufügen wollten. Nehmen wir an, wir haben eine Klasse:


    @Document @Data public class TestDocument { @Id private String id; private String text; } 

    Jetzt fügen wir ein neues Feld hinzu:


     @Document @Data public class TestDocument { @Id private String id; private String text; @Indexed(unique = true) private String text2; } 

    Wir haben eine Migration geschrieben, die das Feld text2 in allen Dokumenten mit eindeutigen Werten füllen soll:


     @ChangeLog public class TestDocumentChangelog { @ChangeSet(order = 1, id = "change1", author = "Stepan") public void changeset(MongoTemplate template) { template.findAll(Document.class, "testDocument").forEach(document -> { document.put("text2", UUID.randomUUID().toString()); template.save(document, "testDocument"); }); } } 

    Als wir die Anwendung starteten, stellten wir fest, dass Spring die Mongo Data-Komponenten vor Beginn der Migration gestartet hat. Daher ist beim automatischen Erstellen des Index über dem Feld text2 ein Fehler aufgetreten, da dieses Feld noch von keinem Dokument ausgefüllt wurde.
    Nach diesem Vorfall haben wir uns entschlossen, Mongobee aufzugeben und zu versuchen, unser eigenes Tool zu schreiben, das auch das Schreiben von Migrationen erleichtert, aber zusätzlich Funktionen wie:


  • Vor Mongo Data ausführen
  • Transaktionsunterstützung in MongoDB 4.0 hinzugefügt
  • Abhängigkeitsinjektion in ChangeLog-Klassen

Das Ergebnis ist eine Bibliothek namens Mongration, die alle oben beschriebenen Funktionen unterstützt.


Spring Boot Lifecycle-Unterstützung


Die erste Funktion wird mithilfe der automatischen Konfiguration implementiert, die nach dem Erstellen von MongoClient und vor dem Scannen der Repositorys beginnt:


 @Configuration @AutoConfigureAfter(MongoAutoConfiguration.class) @AutoConfigureBefore(MongoDataAutoConfiguration.class) @Import(MongrationConfiguration.class) public class MongrationAutoConfiguration { } 

Das Problem bei der automatischen Konfiguration besteht jedoch darin, dass die Mongo Data-Komponenten vor unserer automatischen Konfiguration initialisiert werden, wenn Sie die Annotation @EnableMongoRepositories , um Repositorys zu aktivieren. Um dieses Problem zu vermeiden, müssen Sie die Annotation @EnableMongration zusammen mit @EnableMongoRepositories :


 @Configuration @EnableMongoRepositories @EnableMongration public class MongoRepositoriesConfiguration { } 

Die @EnableMongration führt lediglich die gleiche Konfiguration aus. Sie können sie nur früher ausführen:


 @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Import(MongrationConfiguration.class) public @interface EnableMongration { } 

Transaktionsunterstützung


Um Transaktionen zu unterstützen, müssen Sie das Replikatset MongoDB konfigurieren. Es ist auch erforderlich, die MongoTransationManager-Bean zu deklarieren (wenn Mongration diese Bean nicht im Kontext findet, wird sie selbst erstellt). Mit Mongration können Sie Transaktionen auf zwei Arten verwenden:


  • Verwenden von @Transactional
     @Transactional @ChangeSet(order = 1, id = "change1", author = "Stepan") public void changeset(MongoTemplate template) { template.findAll(Document.class, "testDocument").forEach(document -> { document.put("text2", UUID.randomUUID().toString()); template.save(document, "testDocument"); }); } 
  • Verwenden von TransactionTemplate
     @ChangeSet(order = 1, id = "change1", author = "Stepan") public void migration(MongoTemplate template, TransactionTemplate txTemplate) { template.createCollection("entity"); txTemplate.execute(new TransactionCallbackWithoutResult() { @Override protected void doInTransactionWithoutResult(TransactionStatus status) { template.save(new Document("index", "1").append("text", "1"), "entity"); template.save(new Document("index", "2").append("text", "2"), "entity"); template.save(new Document("index", "3").append("text", "3"), "entity"); } }); } 


Die zweite Methode ist insofern nützlich, als Sie sowohl DDL-Vorgänge verwenden können, die in einer Transaktion nicht gestartet werden können, als auch DML-Vorgänge, die in einer Transaktion in einer einzelnen Migration gestartet werden.


Abhängigkeitsinjektion in ChangeLog-Klassen


Diese Funktionalität ist möglich, weil jede ChangeLog-Klasse selbst eine gewöhnliche Bean ist:


 @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Component public @interface ChangeLog { } 

Es ist zu beachten, dass Sie keine Beans injizieren können, die Abhängigkeiten von den Mongo Data-Komponenten aufweisen, da diese zum Zeitpunkt des Abschlusses der Migrationen noch nicht initialisiert sind.


Der Quellcode der Bibliothek ist auf Github verfügbar.

Source: https://habr.com/ru/post/de451798/


All Articles