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 whichhandleIncomingOrders` 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