异步中事务管理和线程隔离问题

错误代码

@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,不会重复创建
所有操作完成后统一关闭

posted @ 2026-02-09 11:54  4加1等于9  阅读(16)  评论(0)    收藏  举报