Migración de datos con mongoDB y Spring Boot

Cada desarrollador, tarde o temprano, enfrenta la necesidad de migrar datos en una base de datos. En nuestro proyecto, usamos mongoDB como base de datos. Abordamos la migración de datos de diferentes maneras:


  • escribió scripts js y se ejecutó directamente en la base de datos
  • Mongobee usado - una herramienta para migraciones automáticas


    Mongobee funcionó bien hasta que nos encontramos con una situación en la que queríamos agregar un nuevo campo con un índice único. Digamos que tenemos una clase:


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

    Ahora agregamos un nuevo campo:


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

    Escribimos una migración, que debería llenar el campo text2 en todos los documentos con valores únicos:


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

    Pero cuando comenzamos la aplicación, vimos que Spring lanzó los componentes de Mongo Data antes de que comenzara nuestra migración y, por lo tanto, se produjo un error al crear automáticamente el índice sobre el campo text2 , ya que este campo aún no ha sido completado por ningún documento.
    Después de este incidente, decidimos abandonar Mongobee e intentar escribir nuestra propia herramienta, lo que también facilitaría la escritura de migraciones, pero además tendría características como:


  • Ejecutar antes de Mongo Data
  • Soporte de transacciones agregado en MongoDB 4.0
  • Inyección de dependencias en clases ChangeLog

El resultado es una biblioteca llamada Mongration, que admite toda la funcionalidad descrita anteriormente.


Spring Boot Lifecycle Support


La primera función se implementa mediante la configuración automática, que comienza después de crear MongoClient y antes de escanear los repositorios:


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

Pero el problema con la configuración automática es que si usa la anotación @EnableMongoRepositories para activar los repositorios, entonces los componentes de Mongo Data se inicializan antes de nuestra configuración automática. Para evitar este problema, debe usar la anotación @EnableMongration junto con @EnableMongoRepositories :


 @Configuration @EnableMongoRepositories @EnableMongration public class MongoRepositoriesConfiguration { } 

La @EnableMongration no hace más que iniciar la misma configuración, solo le permite ejecutarla antes:


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

Soporte de transacciones


Para admitir transacciones, debe configurar el conjunto de réplicas MongoDB. También es necesario declarar el bean MongoTransationManager (si Mongration no encuentra este bean en contexto, lo creará por sí mismo). Mongration le permite usar transacciones de dos maneras:


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


El segundo método es útil porque le permite utilizar tanto las operaciones DDL que no pueden iniciarse en una transacción como las operaciones DML iniciadas en una transacción en una sola migración.


Inyección de dependencias en clases ChangeLog


Esta funcionalidad es posible debido al hecho de que cada clase ChangeLog es un bean ordinario:


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

Vale la pena señalar que no puede inyectar beans que tengan dependencias en los componentes de Mongo Data, porque para cuando se completan las migraciones aún no se han inicializado.


El código fuente de la biblioteca está disponible en Github .

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


All Articles