如何知道回滚已执行? [@Transactional]

时间:2018-03-23 08:53:19

标签: java spring transactions

我有以下情况:

我在我的Affiliate实体上进行迭代,对于每个实体,我需要在一个唯一的事务中保持并更新数据。所以我有一个带有Spring @Transactional注释注释方法的服务(其中数据被创建和更新)但我不知道如何才能看到该事务已为联盟会员回滚?

我想知道,对于特殊的Affiliate,事务已经回滚并从我的服务中检索自定义错误代码..

这是我使用Spring之前的服务:

public void savePostingPaymentDetails(List<Posting> postingsToUpdate, List<PaymentPostingDetail> detailsToInsert, Payment payment) {
    logger.info("DB ACCESS : INSERT PAYMENT DETAILS & UPDATE POSTINGS");
    long begin = System.nanoTime();

    this.em.getTransaction().begin();

    try {
        // TEST
        // 1 - Save Payments
        this.em.persist(payment);

        // 2 - Save Details
        for (PaymentPostingDetail ppd : detailsToInsert) {
            this.em.persist(ppd);
        }

        // 3 - Update Postings
        for (Posting p : postingsToUpdate) {
            if(p.getSignature() != null)
            {
                p.getSignature().setModification("withholding-tax.pay", new Date());
            }
            else
            {
                logger.error("The Posting with id = " + p.getIdentifier() + " has no PersistenceSignature ?!");
            }
            this.em.merge(p);
        }
    }
    catch (Exception e)
    {
        logger.error("Unexpected error on saving/updating the DB.", e);

        this.em.getTransaction().rollback();
        logger.info("RollBack done.");

        e.printStackTrace();
        System.exit(JobStatus.ABNORMAL_END_OF_EXECUTION_ERROR.getCode());
    }

    this.em.getTransaction().commit();
    logger.info("Details inserted & Postings updated.");

    long end = System.nanoTime();
    logger.info("Execution time = " + ((end-begin) / 1000000) + " milliseconds.");
    logger.info("----------------------------------------------------------");
}

现在我有了这个:

@Transactional
public void savePostingPaymentDetails(List<Posting> postings, List<PaymentPostingDetail> paymentDetails, Payment payment)
{
    logger.info("DB ACCESS : INSERT PAYMENT DETAILS & UPDATE POSTINGS");
    long begin = System.nanoTime();

    this.paymentRepository.save(payment);
    this.ppdRepository.save(paymentDetails);

    for(Posting p : postings){
        if(p.getSignature() != null)
        {
            p.getSignature().setModifiedAt(LocalDate.now());
            p.getSignature().setModifiedBy(PayCopyrightWithholdingTaxProcess.SIGNATURE);
        }
        else{
            p.setSignature(new PersistenceSignature(LocalDate.now(), PayCopyrightWithholdingTaxProcess.SIGNATURE));
        }
        this.postingRepository.save(p);
    }

    long end = System.nanoTime();
    logger.info("Execution time = " + ((end-begin) / 1000000) + " milliseconds.");
    logger.info("----------------------------------------------------------");
}

但是,如果事务已经回滚,我怎么能返回让我们说一个特殊的整数(而不是System.exit())?

2 个答案:

答案 0 :(得分:1)

您可以使用侦听器(@TransactionalEventListener)来通知回滚事务(侦听器可以绑定到事务的不同阶段)。有关详细信息,请参阅https://docs.spring.io/spring/docs/4.2.x/spring-framework-reference/html/transaction.html的第16.8节(要求Spring&gt; = 4.2)

答案 1 :(得分:1)

有一种叫做用户管理事务(UMT)和容器管理事务(CMT)的东西 当您使用@Transactional时,您实际上是将事务管理委派给Spring容器(CMT),后者负责为您打开和关闭事务。它 在unchecked ExceptionNullPointerException投掷RuntimeException时会自动回滚。用于检查 在回滚应该发生的时候必须指定的例外@Transactional(rollbackFor=myCheckedException.class)

您还可以监听,观察事务对TransactionalEventListener的影响,并对某些AOP监听代码做出反应,如here所示。但是你并没有最终管理交易,Spring正在为你做。当发生特殊情况时,客户端代码无法对某些自定义代码做出反应,因为事务的管理委托给Spring。

因此,您必须回到用户管理的事务,在该事务中打开您的事务,提交它并在回滚的情况下做出反应。这正是UMT的目的:完全控制您的交易

从你的旧代码

你可能得到类似的东西:

public int savePostingPaymentDetails(List<Posting> postingsToUpdate, List<PaymentPostingDetail> detailsToInsert, Payment payment) {

    int returnCode = 1 // 1 -> "success" , 0 -> "failure"

    logger.info("DB ACCESS : INSERT PAYMENT DETAILS & UPDATE POSTINGS");
    long begin = System.nanoTime();
    long end = 0;

    this.em.getTransaction().begin();

    try {
        // TEST
        // 1 - Save Payments
        this.em.persist(payment);

        // 2 - Save Details
        for (PaymentPostingDetail ppd : detailsToInsert) {
            this.em.persist(ppd);
        }

        // 3 - Update Postings
        for (Posting p : postingsToUpdate) {
            if(p.getSignature() != null)
            {
                p.getSignature().setModification("withholding-tax.pay", new Date());
            }
            else
            {
                logger.error("The Posting with id = " + p.getIdentifier() + " has no PersistenceSignature ?!");
            }
            this.em.merge(p);
        }
        this.em.getTransaction().commit();
        end = System.nanoTime();
    }
    catch (Exception e)
    {
        returnCode = 0;
        logger.error("Unexpected error on saving/updating the DB.", e);

        this.em.getTransaction().rollback();
        logger.info("RollBack done.");

        // e.printStackTrace();
         System.exit(JobStatus.ABNORMAL_END_OF_EXECUTION_ERROR.getCode());
        return returnCode;
    }

    //this.em.getTransaction().commit();
    logger.info("Details inserted & Postings updated.");

    //long end = System.nanoTime();
    logger.info("Execution time = " + ((end-begin) / 1000000) + " milliseconds.");
    logger.info("----------------------------------------------------------");
    return  returnCode = 1;
}

PS :在旁注中,最佳做法是让您在提交失败时抛出异常,而不是特殊代码。

您的新方法签名可能是:

public void savePostingPaymentDetails(List<Posting> postingsToUpdate, List<PaymentPostingDetail> detailsToInsert, Payment payment) 
throws MyFailedDbOperationException, OtherException {

}

并在catch块上抛出异常

catch (Exception e)
{
    logger.error("Unexpected error on saving/updating the DB.", e);

    this.em.getTransaction().rollback();

    logger.info("RollBack done.");
    throw MyFailedDbOperationException("my db operation failed");
}