ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

NCC 事务

2020-11-03 21:34:28  阅读:204  来源: 互联网

标签:case 事务 RequiresNew TransactionContextType Object new NCC


NC事务
1.NC中新建独立事务
NC中接口方法命名为method__RequiresNew(Object param) throws Exception ,后续步骤同新建NC组件一样——实现接口,在upm中注册接口。

public interface IGuanyiBillFilterService {

/**
* 更新已处理的单据记录
* @param records
*/
void updateBillRecord__RequiresNew(List<GuanYiErpBillRecord> records) throws DAOException;

  

代码中调用事务,需通过NCLocator进行远程组件调用事务才生效,直接调用不会新建事务。

 

2.NC事务原理
当进行远程组件调用时会使用动态代理执行目标方法,其中会判断方法名是否具有_RequiresNew后缀来决定是否新建事务

public class CMTEJBServiceHandler implements InvocationHandler {
      ... public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { try { if (method.getName().endsWith("_RequiresNew")) { return this.cmtProxy.delegate_RequiresNew(this.wrapped, method, args); } return this.cmtProxy.delegate(this.wrapped, method, args); } catch (Throwable e) { Throwable lastEJBException = getLastEJBException(e); if (lastEJBException == null) { throw e; } if (lastEJBException.getCause() != null) { throw lastEJBException.getCause(); } throw e; } }
        ... }

  

下一步会进入到如下方法,其中需要关注的是beforeCallMethod和afterCallMethod,其中分别新建和结束事务

 public class CMTProxy_Local
   extends BeanBase
   implements CMTProxyEjbObject
 {
  .....
   
   public Object delegate_RequiresNew(Object arg0, Method arg1, Object[] arg2)
     throws Exception
   {
     beforeCallMethod(200);
     try {
       o = _getBeanObject().delegate_RequiresNew(arg0, arg1, arg2);
     }
     catch (Exception e) {
       er = e;
     } catch (Throwable thr) {
       er = new FrameworkEJBException("Fatal unknown error", thr);
     }
     try {
       afterCallMethod(200, er);
     }
   .....
   }
   
 
   public Object delegate(Object arg0, Method arg1, Object[] arg2)
     throws Exception
   {
   ....
     beforeCallMethod(201);
     try {
       o = _getBeanObject().delegate_RequiresNew(arg0, arg1, arg2);
     }
     catch (Exception e) {
       er = e;
     } catch (Throwable thr) {
       er = new FrameworkEJBException("Fatal unknown error", thr);
     }
     try {
       afterCallMethod(201, er);
     }
    ......
   }
 }

  

让我们看看里面干了啥…

beforeCallMethod中首先判断组件是否是容器控制事务,获取事务类型和事务隔离级别后调用TransactionManager的begin方法

   protected void beforeCallMethod(int methodId)
   {
    ......
     boolean isCmt = ((HomeBase)getEJBLocalHome()).getEJBBeanDescriptor().isCmt();
     
     if (isCmt)
     {
       try
       {
         this.currentMethodTransectionType = getMethodTransectionType(methodId);
         int isolateLevel = getMethodIsolateLevelType(methodId);
         setIerpTransactionManagerProxy(TransactionFactory.getTMProxy());
         getIerpTransactionManagerProxy().begin(this.currentMethodTransectionType, isolateLevel);
       }
       catch (Exception e) {
         Logger.error("BeforeCallMethod", e);
       }
     }
     else {
       if (getIerpUserTransaction() == null) {
         setIerpTransactionManagerProxy(null);
         
         setIerpUserTransaction(TransactionFactory.getUTransaction());
       }
       
       getIerpUserTransaction().bindToCurrentThread();
     }
     
    ......
   }

  

UAPTransactionManager中的begin方法,其中3是对应声明了_RequiresNew的接口方法,1是对应普通接口方法,自己debug可以跟踪到。

