Why Spring Transactions Fail and How to Fix Them
This article examines common scenarios that cause Spring transaction management to fail—such as non‑public methods, swallowed exceptions, incorrect rollback settings, self‑invocation, final methods, wrong propagation, asynchronous threads, unsupported database engines, and misuse of @Transactional—and provides concrete code‑level solutions to ensure reliable transactional behavior.
1. Introduction
In the Spring framework, transaction management is essential for data consistency and system reliability, but developers often encounter situations where Spring transactions become ineffective. This article summarizes and analyzes various scenarios that cause Spring transaction failures and offers solutions to help you avoid these pitfalls.
2. Transaction Failure Scenarios
2.1 Non‑public Methods
<code>@Transactional
protected void save() {
Person person = new Person();
person.setAge(36);
person.setName("张三");
int result = jdbcTemplate.update("insert into t_person (age, name) values (?, ?)", person.getAge(), person.getName());
System.out.println("save Db Update " + result + " 次");
System.out.println(1 / 0);
}</code>Spring only applies @Transactional to public methods by default. To enable it for protected methods, define a custom TransactionAttributeSource bean:
<code>@Bean
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
public TransactionAttributeSource transactionAttributeSource() {
// Set to false so protected and default‑visibility methods are also transactional
return new AnnotationTransactionAttributeSource(false);
}</code>Make sure bean overriding is allowed:
<code>GenericApplicationContext context = new GenericApplicationContext();
context.setAllowBeanDefinitionOverriding(true);
</code>2.2 Swallowed Exceptions
<code>@Transactional
protected void save() {
try {
int result = jdbcTemplate.update("insert into t_person (age, name) values (?, ?)", person.getAge(), person.getName());
System.out.println(1 / 0);
} catch (Exception e) {
e.printStackTrace();
}
}</code>Spring rolls back a transaction only when an exception propagates out of the method. Catching and not re‑throwing the exception prevents rollback.
2.3 Incorrect Rollback Exception Class
Spring rolls back for RuntimeException and Error by default. If a method throws a checked Exception without specifying rollbackFor , the transaction will not roll back:
<code>@Transactional
protected void save() throws Exception {
try {
// business logic
System.out.println(1 / 0);
} catch (Exception e) {
e.printStackTrace();
throw new Exception(e);
}
}</code>2.4 Self‑Invocation Within the Same Class
<code>protected void save() {
// ...
this.updatePerson();
}
@Transactional
public void updatePerson() {
// ...
}
</code>The call goes through the original object, not the Spring proxy, so the transaction is ignored. Solutions:
Use AopContext.currentProxy() to obtain the proxy and invoke the method.
Inject the bean into itself (e.g., via @Resource private PersonService personService; ) and call personService.updatePerson() .
2.5 Final Methods
<code>@Transactional
protected final void save() {
// ...
}
</code>cglib creates proxies by subclassing; a final method cannot be overridden, so the transaction advice is not applied, leading to a NullPointerException when dependencies are not injected.
2.6 Wrong Propagation Settings
<code>@Transactional(propagation = Propagation.NOT_SUPPORTED)
protected void save() {
// ...
}
</code>Using NOT_SUPPORTED or NEVER disables transaction creation, causing the method to run without a transaction.
2.7 Asynchronous Thread Execution
<code>@Transactional
protected void save() {
new Thread(() -> {
// database operations
System.out.println(1 / 0);
}).start();
try { TimeUnit.SECONDS.sleep(3); } catch (InterruptedException e) {}
}
</code>The new thread does not share the original Connection, so the transaction does not apply.
2.8 Unsupported Database Engine
MySQL tables created with engines such as MyISAM do not support transactions. Only InnoDB provides transactional capabilities. Ensure tables are created with ENGINE=InnoDB .
2.9 Using the Wrong @Transactional Annotation
Some developers mistakenly use @javax.transaction.Transactional . Spring’s AnnotationTransactionAttributeSource detects both Spring’s and JTA annotations if the corresponding classes are present on the classpath, so the transaction can still work.
Conclusion
This article explored numerous situations that cause Spring transactions to become ineffective. Understanding these scenarios helps you grasp the importance of proper transaction management in Spring and equips you with practical solutions to prevent and resolve transaction failures.
Spring Full-Stack Practical Cases
Full-stack Java development with Vue 2/3 front-end suite; hands-on examples and source code analysis for Spring, Spring Boot 2/3, and Spring Cloud.
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.