Reputation: 3243
Due to certain reasons, I have manually performed transaction commit and roll back using Spring PlatformTransactionManager. I want to set up a hook so that a post commit action takes place after transaction has been committed.
By looking at:
void commit(TransactionStatus status) throws TransactionException;
I can't see how I can determine a transaction was successful other than assuming it so if no exception are thrown.
And I could use AOP as one option, but what about programmatically doing it, maybe using callback method?
Upvotes: 51
Views: 66647
Reputation: 3250
Since Spring 4.2, it has been possible to define listeners for post commit events (or more generally transaction synchronization events e.g. rollbacks) using annotation based configuration. This is based off event handling in core spring. It is easier to test code using this approach since you avoid a direct dependency on TransactionSynchronizationManager, which will likely not be active in a unit test. You can easily test that your transactional service publishes an event and also that your listener performs the right action when you receive an event.
So, without further ado, this is how to set it up:
In this example, we'll assume you have a Customer
entity and a CustomerRepository
(and ORM to go with it).
First, you need a new event type NewCustomerEvent
:
// NewCustomerEvent.java
// Just a regular pojo for the event
public class NewCustomerEvent {
public String email;
// constructor, setters and getters omitted
}
Then, you define a listener using @TransactionalEventListener
. By default, this will execute after a successful commit but this can be changed using the phase
parameter:
// NewCustomerEventListener.java
@Component
public class NewCustomerEventListener {
@TransactionalEventListener
public void handleNewCustomerEvent(NewCustomerEvent newCustomerEvent) {
// handle new customer event
}
}
Finally, you augment your transaction service with an ApplicationEventPublisher
on which you call publish once all the transaction statements have been sent.
// CustomerRespositoryService.java
@Service
public class CustomerRepositoryService {
@Inject
private ApplicationEventPublisher applicationEventPublisher;
@Inject
private CustomerRepository customerRepository;
@Transactional
public void createCustomer(String email) {
Customer customer = new Customer(email);
customerRespotory.save(customer);
applicationEventPublisher.publish(new NewCustomerEvent(email));
}
}
See also:
Upvotes: 23
Reputation: 12181
Credit to Grooveek's answer and Alex's comment under it - I put this here because the combined suggestions provide a solid and cleaner solution that is hard to find around the net.
Using Spring 4+. if you need a callback on a @Transactional
method after it successfully commits just add that in the beginning of the method:
@Service
public class OneService {
@Autowired
OneDao dao;
@Transactional
public void a transactionalMethod() {
TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronization(){
public void afterCommit(){
//do stuff right after commit
System.out.println("commit!!!");
}
});
//do db stuff
dao.save();
}
}
Upvotes: 35
Reputation: 4440
In one of my projects because of certain reasons I also had to use PlatformTransactionManager
. So I forced to use org.springframework.transaction.support.TransactionTemplate
.
The main benefit is that if you have implemented PlatformTransactionManager correctly, you don't need to bother with manual commit/rollback. At least source code of TransactionTemplate may help you if you need more specific thing.
It's pretty simply to use:
config.xml
<bean name="transactionTemplate"
class="org.springframework.transaction.support.TransactionTemplate">
<property name="transactionManager" ref="platformTransactionManager"/>
</bean>
MyServiceImpl.java
@Service
public class MyServiceImpl implements MyService {
@Autowired
private TransactionTemplate transactionTemplate;
public Entity getSomethingWithTx(final long id) {
return transactionTemplate.execute(new TransactionCallback<Entity>() {
@Override
public Entity doInTransaction(TransactionStatus status) {
//TODO implement
}
});
}
}
Upvotes: 1
Reputation: 10094
You could get exactly what you want by a simpler way, with TransactionSynchronizationManager
and TransactionSynchronization
With TransactionSynchronizationManager
, you have static methods to get information about current transaction, and you can register a TransactionSynchronization
wich allows you to automatically do a post-commit as you call that
TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronization(){
void afterCommit(){
//do what you want to do after commit
}
})
Be aware that the TransactionSynchronization is on a per-thread basis (which is often not a problem for a basic web request).
Upvotes: 95