I have to update two data sources as part of one transaction. That is -
If update in DB2 fails, I want to roll back both DB1 and DB2 to roll back. Can this be accomplished using @Transactional ?
Here is a sample code -
@Transactional(value="db01TransactionManager") public void updateDb01() { Entity01 entity01 = repository01.findOne(1234); entity01.setName("Name"); repository01.save(entity01); //Calling method to update DB02 updateDb02(); } @Transactional(value="db02TransactionManager") public void updateDb02() { Entity02 entity02 = repository02.findOne(1234); entity02.setName("Name"); repository02.save(entity02); //Added this to force a roll back for testing TransactionAspectSupport.currentTransactionStatus().setRollbackOnly(); }
My problem is that, the setRollbackOnly() in updateDb02 rolls back only the the Db01 transaction.
If your data is distributed across multiple databases, you may wish to update one database while reading from one or more other databases. This type of access can be performed within a single unit of work (transaction). This type of database access is called multisite update or two-phase commit.
Transactions and Proxies. At a high level, Spring creates proxies for all the classes annotated with @Transactional, either on the class or on any of the methods. The proxy allows the framework to inject transactional logic before and after the running method, mainly for starting and committing the transaction.
Instead of using ChainedTransactionManager for attaching callbacks to transaction commit (pre commit/post commit), either register a TransactionSynchronization to explicitly follow transaction cleanup with simplified semantics in case of exceptions.
The controller can be made @Transactional , but indeed it's a common recommendation to only make the service layer transactional (the persistence layer should not be transactional either).
I've solved this problem using ChainedTransactionManager - http://docs.spring.io/spring-data/commons/docs/1.6.2.RELEASE/api/org/springframework/data/transaction/ChainedTransactionManager.html
Spring Boot Configuration:
@Bean(name = "chainedTransactionManager") public ChainedTransactionManager transactionManager(@Qualifier("primaryDs") PlatformTransactionManager ds1, @Qualifier("secondaryDs") PlatformTransactionManager ds2) { return new ChainedTransactionManager(ds1, ds2); }
And then you can use it as follows:
@Transactional(value="chainedTransactionManager") public void updateDb01() { Entity01 entity01 = repository01.findOne(1234); entity01.setName("Name"); repository01.save(entity01); //Calling method to update DB02 updateDb02(); } public void updateDb02() { Entity02 entity02 = repository02.findOne(1234); entity02.setName("Name"); repository02.save(entity02); //Added this to force a roll back for testing TransactionAspectSupport.currentTransactionStatus().setRollbackOnly(); }
The best way is to create a third method, that will be annotated as @Transactional
.
@Transactional(readOnly = false) public void updateCommon(){ upbateDb01(); upbateDb02(); }
According to a spring documentation, transaction control starts when the firts annotation appears,so in this case a single transaction will start when updateCommon
will be invoked. UPDATE But this will work if you use CrudRepository
or something like that.
In case of multiple datasources you may try to use a Global transaction management conception. Here is a sample from a spring documentation:
@Inject private PlatformTransactionManager txManager; TransactionTemplate template = new TransactionTemplate(this.txManager); template.execute( new TransactionCallback<Object>(){ public void doInTransaction(TransactionStatus status){ // work done here will be wrapped by a transaction and committed. // the transaction will be rolled back if // status.setRollbackOnly(true) is called or an exception is thrown } });
And here is a link: http://spring.io/blog/2011/08/15/configuring-spring-and-jta-without-full-java-ee/ I have never use it for my own, so I didn't explore this topic deeply. Hope it will help
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With