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可以跟踪到。
- 使用_RequiresNew方法会直接新建一个事务
- 不使用_RequiresNew会判断堆栈中是否已有事务,有就直接使用栈顶事务,没有就新建事务
- 之后封装成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