Migration des données avec mongoDB et Spring Boot

Tôt ou tard, chaque développeur est confronté à la nécessité de migrer des données dans une base de données. Sur notre projet, nous utilisons mongoDB comme base de données. Nous avons abordé la migration des données de différentes manières:


  • écrit des scripts js et exécuté directement dans la base de données
  • utilisé Mongobee - un outil pour les migrations automatiques


    Mongobee a bien fonctionné jusqu'à ce que nous rencontrions une situation où nous voulions ajouter un nouveau champ avec un index unique. Disons que nous avons une classe:


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

    Maintenant, nous ajoutons un nouveau champ:


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

    Nous avons écrit une migration, qui devrait remplir le champ text2 dans tous les documents avec des valeurs uniques:


     @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"); }); } } 

    Mais lorsque nous avons démarré l'application, nous avons vu que Spring a lancé les composants Mongo Data avant le début de notre migration, et donc une erreur s'est produite lors de la création automatique de l'index au-dessus du champ text2 , car ce champ n'a encore été rempli par aucun document.
    Après cet incident, nous avons décidé d'abandonner Mongobee et d'essayer d'écrire notre propre outil, ce qui faciliterait également l'écriture des migrations, mais aurait en outre des fonctionnalités telles que:


  • Exécuter avant Mongo Data
  • Prise en charge des transactions ajoutée dans MongoDB 4.0
  • Injection de dépendances dans les classes ChangeLog

Le résultat est une bibliothèque appelée Mongration, qui prend en charge toutes les fonctionnalités décrites ci-dessus.


Prise en charge du cycle de vie Spring Boot


La première fonction est implémentée à l'aide de la configuration automatique, qui démarre après la création de MongoClient et avant l'analyse des référentiels:


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

Mais le problème avec la configuration automatique est que si vous utilisez l'annotation @EnableMongoRepositories pour activer les référentiels, les composants Mongo Data sont initialisés avant notre configuration automatique. Pour éviter ce problème, vous devez utiliser l'annotation @EnableMongoRepositories avec @EnableMongoRepositories :


 @Configuration @EnableMongoRepositories @EnableMongration public class MongoRepositoriesConfiguration { } 

L' @EnableMongration ne fait rien de plus que lancer la même configuration, vous permet seulement de l'exécuter plus tôt:


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

Prise en charge des transactions


Pour prendre en charge les transactions, vous devez configurer le jeu de réplicas MongoDB. Il est également nécessaire de déclarer le bean MongoTransationManager (si Mongration ne trouve pas ce bean dans son contexte, il le créera par lui-même). Mongration vous permet d'utiliser les transactions de deux manières:


  • Utilisation de @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"); }); } 
  • Utilisation de 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"); } }); } 


La deuxième méthode est utile en ce qu'elle vous permet d'utiliser à la fois les opérations DDL qui ne peuvent pas être lancées dans une transaction et les opérations DML lancées dans une transaction dans une seule migration.


Injection de dépendances dans les classes ChangeLog


Cette fonctionnalité est possible du fait que chaque classe ChangeLog elle-même est un bean ordinaire:


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

Il convient de noter que vous ne pouvez pas injecter de beans ayant des dépendances sur les composants Mongo Data, car au moment où les migrations sont terminées, ils ne sont pas encore initialisés.


Le code source de la bibliothèque est disponible sur Github .

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


All Articles