Developers integrating Spring, SpringMVC, and MyBatis often encounter scenarios where @Transactional annotations seemingly fail, resulting in persistent database changes despite runtime exceptions. Log files typically contain the warning: JDBC Connection will not be managed by Spring, indicating the transaction advisor never intercepted the method execution.
Typical Misconfiguration Pattern
The issue commonly arises from overlapping component-scan directives across the root and web application contexts. Consider this standard setup:
Root Context Configuration (applicationContext.xml):
<!-- Enabling annotation-driven transactions -->
<tx:annotation-driven />
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="primaryDataSource" />
</bean>
Service Layer Implementation:
@Service
public class FundTransferService {
@Autowired
private AccountDao accountDao;
@Transactional(rollbackFor = Exception.class)
public void processTransfer(Integer debitAccount, Integer creditAccount, Double transferAmount) {
// Deduct from source account
accountDao.adjustBalance(debitAccount, -transferAmount);
System.out.println("Debited: " + transferAmount);
// Simulate business logic exception
if (transferAmount > 5000.0) {
throw new IllegalStateException("Amount exceeds single transaction limit");
}
// Add to destination account
accountDao.adjustBalance(creditAccount, transferAmount);
System.out.println("Credited: " + transferAmount);
}
}
Web Context Componnet Scanning (springmvc.xml):
<context:component-scan base-package="com.example.controllers,com.example.services" />
Underlying Container Hierarchy Issue
Spring's context architecture creates a parent-child relationship:
- The
ContextLoaderListenerbootstraps the parent conatiner fromapplicationContext.xml - The
DispatcherServletinitializes a child container fromspringmvc.xml - When the child container scans
@Servicecomponents, it instantiates them before the parent container's transaction post-processor can apply AOP proxies - These unproxied service beans lack transaction interception and override the properly-configured parent beans
Resolution Strategy
Segregate component scanning responsibilities between contexts:
In the root application context, scan all service and repository components:
<context:component-scan base-package="com.example.services,com.example.repositories" />
In the web context, restrict scanning to controller components exclusively:
<context:component-scan base-package="com.example.controllers">
<context:exclude-filter type="annotation" expression="org.springframework.stereotype.Service"/>
<context:exclude-filter type="annotation" expression="org.springframework.stereotype.Repository"/>
</context:component-scan>
This architectural separation ensures:
- Service beans initialize within the parent container where the transaction manager applies AOP advice
- Controller beans remain in the child container with access to web-specific features
- No bean definition conflicts occur between container hierarchies