  1. 使用_RequiresNew方法会直接新建一个事务
  2. 不使用_RequiresNew会判断堆栈中是否已有事务,有就直接使用栈顶事务,没有就新建事务
  3. 之后封装成context,压栈
   public void begin(int transType) throws NotSupportedException, SystemException {
     switch (transType) {
     case 1: 
       if (this.tranStack.isEmpty()) {
         createTransaction(TransactionContextType.SOURCE);
       } else {
         createTransaction(TransactionContextType.JOINED);
       }
       break;
     case 3: 
       createTransaction(TransactionContextType.SOURCE);
       break;
     case 4: 
       if (this.tranStack.isEmpty()) {
         throw new SystemException();
       }
       createTransaction(TransactionContextType.JOINED);
       
       break;
     case 5: 
       if (!this.tranStack.isEmpty()) {
         throw new SystemException();
       }
       createTransaction(TransactionContextType.NULL);
       break;
     case 2: 
       if (!this.tranStack.isEmpty()) {
         createTransaction(TransactionContextType.NULL);
       } else {
         createTransaction(TransactionContextType.JOINED);
       }
       break;
     case 0: 
       createTransaction(TransactionContextType.NULL);
       break;
     case 11: 
       createTransaction(TransactionContextType.JOINED);
       try {
         setCurInvokeSavePoint();
       } catch (SQLException e) {
         throw new NotSupportedException("savePoint error!");
       }
     case 6: case 7: case 8: 
     case 9: case 10: default: 
       throw new NotSupportedException("trans type error!");
     }
     
   }

 private UAPTransactionContext createTransaction(TransactionContextType transType) throws 	SystemException
   {
     UAPTransaction uapTran = null;
     if (transType == TransactionContextType.SOURCE) {
       uapTran = new UAPTransaction();
     }
     if (transType == TransactionContextType.JOINED) {
       if (this.tranStack.isEmpty()) {
         throw new SystemException("no source Transaction,can not join ");
       }
       uapTran = (UAPTransaction)getTranContext().getTransaction();
     } else {
       uapTran = new UAPTransaction();
     }
     
     UAPTransactionContext tranText = new UAPTransactionContext(uapTran);
     tranText.setTransType(transType);
     this.tranStack.push(tranText);
     return tranText;
   }

  

  afterCallMethod同理

   protected void afterCallMethod(int methodId, Exception exception)
     throws java.rmi.RemoteException
   {
......
     boolean isCmt = ((HomeBase)getEJBLocalHome()).getEJBBeanDescriptor().isCmt();
     
     if (isCmt) {
       getIerpTransactionManagerProxy().end(exception);
       setIerpTransactionManagerProxy(null);
     }
     else
     {
       getIerpUserTransaction().unbindCurrentThread();
     }
......
   }

  

UAPTransactionManager中的end方法,其中会判断目标方法是否抛出异常

   public void end(Exception ex)
   {
     IUAPTransactionManager m_tranManager = (IUAPTransactionManager)tm_local.get();
     try {
       if (ex != null) {
         if (m_tranManager.getTranContext().needRBPoint()) {
           if (!((UAPTransaction)m_tranManager.getTranContext().getTransaction()).getRollbackOnly())
           {
             m_tranManager.rollBackToCurInvokePoint();
           }
         }
         else {
           m_tranManager.setCurTransRollBack();
         }
       }
       m_tranManager.commit();
     } catch (Exception e) {
       log.error("", e);
     }
   }

  

假如抛出异常,最终会调用UAPTransaction的setRollbackOnly方法,值得注意的是这里的rollback仅仅是设置了一个回滚标志,没有真正回滚,之后会说这样会导致什么问题。

 

 

 

最终会调用commit方法,这里的commit也不是单纯的提交,他会判断之前是否有设置回滚标志来进行统一回滚,整个事务调用到此完成。

 

 

知道了以上事务的原理后,假如我们代码里面有这样一种场景,即便调用方法B的时候捕捉异常且不向外抛出,之后的数据库操作也会回滚。其中是因为调用方法B之后代理会将当前的事务设置一个回滚标志,当完成方法A之后,整个事务会根据回滚标志统一回滚。

methodA__RequiresNew{
ClassB cb = NCLocate.lookup(ClassB.class);
try{
cb.methodB();
}catch(e){

}
//insert to db
insertsomething();
}


同大多数框架一样,NC实现了JTA规范,拓展:

https://www.jianshu.com/p/3938e7172443

 

https://blog.csdn.net/u014235678/article/details/103898538

 

3.checkpoint
4.Synchronization

标签:case,事务,RequiresNew,TransactionContextType,Object,new,NCC
来源: https://www.cnblogs.com/zhongxiaoze/p/13922676.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有