1. abstraction
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)
2. test
step1 :
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
MyDaoHibernateImpl 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 transaction (MANDATORY).
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:
@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:
@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
MyDaoHibernateImpl 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, 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 transaction (MANDATORY).