使用mongoDB和Spring Boot进行数据迁移

每个开发人员迟早都需要迁移数据库中的数据。 在我们的项目中,我们使用mongoDB作为数据库。 我们以不同的方式处理数据迁移:


  • 编写了js脚本并直接在数据库中运行
  • 二手的Mongobee-自动迁移工具


    Mongobee运作良好,直到遇到我们想要添加具有唯一索引的新字段的情况。 假设我们有一个课程:


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

    现在,我们添加一个新字段:


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

    我们编写了一个迁移,该迁移应使用唯一值填充所有文档中的text2字段:


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

    但是,当我们启动应用程序时,我们发现Spring在迁移开始之前就启动了Mongo Data组件,因此在text2字段上方自动创建索引时发生错误,因为该字段尚未被任何文档填充。
    在此事件之后,我们决定放弃Mongobee并尝试编写我们自己的工具,这也将使编写迁移文件变得容易,但除此之外,它还具有以下功能:


  • 在Mongo Data之前运行
  • MongoDB 4.0中添加了事务支持
  • ChangeLog类中的依赖注入

结果是一个名为Mongration的库,该库支持上述所有功能。


Spring Boot生命周期支持


第一个功能是使用自动配置实现的,该自动配置在创建MongoClient之后并在扫描存储库之前启动:


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

但是自动配置的问题是,如果您使用@EnableMongoRepositories批注来激活存储库,则在我们的自动配置之前会初始化Mongo Data组件。 为避免此问题,必须将@EnableMongration批注与@EnableMongoRepositories一起使用:


 @Configuration @EnableMongoRepositories @EnableMongration public class MongoRepositoriesConfiguration { } 

@EnableMongration只不过启动相同的配置,仅允许您更早地运行它:


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

交易支持


要支持事务,必须配置副本集MongoDB。 还必须声明MongoTransationManager bean(如果Mongration在上下文中未找到此bean,它将自行创建)。 Mongration允许您以两种方式使用事务:


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


第二种方法很有用,因为它允许您在一次迁移中使用无法在事务中启动的DDL操作和在事务中启动的DML操作。


ChangeLog类中的依赖注入


由于每个ChangeLog类本身都是普通的bean,因此可以使用此功能:


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

值得注意的是,您不能注入依赖于Mongo Data组件的bean,因为到迁移完成时,它们尚未初始化。


该库的源代码可在Github上获得

Source: https://habr.com/ru/post/zh-CN451798/


All Articles