异步中事务管理和线程隔离问题
错误代码
@Override
public MyRecord createOrder( CreateOrderRequest request) {
Integer uid = request.getUserId();
PaymentProductEntity product = paymentProductDao.getProductInfoByProductId(2L);
// 生成订单号
String orderNo = WxPayUtil.getOrderNo("order");
// 保存用户兑换码信息 user_redemption_info(设置为is_used = 0 当 支付成功的时候修改为1 )
UserRedemptionInfoEntity userRedemptionInfo = buildUserRedemptionInfo(uid, orderNo);
userRedemptionInfoService.save(userRedemptionInfo);
Integer redemptionId = userRedemptionInfo.getId();
// 并行构建其他实体(减少串行时间)
CompletableFuture<PaymentOrderEntity> paymentOrderFuture = CompletableFuture.supplyAsync(() ->
buildPaymentOrder(orderNo, product, uid)
);
CompletableFuture<UserInfoDto> userInfoFuture = CompletableFuture.supplyAsync(() ->
buildUserInfoDto(request, redemptionId)
);
// 等待构建完成并保存
PaymentOrderEntity paymentOrder = paymentOrderFuture.join();
UserInfoDto userInfoDto = userInfoFuture.join();
paymentOrderService.save(paymentOrder);
// 异步保存用户信息(不阻塞主流程)
CompletableFuture.runAsync(() -> {
try {
System.out.println("执行1");
userInfoService.addUserInfo(userInfoDto);
System.out.println("执行2");
} catch (Exception e) {
throw new RuntimeException("异步保存用户信息失败" + e.getMessage());
}
});
MyRecord record = new MyRecord();
record.set("orderNo", paymentOrder.getOrderNo());
System.out.println("执行3");
return record;
}
报错信息
Creating a new SqlSession
SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@58720eb9] was not registered for synchronization because synchronization is not active
2026-02-09 10:55:20.622 [main] INFO com.zaxxer.hikari.HikariDataSource - HikariPool-1 - Starting...
2026-02-09 10:55:20.949 [main] INFO com.zaxxer.hikari.pool.HikariPool - HikariPool-1 - Added connection com.mysql.cj.jdbc.ConnectionImpl@7097ead6
2026-02-09 10:55:20.951 [main] INFO com.zaxxer.hikari.HikariDataSource - HikariPool-1 - Start completed.
JDBC Connection [HikariProxyConnection@1096030628 wrapping com.mysql.cj.jdbc.ConnectionImpl@7097ead6] will not be managed by Spring
==> Preparing: SELECT product_id, product_name, product_type, price, status, description, image_url, category_id, create_time, update_time FROM payment_product WHERE product_id = ? LIMIT 1
==> Parameters: 2(Long)
<== Columns: product_id, product_name, product_type, price, status, description, image_url, category_id, create_time, update_time
<== Row: 2, 测试商品, VIRTUAL, 0.01, ON_SALE, <<BLOB>>, null, 1, 2026-02-03 16:00:56, 2026-02-03 17:21:14
<== Total: 1
Closing non transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@58720eb9]
Creating a new SqlSession
SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@17554316] was not registered for synchronization because synchronization is not active
JDBC Connection [HikariProxyConnection@105784446 wrapping com.mysql.cj.jdbc.ConnectionImpl@7097ead6] will not be managed by Spring
==> Preparing: INSERT INTO user_redemption_info ( uid, redemption_code, is_used, expire_time, create_time, remark ) VALUES ( ?, ?, ?, ?, ?, ? )
==> Parameters: 55(Integer), order82497177060572105834790(String), 0(Integer), 9999-12-31 23:59:59.0(Timestamp), 2026-02-09 10:55:21.067(Timestamp), 微信支付(String)
<== Updates: 1
Closing non transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@17554316]
Creating a new SqlSession
SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@1d0fc0bc] was not registered for synchronization because synchronization is not active
JDBC Connection [HikariProxyConnection@33570527 wrapping com.mysql.cj.jdbc.ConnectionImpl@7097ead6] will not be managed by Spring
==> Preparing: INSERT INTO payment_order ( order_no, appid, mchid, total_amount, pay_price, product_id, product_info, user_id, create_time ) VALUES ( ?, ?, ?, ?, ?, ?, ?, ?, ? )
==> Parameters: order82497177060572105834790(String), wx0e6d62f5e3a5feb7(String), 1622829999(String), 0(Integer), 0.01(BigDecimal), 2(Long), 测试商品(String), 55(Long), 2026-02-09 10:55:21.165(Timestamp)
<== Updates: 1
Closing non transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@1d0fc0bc]
执行1
执行3
问题分析:
1、事务问题
SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@58720eb9] was not registered for synchronization because synchronization is not active
第一次创建 SqlSession 提示"未注册同步",说明该连接没有被Spring事务管理
同样,插入兑换码信息
Creating a new SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@17554316]
was not registered for synchronization because synchronization is not active
新的SqlSession,且非事务,说明连接没有被复用,每个DAO操作都独立获取连接
同样,插入支付订单
Creating a new SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@1d0fc0bc]
又一个新的SqlSession
连接不同步:日志显示 SqlSession was not registered for synchronization because synchronization is not active
2、 异步线程问题
执行1 # 异步线程开始
执行3 # 主线程在异步操作完成前就返回响应
主线程提前关闭导致异步任务被中断
解决方案:
@Service
@Slf4j
public class AsyncService {
@Autowired
private UserInfoService userInfoService;
@Async("taskExecutor") // 使用配置的线程池
@Transactional(propagation = Propagation.REQUIRES_NEW) // 新开事务
public void asyncAddUserInfo(UserInfoDto userInfoDto) {
try {
log.info("执行1 - 开始异步保存用户信息");
userInfoService.addUserInfo(userInfoDto);
log.info("执行2 - 异步保存用户信息完成");
} catch (Exception e) {
log.error("异步保存用户信息失败", e);
}
}
}
createOrder 使用 asyncService.asyncAddUserInfo(userInfoDto);
@Async("taskExecutor")
启用异步执行:方法会在单独的线程中执行,不阻塞调用方
异步线程与请求线程解耦:@Async创建的线程与HTTP请求线程无关,不受请求生命周期约束
@Transactional(propagation = Propagation.REQUIRES_NEW)
创建新事务:在异步线程中开启一个全新的、独立的事务
与主事务隔离:不会受主事务回滚的影响
独立提交:异步操作成功就提交,失败就回滚,不影响主流程
正确日志
2026-02-09 11:22:44.734 [main] INFO com.zaxxer.hikari.HikariDataSource - HikariPool-1 - Starting...
2026-02-09 11:22:45.121 [main] INFO com.zaxxer.hikari.pool.HikariPool - HikariPool-1 - Added connection com.mysql.cj.jdbc.ConnectionImpl@8f8717b
2026-02-09 11:22:45.123 [main] INFO com.zaxxer.hikari.HikariDataSource - HikariPool-1 - Start completed.
Creating a new SqlSession
Registering transaction synchronization for SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@240f712e]
JDBC Connection [HikariProxyConnection@47843184 wrapping com.mysql.cj.jdbc.ConnectionImpl@8f8717b] will be managed by Spring
==> Preparing: SELECT product_id, product_name, product_type, price, status, description, image_url, category_id, create_time, update_time FROM payment_product WHERE product_id = ? LIMIT 1
==> Parameters: 2(Long)
<== Columns: product_id, product_name, product_type, price, status, description, image_url, category_id, create_time, update_time
<== Row: 2, 测试商品, VIRTUAL, 0.01, ON_SALE, <<BLOB>>, null, 1, 2026-02-03 16:00:56, 2026-02-03 17:21:14
<== Total: 1
Releasing transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@240f712e]
Fetched SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@240f712e] from current transaction
==> Preparing: INSERT INTO user_redemption_info ( uid, redemption_code, is_used, expire_time, create_time, remark ) VALUES ( ?, ?, ?, ?, ?, ? )
==> Parameters: 55(Integer), order44966177060736535157254(String), 0(Integer), 9999-12-31 23:59:59.0(Timestamp), 2026-02-09 11:22:45.363(Timestamp), 微信支付(String)
<== Updates: 1
Releasing transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@240f712e]
Fetched SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@240f712e] from current transaction
==> Preparing: INSERT INTO payment_order ( order_no, appid, mchid, total_amount, pay_price, product_id, product_info, user_id, create_time ) VALUES ( ?, ?, ?, ?, ?, ?, ?, ?, ? )
==> Parameters: order44966177060736535157254(String), wx0e6d62f5e3a5feb7(String), 1622829999(String), 0(Integer), 0.01(BigDecimal), 2(Long), 测试商品(String), 55(Long), 2026-02-09 11:22:45.44(Timestamp)
<== Updates: 1
Releasing transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@240f712e]
Transaction synchronization suspending SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@240f712e]
Creating a new SqlSession
Registering transaction synchronization for SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@7528089c]
JDBC Connection [HikariProxyConnection@1969754956 wrapping com.mysql.cj.jdbc.ConnectionImpl@28c75c93] will be managed by Spring
==> Preparing: SELECT id,redemption_id,uid,xing,ming,lunar_birth,solar_birth,gender,phone,benming_gua,neihan_gua,zhousui_gua,huangdao_gua,muxing_gua,benming_yao,huangdao_yao,muxing_yao,benming_dizhi,huangdao_dizhi,muxing_dizhi,created_at,updated_at,calendar,birth_date FROM user_info WHERE (uid = ? AND xing = ? AND ming = ? AND gender = ? AND birth_date = ? AND calendar = ?)
==> Parameters: 55(Integer), 赵(String), 测试(String), 1(Integer), 20251011(Long), 1(Integer)
<== Total: 0
Releasing transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@7528089c]
Fetched SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@7528089c] from current transaction
==> Preparing: INSERT INTO user_info ( redemption_id, uid, xing, ming, lunar_birth, solar_birth, gender, benming_gua, zhousui_gua, huangdao_gua, muxing_gua, benming_yao, huangdao_yao, muxing_yao, benming_dizhi, huangdao_dizhi, muxing_dizhi, created_at, updated_at, calendar, birth_date ) VALUES ( ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ? )
==> Parameters: 678(Integer), 55(Integer), 赵(String), 测试(String), 1970-07-22T17:16:51(LocalDateTime), 1970-08-23T17:16:51(LocalDateTime), 1(Integer), 11(Integer), 55(Integer), 46(Integer), 81(Integer), 4(Integer), 4(Integer), 2(Integer), 子(String), 巳(String), 戌(String), 2026-02-09T11:22:47.458555700(LocalDateTime), 2026-02-09T11:22:47.458555700(LocalDateTime), 1(Integer), 20251011(Long)
<== Updates: 1
Releasing transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@7528089c]
Transaction synchronization committing SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@7528089c]
Transaction synchronization deregistering SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@7528089c]
Transaction synchronization closing SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@7528089c]
Transaction synchronization resuming SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@240f712e]
执行3
Transaction synchronization committing SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@240f712e]
Transaction synchronization deregistering SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@240f712e]
Transaction synchronization closing SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@240f712e]
will be managed by Spring 事务管理
多个操作使用同一个SqlSession,不会重复创建
所有操作完成后统一关闭

浙公网安备 33010602011771号