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.

Top Architect
Top Architect
Top Architect
Common Spring Transaction Failure Scenarios and Their Solutions

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.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

javatransactionaopException Handlingspringmysql
Top Architect
Written by

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.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.