Use multi-document ACID transactions in MongoDB 4.0+ with session management and error handling.
Published April 10, 2025
MongoDB 4.0 introduced multi-document ACID transactions, enabling the same reliability guarantees as relational databases for operations spanning multiple documents or collections.
Use transactions when you need atomicity across multiple writes:
For single-document writes, MongoDB is always atomic by default — no transaction needed.
@Service
@RequiredArgsConstructor
public class PaymentService {
private final MongoTemplate mongoTemplate;
private final MongoDatabaseFactory dbFactory;
public void transferFunds(String fromId, String toId, BigDecimal amount) {
MongoTransactionManager txManager = new MongoTransactionManager(dbFactory);
TransactionTemplate txTemplate = new TransactionTemplate(txManager);
txTemplate.execute(status -> {
// Debit
mongoTemplate.updateFirst(
Query.query(Criteria.where("_id").is(fromId)
.and("balance").gte(amount)),
new Update().inc("balance", amount.negate()),
Account.class
);
// Credit
mongoTemplate.updateFirst(
Query.query(Criteria.where("_id").is(toId)),
new Update().inc("balance", amount),
Account.class
);
return null;
});
}
}
// application.properties
spring.data.mongodb.uri=mongodb://localhost:27017/mydb
// Configuration
@Configuration
public class MongoConfig extends AbstractMongoClientConfiguration {
@Bean
MongoTransactionManager transactionManager(MongoDatabaseFactory dbFactory) {
return new MongoTransactionManager(dbFactory);
}
}
// Service
@Service
public class OrderService {
@Transactional // Works with MongoTransactionManager
public Order createOrder(CreateOrderRequest request) {
Order order = orderRepository.save(new Order(request));
inventoryService.decrementStock(request.items()); // same transaction
return order;
}
}
// MongoDB shell / driver
const session = client.startSession();
session.startTransaction({
readConcern: { level: 'snapshot' },
writeConcern: { w: 'majority' }
});
try {
await accounts.updateOne(
{ _id: fromId }, { $inc: { balance: -amount } }, { session }
);
await accounts.updateOne(
{ _id: toId }, { $inc: { balance: amount } }, { session }
);
await session.commitTransaction();
} catch (error) {
await session.abortTransaction();
throw error;
} finally {
session.endSession();
}
// Strongest consistency for financial operations
MongoTransactionManager txManager = new MongoTransactionManager(
dbFactory,
TransactionOptions.builder()
.readConcern(ReadConcern.SNAPSHOT)
.writeConcern(WriteConcern.MAJORITY)
.build()
);