如何实现Spring Data Mongo多数据库事务

时间:2020-06-19 10:13:59

标签: spring mongodb transactions multiple-databases

我创建了一个应用程序,该应用程序基于此tutorial将数据从一个数据库复制到另一个数据库。 实际上,我需要创建一种在事务中插入两个不同数据库的方法。 MongoDB有可能吗?以及如何实施?

1 个答案:

答案 0 :(得分:0)

要在Spring Data MongoDB中的多个数据库中使用MongoDB中的多文档事务,您需要为每个数据库配置MongoTemplate,但是它们都必须使用相同的MongoDbFactory,因为它被视为交易资源。

这意味着您将需要重写MongoTemplate的几种方法以使其使用应使用的数据库(而不是SimpleMongoClientDbFactory内部配置的数据库)。

让我们假设您的数据库被称为“一个”和“两个”。然后是这样的:

public class MongoTemplateWithFixedDatabase extends MongoTemplate {
    private final MongoDbFactory mongoDbFactory;
    private final String databaseName;

    public MongoTemplateWithFixedDatabase(MongoDbFactory mongoDbFactory,
            MappingMongoConverter mappingMongoConverter, String databaseName) {
        super(mongoDbFactory, mappingMongoConverter);
        this.mongoDbFactory = mongoDbFactory;
        this.databaseName = databaseName;
    }

    @Override
    protected MongoDatabase doGetDatabase() {
        return MongoDatabaseUtils.getDatabase(databaseName, mongoDbFactory, ON_ACTUAL_TRANSACTION);
    }
}

@Bean
public MongoDbFactory mongoDbFactory() {
    // here, some 'default' database name is configured, the following MongoTemplate instances will ignore it
    return new SimpleMongoDbFactory(mongoClient(), getDatabaseName());
}

@Bean
public MongoTransactionManager mongoTransactionManager() {
    return new MongoTransactionManager(mongoDbFactory());
}

@Bean
public MongoTemplate mongoTemplateOne(MongoDbFactory mongoDbFactory,
        MappingMongoConverter mappingMongoConverter) {
    MongoTemplate template = new MongoTemplateWithFixedDatabase(mongoDbFactory,
            mappingMongoConverter, "one");
    return template;
}

@Bean
public MongoTemplate mongoTemplateTwo(MongoDbFactory mongoDbFactory,
        MappingMongoConverter mappingMongoConverter) {
    MongoTemplate template = new MongoTemplateWithFixedDatabase(mongoDbFactory,
            mappingMongoConverter, "two");
    return template;
}

然后只需在服务中注入mongoTemplateOnemongoTemplateTwo,并用@Transactional标记其方法即可。

反应性情况

在反应性情况下,它非常相似。当然,您需要使用诸如ReactiveMongoTemplateReactiveMongoDatabaseFactoryReactiveMongoTransactionManager之类的反应式版本。

还有一些警告。首先,您必须重写3种方法,而不是2种(因为getCollection(String)也需要重写)。另外,我必须使用抽象类来使其起作用:

@Bean
public ReactiveMongoOperations reactiveMongoTemplateOne(
        @ReactiveMongoDatabaseFactory reactiveMongoDatabaseFactory,
        MappingMongoConverter mappingMongoConverter) {
    ReactiveMongoTemplate template = new ReactiveMongoTemplate(reactiveMongoDatabaseFactory,
            mappingMongoConverter) {

        @Override
        protected Mono<MongoDatabase> doGetDatabase() {
            return ReactiveMongoDatabaseUtils.getDatabase("one", reactiveMongoDatabaseFactory,
                    ON_ACTUAL_TRANSACTION);
        }

        @Override
        public MongoDatabase getMongoDatabase() {
            return reactiveMongoDatabaseFactory.getMongoDatabase(getDatabaseName());
        }

        @Override
        public MongoCollection<Document> getCollection(String collectionName) {
            Assert.notNull(collectionName, "Collection name must not be null!");

            try {
                return reactiveMongoDatabaseFactory.getMongoDatabase(getDatabaseName())
                        .getCollection(collectionName);
            } catch (RuntimeException e) {
                throw potentiallyConvertRuntimeException(e,
                        reactiveMongoDatabaseFactory.getExceptionTranslator());
            }
        }

        private RuntimeException potentiallyConvertRuntimeException(RuntimeException ex,
                PersistenceExceptionTranslator exceptionTranslator) {
            RuntimeException resolved = exceptionTranslator.translateExceptionIfPossible(ex);
            return resolved == null ? ex : resolved;
        }
    };
    return template;
}

P.S。所提供的代码已在spring-data-mongodb 2.2.4中进行了测试。