问题本质:
Spring通过动态代理实现事务管理,自调用会绕过代理直接调用目标方法。
@Service
public class PaymentService {
// 事务失效的自调用案例
public void processPayment(PaymentRequest request) {
validateFunds(request); // 直接调用导致事务未生效
executePayment(request);
}
@Transactional
private void validateFunds(PaymentRequest request) {
// 资金校验逻辑(抛出InsufficientFundsException)
}
}
解决方案:
((PaymentService)AopContext.currentProxy()).validateFunds(request);
默认行为:
配置陷阱:
@Transactional(rollbackFor = BusinessException.class) // 仅指定特定异常
public void updateInventory() throws InventoryException {
// 抛出InventoryException(父类)不会触发回滚
}
最佳实践:
@Transactional(rollbackFor = Exception.class, noRollbackFor = SystemException.class)
原理:
CGLIB通过继承生成代理类,无法代理private/final方法
检测工具:
// 在启动类添加校验
@Bean
public CommandLineRunner checkProxy() {
return args -> {
if (!Modifier.isPublic(MyService.class.getMethod("txMethod").getModifiers())) {
throw new IllegalStateException("事务方法必须为public");
}
};
}
典型错误:
@Transactional(propagation = Propagation.REQUIRED)
public void outerMethod() {
innerService.innerMethod(); // 使用REQUIRES_NEW
// 外层异常不会影响内层事务
}
@Service
class InnerService {
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void innerMethod() { ... }
}
传播类型对照表:
传播行为 | 外部存在事务 | 外部无事务 |
---|---|---|
REQUIRED(默认) | 加入 | 新建 |
REQUIRES_NEW | 挂起并新建 | 新建 |
NESTED | 嵌套事务 | 新建 |
MANDATORY | 加入 | 抛出异常 |
多数据源配置示例:
# application.yml
spring:
datasource:
order:
url: jdbc:mysql://localhost:3306/order
username: root
user:
url: jdbc:mysql://localhost:3306/user
username: root
事务管理器指定:
@Transactional("orderTransactionManager")
public void createOrder() { ... }
错误示例:
@Async
public void asyncTask() {
updateData(); // 异步线程中调用事务方法
}
@Transactional
public void updateData() { ... } // 事务失效
解决方案:
将事务方法移动到其他Service并通过代理调用
危险代码:
try {
userDao.save(user);
} catch (DataIntegrityViolationException e) {
// 异常被吞没,事务继续提交
log.error("保存失败", e);
}
修复方案:
try {
userDao.save(user);
} catch (DataIntegrityViolationException e) {
TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
throw new BusinessException("保存失败", e);
}
检测脚本:
SHOW TABLE STATUS LIKE 'order_table';
-- 确保Engine=InnoDB
迁移方案:
ALTER TABLE my_table ENGINE=InnoDB;
注意 : 避免在事务的方法里调用第三方接口,如果调用第三方接口,需要设置过期时间,如果过期则直接抛异常回滚。
@Transactional(timeout = 30)
public void batchProcess() {
int pageSize = 500;
for (int page = 0; ; page++) {
List<Data> batch = dataRepository.findBatch(page, pageSize);
if (batch.isEmpty()) break;
processBatch(batch);
// 手动刷新会话
entityManager.flush();
entityManager.clear();
}
}
private void processBatch(List<Data> batch) {
batch.forEach(data -> {
data.setStatus(PROCESSED);
dataRepository.save(data);
});
}
@Configuration
public class HikariConfig {
@Bean
public HikariDataSource dataSource() {
HikariConfig config = new HikariConfig();
config.setMaximumPoolSize(20);
config.setConnectionTimeout(30000);
config.setIdleTimeout(600000);
config.setMaxLifetime(1800000);
config.setLeakDetectionThreshold(5000); // 泄漏检测
return new HikariDataSource(config);
}
}
// 使用Micrometer监控事务
@Bean
public MeterRegistryCustomizer<MeterRegistry> metrics() {
return registry -> {
registry.gauge("transaction.active",
TransactionSynchronizationManager.getCurrentTransactionName());
};
}
Proxy.newProxyInstance(
targetClass.getClassLoader(),
targetClass.getInterfaces(),
new TransactionInterceptor(txManager)
);
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(targetClass);
enhancer.setCallback(new TransactionInterceptor());
return enhancer.create();
public abstract class TransactionSynchronizationManager {
// 线程绑定资源存储
private static final ThreadLocal<Map<Object, Object>> resources =
new NamedThreadLocal<>("Transactional resources");
// 事务同步回调
private static final ThreadLocal<Set<TransactionSynchronization>> synchronizations =
new NamedThreadLocal<>("Transaction synchronizations");
}
PROPAGATION_REQUIRES_NEW实现:
public class TransactionAspectSupport {
protected Object invokeWithinTransaction(Method method, Class<?> targetClass,
final InvocationCallback invocation) throws Throwable {
TransactionInfo txInfo = createTransactionIfNecessary(tm, txAttr, joinpointIdentification);
try {
Object retVal = invocation.proceedWithInvocation();
commitTransactionAfterReturning(txInfo);
return retVal;
} catch (Throwable ex) {
completeTransactionAfterThrowing(txInfo, ex);
throw ex;
} finally {
cleanupTransactionInfo(txInfo);
}
}
}
@Transactional(readOnly=true)
@Around("@annotation(transactional)")
public Object logTransaction(ProceedingJoinPoint joinPoint) throws Throwable {
long start = System.currentTimeMillis();
try {
return joinPoint.proceed();
} finally {
log.info("事务执行时间: {}ms", System.currentTimeMillis() - start);
}
}
事务回滚测试:
@SpringBootTest
@Transactional // 测试后自动回滚
public class TransactionTest {
@Test
@Rollback(false) // 强制提交
public void testCommitBehavior() { ... }
}
Arthas命令:
watch org.springframework.transaction.support.TransactionTemplate execute \
'{params, returnObj, throwExp}' -x 3
Spring Actuator端点:
GET /actuator/transactions
logging.level.org.hibernate.SQL=DEBUG
logging.level.org.hibernate.type.descriptor.sql.BasicBinder=TRACE