Migração de dados com mongoDB e Spring Boot

Todo desenvolvedor, mais cedo ou mais tarde, enfrenta a necessidade de migrar dados em um banco de dados. Em nosso projeto, usamos o mongoDB como um banco de dados. Abordamos a migração de dados de diferentes maneiras:


  • escreveu scripts js e correu diretamente no banco de dados
  • Mongobee usado - uma ferramenta para migrações automáticas


    Mongobee funcionou bem até encontrarmos uma situação em que queríamos adicionar um novo campo com um índice exclusivo. Digamos que temos uma aula:


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

    Agora adicionamos um novo campo:


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

    Escrevemos uma migração, que deve preencher o campo text2 em todos os documentos com valores exclusivos:


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

    Porém, quando iniciamos o aplicativo, vimos que o Spring lançou os componentes do Mongo Data antes do início da migração e, portanto, ocorreu um erro ao criar automaticamente o índice acima do campo text2 , pois esse campo ainda não foi preenchido por nenhum documento.
    Após esse incidente, decidimos abandonar o Mongobee e tentar escrever nossa própria ferramenta, o que também facilitaria a migração de migrações, mas além disso, apresentaria recursos como:


  • Executar antes do Mongo Data
  • Suporte à transação adicionado no MongoDB 4.0
  • Injeção de dependência nas classes ChangeLog

O resultado é uma biblioteca chamada Mongration, que suporta todas as funcionalidades descritas acima.


Suporte ao ciclo de vida de inicialização por mola


A primeira função é implementada usando a configuração automática, iniciada após a criação do MongoClient e antes da varredura dos repositórios:


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

Mas o problema com a configuração automática é que, se você usar a anotação @EnableMongoRepositories para ativar repositórios, os componentes do Mongo Data serão inicializados antes da nossa configuração automática. Para evitar esse problema, você deve usar a anotação @EnableMongration junto com @EnableMongoRepositories :


 @Configuration @EnableMongoRepositories @EnableMongration public class MongoRepositoriesConfiguration { } 

A @EnableMongration nada mais faz do que iniciar a mesma configuração, apenas permite executá-la mais cedo:


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

Suporte de transação


Para oferecer suporte a transações, você deve configurar o MongoDB do conjunto de réplicas. Também é necessário declarar o bean MongoTransationManager (se o Mongration não encontrar esse bean no contexto, ele o criará por si só). O Mongration permite usar transações de duas maneiras:


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


O segundo método é útil, pois permite o uso de operações DDL que não podem ser iniciadas em uma transação e operações DML iniciadas em uma transação em uma única migração.


Injeção de dependência nas classes ChangeLog


Essa funcionalidade é possível devido ao fato de que cada classe ChangeLog em si é um bean comum:


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

Vale ressaltar que você não pode injetar beans que possuem dependências nos componentes do Mongo Data, porque no momento em que as migrações são concluídas, elas ainda não foram inicializadas.


O código fonte da biblioteca está disponível no Github .

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


All Articles