Common Spring Transaction Failure Scenarios and Their Solutions
This article explains typical reasons why @Transactional may not work in Spring applications—such as non‑public methods, beans not managed by the container, internal method calls, non‑RuntimeException throws, swallowed exceptions, wrong propagation settings, and unsupported MySQL storage engines—and provides concrete code examples and fixes for each case.
In real projects, developers often rely on Spring's transaction mechanism to keep data consistent across multiple tables, but many encounter situations where transactions silently fail. This guide lists the most common failure scenarios, explains why they happen, and shows how to resolve them.
Typical failure scenarios
Methods annotated with @Transactional are not public.
The class containing @Transactional is not a Spring‑managed bean.
A @Transactional method is invoked from another method of the same class (self‑invocation).
The business code throws an exception that is not a RuntimeException.
Exceptions are caught without re‑throwing, so the transaction manager never sees them.
Incorrect Propagation setting, e.g., Propagation.NOT_SUPPORTED.
Using MySQL with the MyISAM engine, which does not support transactions.
Non‑public method
Spring proxies only apply transaction advice to public methods. Non‑public methods are ignored.
When using proxies, you should apply the @Transactional annotation only to methods with public visibility.
Solution: keep @Transactional on public methods or switch to AspectJ weaving.
Bean not managed by Spring
If a class is not registered as a Spring bean, its @Transactional annotation has no effect.
/**
* @Author:qxy
*/
// @Service
public class StudentServiceImpl implements StudentService {
@Autowired
private StudentMapper studentMapper;
@Autowired
private ClassService classService;
@Override
@Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
public void insertClassByException(StudentDo studentDo) throws CustomException {
studentMapper.insertStudent(studentDo);
throw new CustomException();
}
}Make sure the class is annotated with @Service, @Component, etc.
Self‑invocation
When a method in the same class calls another @Transactional method, the call bypasses the proxy, so the transaction is not started.
@Service
public class ClassServiceImpl implements ClassService {
@Autowired
private ClassMapper classMapper;
public void insertClass(ClassDo classDo) throws CustomException {
insertClassByException(classDo); // self‑invocation – transaction ignored
}
@Override
@Transactional(propagation = Propagation.REQUIRED)
public void insertClassByException(ClassDo classDo) throws CustomException {
classMapper.insertClass(classDo);
throw new RuntimeException();
}
}
// test case
@Test
public void insertInnerExceptionTest() throws CustomException {
classDo.setClassId(2);
classDo.setClassName("java_2");
classDo.setClassNo("java_2");
classService.insertClass(classDo);
}Fix: invoke the transactional method via the proxy, e.g.
((ClassServiceImpl)AopContext.currentProxy()).insertClassByException(classDo);and enable proxy exposure with @EnableAspectJAutoProxy(exposeProxy = true).
Non‑RuntimeException
By default Spring rolls back only on unchecked exceptions. Throwing a checked exception will not trigger a rollback.
@Service
public class ClassServiceImpl implements ClassService {
@Autowired
private ClassMapper classMapper;
public void insertClass(ClassDo classDo) throws Exception {
((ClassServiceImpl)AopContext.currentProxy()).insertClassByException(classDo);
}
@Override
@Transactional(propagation = Propagation.REQUIRED)
public void insertClassByException(ClassDo classDo) throws Exception {
classMapper.insertClass(classDo);
throw new Exception(); // checked exception – no rollback
}
}Solution: add rollbackFor = Exception.class to the annotation.
@Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
public void insertClassByException(ClassDo classDo) throws Exception {
classMapper.insertClass(classDo);
throw new Exception();
}Catching exceptions without re‑throw
If a transactional method catches an exception and does not re‑throw it, the transaction manager assumes the method completed successfully.
@Service
public class ClassServiceImpl implements ClassService {
@Autowired
private ClassMapper classMapper;
public void insertClass(ClassDo classDo) {
((ClassServiceImpl)AopContext.currentProxy()).insertClassByException(classDo);
}
@Override
@Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
public void insertClassByException(ClassDo classDo) {
classMapper.insertClass(classDo);
try {
int i = 1 / 0;
} catch (Exception e) {
e.printStackTrace(); // swallowed
}
}
}Fix: re‑throw a runtime exception inside the catch block.
catch (Exception e) {
e.printStackTrace();
throw new RuntimeException();
}Wrong propagation setting
Using Propagation.NOT_SUPPORTED disables transaction creation, so any exception will not cause a rollback.
@Transactional(propagation = Propagation.NOT_SUPPORTED, rollbackFor = Exception.class)
public void insertClassByException(ClassDo classDo) {
classMapper.insertClass(classDo);
try {
int i = 1 / 0;
} catch (Exception e) {
e.printStackTrace();
throw new RuntimeException();
}
}Usually you should use Propagation.REQUIRED or another appropriate setting.
Unsupported storage engine
MySQL tables using the MyISAM engine do not support transactions. Switch to InnoDB (default from MySQL 5.5) to enable transactional behavior.
By understanding these pitfalls and applying the shown corrections, developers can ensure that Spring transactions work reliably in their applications.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
Top Architect
Top Architect focuses on sharing practical architecture knowledge, covering enterprise, system, website, large‑scale distributed, and high‑availability architectures, plus architecture adjustments using internet technologies. We welcome idea‑driven, sharing‑oriented architects to exchange and learn together.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
