We have a Spring Transaction rollback issues, where rollback doesn't seems to be working.
Within my service layer method which is annotated with @Transactional
I call three different DAOImpl
classes to insert 3 records.
The middle insert do a get from a 4th table to populate a description field but this get failed. I expect the first insert to rollback but it doesn't seems to be happening.
Few Points:
org.springframework.jdbc.datasource.DataSourceTransactionManager
and MySQL datasource
defined in applicationContext.xml
. Beans are created in Beans.xml
which is imported into ApplicationContext.xml
@Transactional
annotation in DAO
layer <tx:annotation-driven transaction-manager="transactionManager"/>
again in applicationContext.xml
UPDATE:
Code snippets....
Service Class- This is somthing similar to what I have .... I tested with and without @Autowired. The transaction enable method is called within the service class.
public class CustomerService { //@Autowired CustomerOrderDAO customerOrderDAOImpl; //@Autowired CustomerItemDAO customerItemDAOImpl; //@Autowired CustomerPromotionDAO customerPromotionDAOImpl; //@Autowired PromotionDAO promotionDAOImpl; //other variables public CustomerOrder handleIncomingOrders(CustomerOrder customerOrder) { try { saveOrderDetails(customerOrder); ..... return customerOrder; } catch (Exception e) //TO-DO catch proper exception { //Send error response ....... return customerOrder; } } @Transactional public void saveOrderDetails(CustomerOrder customerOrder) throws Exception { customerOrderDAOImpl.create(customerOrder); .... while (promotionsIterator.hasNext()) { customerPromotion.setPromotionName(promotionDAOImpl.getName(customerOrder.getPromotionId)); customerPromotionDAOImpl.create(customerPromotion); } ...... while (customerItemIterator.hasNext()) { customerItemDAOImpl.create(customerItem); } } }
Any idea? Thanks.
The default behaviour of @Transactional
is that transactional behaviour is added with a proxy around the object (the CustomerService
in your example). From the reference docs (scroll down):
In proxy mode (which is the default), only external method calls coming in through the proxy are intercepted. This means that self-invocation, in effect, a method within the target object calling another method of the target object, will not lead to an actual transaction at runtime even if the invoked method is marked with @Transactional.
In your example, an external call to the handlingIncomingOrders()
passes through the proxy and hits the target object (an instance of the CustomerService
). However, the subsequent call to saveOrderDetails()
is a normal method call inside the target object, thus the transactional behaviour in the proxy is never invoked. However, if the saveOrderDetails()
was called from another class, you will find that the transactional behaviour will work as expected.
The solution in your case would be calling saveOrderDetails(customerOrder);
as proxyBean.saveOrderDetails(customerOrder);
Where proxybean is the Object on which
handleIncomingOrders` is being called.
If CustomerService
is singleton
(Defualt scope) it can be as simple as adding below code to the Service class. (adding a self reference as autowired)
//@Autowired
CustomerService customerService; // As this is injected its a proxy
and in the Method use it as
public CustomerOrder handleIncomingOrders(CustomerOrder customerOrder) {
try {
customerService.saveOrderDetails(customerOrder);
.....
return customerOrder;
} catch (Exception e) //TO-DO catch proper exception
{
//Send error response
.......
return customerOrder;
}
}
If its scope is Prototype
the one of possible simple solution will be as follows.
public CustomerOrder handleIncomingOrders(CustomerOrder customerOrder, CustomerService customerService) {
try {
customerService.saveOrderDetails(customerOrder);
.....
return customerOrder;
} catch (Exception e) //TO-DO catch proper exception
{
//Send error response
.......
return customerOrder;
}
}
And where you are calling handleIncomingOrders
use changes suggested in below code.
bean.handleIncomingOrders(customerOrder); //Suppose this is old code
Change it to
bean.handleIncomingOrders(customerOrder, bean);// THough it appears as we are sending reference to `THIS` as parameter whcihc can be unnecessary, in case of `Proxy`while inside your method `this` and `Passed reference` will point to different Obejects.
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