I have three different sources to which I want to send information:
An Oracle database, related to a JpaRepository. A MongoDB, related to a MongoRepository. And a Kafka topic, which has configured via yml all the necessary data for producing into that topic. I want to synchronize these resources with Spring Tx. I have a JpaTransactionManager related to the Oracle db, another MongoTransactionManager related to the MongoDB, and the KafkaTransactionManager created by Spring automatically when the transactions are activated.
The idea I had in mind was the following one:
First, I would have a basic controller that would autowire a service:
@RestController
public class SimpleController {
@Autowired
private SimpleService simpleService;
@PostMapping(...)
public Response doTx () {
simpleService.doTx();
}
Then, that service would autowire at the same time another three services, one for each resource I have:
@Service
public class SimpleService {
@Autowired
private OracleService oracleService;
@Autowired
private MongoService mongoService;
@Autowired
private KafkaService kafkaService;
...
}
Such SimpleService, would have a method anotated with the @Transactional annotation in which these services are called:
@Service
public class SimpleService {
@Transactional
public void doTx () {
oracleService.doTx();
mongoService.doTx();
kafkaService.doTx();
}
}
And, each of these calls to these methods would be annotated with the @Transactional annotation too, with their correspondent Transaction Manager each:
public class OracleService {
@Autowired
JpaRepository jpaRepository
@Transactional( tm = "oracleJpaTransactionManager" )
public void doTx(){
jpaRepository.save();
}
}
What I would hope of this, is to initiate a transaction in SimpleService, and each of the other services that I call, join such transaction, and commit one after the other just when the method is about to return, at the end.
But this doesn't work. The only way it "works" (and I say "works" because it doesn't allow me to fully customize the order of committing the resources) is having all the resources in one service, all called from the same method:
@Service
public class SimpleService {
@Autowired
private JpaRepository oracleJpaRepository;
@Autowired
private MongoRepository mongoRepository;
@Autowired
private KafkaTemplate kafkaTemplate;
@Transactional(transactionManager = "oracleJpaTransactionManager")
public void doTx() {
oracleJpaRepository.save();
mongoRepository.save();
kafkaTemplate.send();
}
So, the thing is, should this work out of the box, and recognize all the transactions as they get executed, or is this behavior not supported?
I tried to synchronize transactions with the above approach, but it would just commit whenever the method was called, not joining the previous transaction and waiting for the doTx() method in the SimpleService class to return.
EDIT: I'm not trying to achieve full transactionality. Just synchronization, a Best Effort 1 Phase Commit. I know there may be some data inconsistency when synchronizing these resources, compensation is not a problem, the key is the chaining of the commits at the end of the method.
Comment From: mdeinum
You would need 3 @Transactional
annotations on your SimpleService
one for each specific transaction manager to achieve (sort of) synchronization. There is no single transaction each resource has its own transaction and each will commit by itself, so there is no real joining one transaction or not.
Comment From: khaeghar
So, you're saying something like:
@Service
public class SimpleService {
@Transactional
public void doTx () {
doOracleTx();
doMongoTx();
doKafkaTx();
}
@Transactional(tm ="jpaTransactionManager")
doOracleTx();
@Transactional(tm ="mongoTransactionManager")
doMongoTx();
@Transactional(tm ="kafkaTransactionManager")
doKafkaTx();
}
But, as far as I know, I think this wouldn't work due to the fact that you cannot call methods annotated with @Transactional from the same class, since the proxy would not intercept those methods and therefore not applying the @Transactional annotation interceptor. Am I wrong?
Besides, what's the difference between that and having 4 classes, a principal one and three other services with those methods with each its transactional manager? Wouldn't it be the same?
Btw, lemme edit the example so I can clearly state that each @Transactional method that invokes a resource has its own TransactionManager. Didn't specify that there.
Comment From: snicoll
Thanks for getting in touch, but it feels like this is a question that would be better suited to Stack Overflow. As mentioned in the guidelines for contributing, we prefer to use the issue tracker only for bugs and enhancements. Feel free to update this issue with a link to the re-posted question (so that other people can find it) or add some more details if you feel this is a genuine bug.
Comment From: khaeghar
@snicoll
The link to the stackoverflow question is this:
https://stackoverflow.com/questions/76454963/how-to-synchronize-transactions-with-spring-tx?noredirect=1#comment134811322_76454963
And I agree that this sounds like a question well suited for Stack Overflow, but this also looks like a bug, since the @Transactional annotation is not working as expected.
Comment From: mdeinum
I stated exactly the same answer in your question as I stated here. And no not on the individual methods but 1 method with 3 transactions or create yuour own ChainedTransactionManager
(as answered on stackoverflow as well). Transactions work as designed and as expected, as there are 3 separate transactions.
And now lets continue on stackoverflow.