容器管理的事务 - 未标记为回滚

Container Managed Transaction - Not marked for roll back

Bank.java

@Stateless
@Local
public class Bank implements IBank {

    @EJB
    IConfigBean iConfigBean;

    @EJB
    IDbs iDBS;

    @EJB
    IPosb iPosb;

    @Override
    public void doTransaction() {
        System.out.println("--Bank Transaction Started--");
        try {
            Config config1 = getConfig(1);
            iConfigBean.create(config1);

            iDBS.doDBSTransaction();

            Config config3 = getConfig(3);
            iConfigBean.create(config3);

            iPosb.doPOSBTransaction();

            Config config5 = getConfig(5);
            iConfigBean.create(config5);
        } catch (Exception e) {
            e.printStackTrace();
            System.out.println("---Bank Exception--");
        }
        System.out.println("--Bank Transaction End--");
    }

    @Override
    public Config getConfig(int inserttionOrderNo) {
        Config config = new Config();
        config.setType("EJBTransactionTESTING - " + inserttionOrderNo);
        return config;
    }
}

DBS.java

@Stateless
@Local
public class DBS implements IDbs {

    @EJB
    IConfigBean iConfigBean;

    @Override
    public void doDBSTransaction() {
        System.out.println("--DBS Transaction Started--");
        try {
            Config config2 = getConfig(2);
            iConfigBean.create(config2);
        } catch (Exception e) {
            e.printStackTrace();
            System.out.println("--DBS Exception--");
        }
        System.out.println("--DBS Transaction End--");
    }

    @Override
    public Config getConfig(int inserttionOrderNo) {
        Config config = new Config();
        config.setType("EJBTransactionTESTING - " + inserttionOrderNo);
        return config;
    }

}

POSB.java

@Stateless
@Local
public class POSB implements IPosb {

    @EJB
    IConfigBean iConfigBean;

    @Override
    public void doPOSBTransaction() {
        System.out.println("--POSB Transaction Started--");
        try {
            Config config4 = getConfig(4);
            iConfigBean.create(config4);
            if (true) {
                //For Test 1 
                //throw new NullPointerException(); 
            }
        } catch (Exception e) {
            e.printStackTrace();
            System.out.println("--POSB Exception--");
        }
        if (true) {
            //For Test 2 
            // throw new NullPointerException();
        }
        System.out.println("--POSB Transaction End--");
    }

    @Override
    public Config getConfig(int inserttionOrderNo) {
        Config config = new Config();
        config.setType("EJBTransactionTESTING - " + inserttionOrderNo);
        return config;
    }
}

我是 Stack Overflow 的新手,这是我的新问题,如果我错了请纠正我。

环境是..

我有三个stateless bean,请查看Transaction flow的Sequence Diagram。

我在交易过程中特意在两个地方制造了NullPointer Exception来了解区别,我在时序图中用Lightening Bold符号标记了。

我没有对任何方法使用任何@TransactionAttribute。

测试 1 - try 块内部的空指针(用绿色加亮粗体符号) 当我开始测试时,出现空指针异常并且所有事务都没有标记为回滚并且数据也被插入到数据库中。

我只能在控制台日志中看到空指针异常。

测试 2 - try - catch 方法外部的空指针(用红色加亮粗体符号) 当我开始测试时,得到空指针异常加上 EJBTransactionRolledbackException 和所有标记为回滚的事务并且没有数据插入 db.

我可以在控制台日志中看到 NullPointer 和 EJBTransactionRolledback 异常。

这里的问题是,

  1. 为什么 EJB 事务没有标记为回滚如果我在 try 块中创建空指针
  2. 为什么 EJB 事务会回滚如果我在 try 块外创建了空指针

提前致谢。

请记住 EJB 调用,容器产生的所有“魔法”都在那里发生,包括事务标记。这是可能的,因为 EJB 调用不是直接的,而是总是通过代理。

您的代码中有这样的调用:

iPosb.doPOSBTransaction(); 

因此,如果未经检查的异常(例如 NPE)在此方法中被抛出并且未被捕获 - 由于 EJB 代理包装了上述调用,它最终被容器捕获。在这种情况下,事务只能回滚。

在您的方法中添加对同一 bean 的方法的调用(不使用 @EJB 引用),不会改变:

@Override
public void doPOSBTransaction() {
    try {
        Config config4 = getConfig(4);
        iConfigBean.create(config4);
        if (true) {
            newMethod(); 
        }
    } catch (Exception e) {
        e.printStackTrace();
        System.out.println("--POSB Exception--");
    }
    if (true) {
        newMethod(); 
    }        
}

private void newMethod(){
    throw new RuntimeException();
}

您可以很容易地检查 commit/rollback 在这种情况下行为是一样的,无论将方法添加到调用堆栈。

因此,您必须记住的重要一点是,所有容器技巧仅适用于@EJB 调用。因此,例如,将事务注释放在私有方法上是没有意义的——它永远不会被使用。

另一个重点是关于检查异常。默认情况下,这些确实不会导致事务回滚。但是仍然可以像下面这样注释您的已检查异常以使其回滚正在进行的事务:

@ApplicationException(rollback = true)