Hello,
I would expose a simple post with 2 examples concerning the propagation of a transaction in the application layers (Spring MVC controller, Manager layer, Service Layer, DAO layer) with the use of Propagation.REQUIRED and the readOnly attribute in the @Transactionnal annotation like:
@Transactional(readOnly = true, propagation = Propagation.REQUIRED)
See the official Spring documentation concerning the propagation.
TEST n°1:
In this 1st test, we will study a simple example with a no-transactional Spring MVC controller MyController:
public class MyController extends MultiActionController { public ModelAndView handleMyAction(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException, InterruptedException { //... myManager.myMethod(param1); //... } }
… which calls a manager class MyManagerImpl in the sub-layer:
@Transactional(readOnly = true) @Service("myManager") public class MyManagerImpl implements MyManager { @Override @Transactional(readOnly = false, propagation = Propagation.REQUIRED) public void myMethod(String param1){ myService.myMethod(param1); } ... }
… which calls a service class MyServiceImpl in the sub-layer:
@Transactional(readOnly = true) @Service("myService") public class MyServiceImpl implements MyService { @Override @Transactional(readOnly = false, propagation = Propagation.REQUIRED) public void myMethod(String param1){ //... myDao.createOrUpdate(myObject); //... } }
… which calls a DAO class MyServiceImpl in the sub-layer:
@Transactional(propagation=Propagation.MANDATORY) public class MyDaoHibernateImpl extends GenericDaoImpl<MyObject, String> implements MyDao { public void createOrUpdate(T o) { //... if (o instanceof AbstractPersistentObject) { if (((AbstractPersistentObject) o).isCreation()) { getSession().saveOrUpdate(o); } else { getSession().merge(o); } } else { throw new RuntimeException("this method support only AbstractPersistentObject"); } //... } }
So, the result is that the data (in database) are modified/updated because:
- the Spring MVC controller MyController is no-transactional component,
- the method MyManagerImpl.myMethod(String param1) is NOT READONLY and supports the current transaction, or will create a new one if none exists (REQUIRED),
- the method MyServiceImpl.myMethod(String param1) is also NOT READONLY and supports the current transaction, or will create a new one if none exists (REQUIRED),
- and the latest MyDaoHibernateImpl supports the current transaction and throws an exception if none exists (MANDATORY).
TEST n°2:
In this 2nd test, we will study a more complex example with a no-transactional Spring MVC controller MyController:
[NO CHANGE]
public class MyController extends MultiActionController { public ModelAndView handleMyAction(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException, InterruptedException { //... myManager.myMethod(param1); //... } }
… which calls a manager class MyManagerImpl in the sub-layer. In this example, we will modify the attribute readOnly = false to the value readOnly = true:
[CHANGED]
@Transactional(readOnly = true) @Service("myManager") public class MyManagerImpl implements MyManager { @Override @Transactional(readOnly = true, propagation = Propagation.REQUIRED) public void myMethod(String param1){ myService.myMethod(param1); } ... }
… which calls a service class MyServiceImpl in the sub-layer:
[NO CHANGE]
@Transactional(readOnly = true) @Service("myService") public class MyServiceImpl implements MyService { @Override @Transactional(readOnly = false, propagation = Propagation.REQUIRED) public void myMethod(String param1){ //... myDao.createOrUpdate(myObject); //... } }
… which calls a DAO class MyServiceImpl in the sub-layer:
[NO CHANGE]
@Transactional(propagation=Propagation.MANDATORY) public class MyDaoHibernateImpl extends GenericDaoImpl<MyObject, String> implements MyDao { public void createOrUpdate(T o) { //... if (o instanceof AbstractPersistentObject) { if (((AbstractPersistentObject) o).isCreation()) { getSession().saveOrUpdate(o); } else { getSession().merge(o); } } else { throw new RuntimeException("this method support only AbstractPersistentObject"); } //... } }
So, we have:
- the Spring MVC controller MyController is no-transactional component,
- the method MyManagerImpl.myMethod(String param1) is READONLY and supports the current transaction, or will create a new one if none exists (REQUIRED),
- the method MyServiceImpl.myMethod(String param1) is also NOT READONLY and supports the current transaction, or will create a new one if none exists (REQUIRED),
- and the latest MyDaoHibernateImpl supports the current transaction and throws an exception if none exists (MANDATORY).
… the result is that the data (in database) are NOT modified/updated because the method MyServiceImpl.myMethod(String param1) supports the current transaction which has been created READNLY in the method MyManagerImpl.myMethod(String param1).
That’s all!!!
Huseyin OZVEREN