You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Spring @Transactional场景下,如何判断事务已回滚并获取自定义错误码?

Hey there! Let's work through how to handle your Spring transaction scenario for each Affiliate—including tracking rollbacks and retrieving custom error codes. Here's a step-by-step solution tailored to your needs:

Solution Breakdown

1. Isolate Single-Affiliate Logic with a New Transaction per Entity

To ensure each Affiliate gets its own independent transaction, we'll extract the persistence/update logic into a separate method with the REQUIRES_NEW transaction propagation. This tells Spring to start a fresh transaction for every call to this method, so failures in one won't affect others.

2. Create a Custom Business Exception for Error Codes

We'll define a runtime exception that carries your custom error code. Spring automatically rolls back transactions when runtime exceptions are thrown, so this lets us signal failure and pass along your specific error code at the same time.

3. Iterate Through Affiliates and Handle Rollbacks

In your main traversal method, call the single-Affiliate processing method in a try/catch block. Catching the custom exception will tell you that the transaction for that Affiliate rolled back, and you can extract the error code directly from the exception.


Code Implementation

Step 1: Custom Exception with Error Code

public class AffiliateProcessingException extends RuntimeException {
    private final int errorCode;

    public AffiliateProcessingException(int errorCode, String message) {
        super(message);
        this.errorCode = errorCode;
    }

    public AffiliateProcessingException(int errorCode, String message, Throwable cause) {
        super(message, cause);
        this.errorCode = errorCode;
    }

    public int getErrorCode() {
        return errorCode;
    }
}

Step 2: Service Layer with Transactional Methods

@Service
public class AffiliateService {

    private final EntityManager em;
    private final Logger logger = LoggerFactory.getLogger(AffiliateService.class);

    // Constructor injection for EntityManager
    public AffiliateService(EntityManager em) {
        this.em = em;
    }

    // Main traversal method: no transaction needed here
    public void processAllAffiliates(List<Affiliate> affiliates) {
        for (Affiliate affiliate : affiliates) {
            try {
                processSingleAffiliate(affiliate);
                logger.info("Successfully processed Affiliate ID: {}", affiliate.getId());
            } catch (AffiliateProcessingException e) {
                // This catch block means the transaction for this Affiliate rolled back
                logger.error("Failed to process Affiliate ID: {} | Error Code: {}", 
                    affiliate.getId(), e.getErrorCode(), e);
                // Add logic here to handle failed affiliates (e.g., log to a failure table)
            }
        }
    }

    // Single-Affiliate processing: independent transaction per call
    @Transactional(propagation = Propagation.REQUIRES_NEW, rollbackFor = Exception.class)
    public void processSingleAffiliate(Affiliate affiliate) {
        logger.info("Starting DB operations for Affiliate ID: {}", affiliate.getId());
        long startTime = System.nanoTime();

        try {
            // 1. Persist Payment
            Payment payment = affiliate.getPayment();
            em.persist(payment);

            // 2. Persist Payment Details
            List<PaymentPostingDetail> detailsToInsert = affiliate.getPaymentPostingDetails();
            for (PaymentPostingDetail ppd : detailsToInsert) {
                em.persist(ppd);
            }

            // 3. Update Postings
            List<Posting> postingsToUpdate = affiliate.getPostings();
            for (Posting p : postingsToUpdate) {
                if (p.getSignature() != null) {
                    p.getSignature().setModification("withholding-tax.pay", new Date());
                } else {
                    String errorMsg = String.format("Posting ID %s has no PersistenceSignature!", p.getIdentifier());
                    logger.error(errorMsg);
                    // Throw custom exception to trigger rollback and pass error code
                    throw new AffiliateProcessingException(
                        JobStatus.ABNORMAL_END_OF_EXECUTION_ERROR.getCode(), 
                        errorMsg
                    );
                }
                em.merge(p);
            }

            logger.info("Completed processing Affiliate ID {} in {} ns", 
                affiliate.getId(), System.nanoTime() - startTime);
        } catch (Exception e) {
            logger.error("Unexpected error processing Affiliate ID: {}", affiliate.getId(), e);
            // Wrap any unexpected exceptions in our custom error code exception
            throw new AffiliateProcessingException(
                JobStatus.ABNORMAL_END_OF_EXECUTION_ERROR.getCode(), 
                "Failed to process affiliate data", 
                e
            );
        }
    }
}

Key Notes
  • REQUIRES_NEW Propagation: This ensures every call to processSingleAffiliate starts a brand-new transaction. If one fails, only that transaction rolls back—others continue unaffected.
  • Automatic Transaction Management: Spring handles begin() and rollback() for you. If the method completes without exceptions, Spring commits the transaction; if our custom runtime exception is thrown, it rolls back automatically.
  • Error Code Handling: By catching AffiliateProcessingException in the traversal loop, you know exactly which Affiliate's transaction failed, and you can access the custom error code directly from the exception object.

内容的提问来源于stack exchange,提问作者Grechka Vassili

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.21 03:47:56