每个开发人员迟早都需要迁移数据库中的数据。 在我们的项目中,我们使用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上获得 。