-
Notifications
You must be signed in to change notification settings - Fork 1.1k
Description
Transaction Rollback Not Working with Spring Data MongoDB 4.3.x and MongoDB Java Driver 5.x
Summary:
When using Spring Data MongoDB 4.3.2 with MongoDB Java Driver 5.0.1 (default via Spring Boot 3.3.2), transaction rollback does not work when using @Transactional
and MongoTransactionManager
.
However, rollback works as expected when using transactions directly via the raw MongoClient
API.
Environment:
- Spring Boot: 3.3.2
- Spring Data MongoDB: 4.3.2
- MongoDB Java Driver: 5.0.1
- MongoDB server: 5.0.29
- Java: 21
Configuration:
@Bean
public MongoTemplate mongoTemplate(MongoClient mongoClient, @Value("${spring.data.mongodb.database}") String databaseName) {
return new MongoTemplate(mongoClient, databaseName);
}
@Bean
public MongoTransactionManager transactionManager(MongoDatabaseFactory dbFactory) {
return new MongoTransactionManager(dbFactory);
}
Problem Description:
- When performing write operations (insert/update/delete) inside a method annotated with
@Transactional
, and an exception is thrown, the transaction manager logs that it is rolling back and callsabortTransaction()
on the session. - However, the changes are not rolled back in the database – the data remains persisted.
- When debugging, I found that in the MongoDB Java Driver, the
ClientSessionImpl.abortTransaction()
method does not actually send an abort command to the server, because the internal flagmessageSentInCurrentTransaction
isfalse
. - This is because Spring Data MongoDB calls
MongoCollection.insertOne(document)
without passing the session as a parameter, so the driver does not associate the operation with the transaction.
Key findings:
- When using the raw
MongoClient
API and passing the session explicitly to all operations, rollback works as expected. - With Spring Data MongoDB, the session is not propagated to the driver operations, so the driver does not recognize any transactional operations, and rollback is effectively a no-op.
Specific code location:
- In
MongoTemplate
, the methodinsertDocument
(and similar methods for update/delete) calls:instead of the session-aware variant:collection.insertOne(mappedDocument.getDocument());
collection.insertOne(session, mappedDocument.getDocument());
- As a result, the driver does not associate the operation with the current transaction session.
Minimal Example:
@Service
public class TxTestService {
@Autowired
private MongoTemplate mongoTemplate;
@Transactional
public void testTx() {
mongoTemplate.insert(new MyEntity("test"));
throw new RuntimeException("rollback!");
}
}
After calling testTx()
, the document is still present in the collection.
Expected behavior:
All changes made within the transaction should be rolled back if an exception occurs.
Actual behavior:
Changes are committed even after rollback is triggered.
Debug details:
MongoTransactionManager
logs show rollback is initiated andabortTransaction()
is called.- In the driver,
messageSentInCurrentTransaction
isfalse
, so no abort command is sent. - The root cause is that
MongoTemplate
does not pass the session to the underlying driver operations.
Steps to reproduce:
- Set up Spring Boot project with the above dependencies and configuration.
- Create a service method with
@Transactional
that writes to MongoDB and throws an exception. - Observe that the data is not rolled back.
Workaround:
Using the raw MongoClient
API with explicit session management works as expected.