有关ibatis-dao组件部分的框架剖析和业务流程在《iBATIS 框架源码剖析》一书中写的已经比较详细。具体内容就不在这里写出来了,今天就把自己看过之后的几条心得和分析跟大家分享一下。写的不是很全,但都是个人觉得对于整体理解有很大帮助的内容。
1. daoManager提供的需求:·根据接口来获得实例化对象的代理对象——getDao方法实现。理由显而易见,必须获得代理对象来调用相关方法。
·根据实例化对象或代理对象获得事务——getTransaction方法实现impl.getDaoContext().getTransaction()。理由见第四点
2. daoManager提供事务方法的原因(start,commit,end)
可以实现多个context同时提交事务。
为此:有了连个ThreadLocal变量。transactionMode和contextInTransactionList
transactionMode:用于记录否开启事务
contextInTransactionList:存多个context到Arraylist中,不存入相同的context
实例:
情况一:
前提为同一线程下
一个context包含两个dao:daoProxy1,daoProxy2
那么,
//daoManager.startTransaction()开启事务
//daoProxy1.insert();会触发invoke,然后context.start
//daoProxy2.insert();会触发invoke,然后context.start。本来两个start与与下面的一次commit不对应会出现异常,但Context采用单例事务启动,如下
//daoManager.commitTransaction()只提交一个context即context.commitTransaction
//daoManager.endTransaction()只提交一个context即context.endTransaction
context中单例事务提交
//采用单例模式实现事务
public void startTransaction() {
if (state.get() != DaoTransactionState.ACTIVE) {//因此,相同的context只会start一次,因而与commit一次同步
DaoTransaction trans = transactionManager.startTransaction();
transaction.set(trans);
state.set(DaoTransactionState.ACTIVE);
daoManager.addContextInTransaction(this);
}
}
=> state参数的必要性
private ThreadLocal state = new ThreadLocal();// 处理事务状态信息的本地线程
情况二:
一个context1包含1个dao:daoProxy1。一个context2包含1个dao:daoProxy2
那么:
//daoManager.startTransaction()开启事务
//daoProxy1.insert();会触发invoke,然后context1.start
//daoProxy2.insert();会触发invoke,然后context2.start
//daoManager.commitTransaction()遍历所有context,全部提交context1.commitTransaction,context2.commitTransaction
//daoManager.endTransaction()同样end两次
=>保证start,commit,end一一对应
3. 解析dao.xml时就已经确定了DaoTransactionManager对象。所以,context.startTransaction()中
DaoTransaction trans = transactionManager.startTransaction();将会new出一个具体的事务对象如SQLMap
public DaoTransaction startTransaction() {
return new SqlMapDaoTransaction(client);继续调用client.startTransaction()这属于底层启动事务
}
其中 client对象是解析sqlmap-config.xml得到的对象。
client = SqlMapClientBuilder.buildSqlMapClient(reader, properties);属于SQLMap组件部分
4. 就SQLMap实现而言,由于所有基本的数据库操作都基于client对象,而client对象在SqlMapDaoTransaction中有,故提供一个方法getSqlMap()来获得
client:
SqlMapDaoTemplate:为对外、面向用户的模板类,用户继承它后,需要使用client来实现相关CRUD操作,所以实现了一个方法
protected SqlMapExecutor getSqlMapExecutor() {
SqlMapDaoTransaction trans = (SqlMapDaoTransaction) daoManager.getTransaction(this);
return trans.getSqlMap();
}
这个方法可以获得client对象。而daoManager是肯定可以获得,因此,要实现daoManager.getTransaction方法,参数是实例化对象或代理。
public DaoTransaction getTransaction(Dao dao) {
DaoImpl impl = (DaoImpl) daoImplMap.get(dao);
return impl.getDaoContext().getTransaction();
}
=>该方法应运而生=>daoImplMap内置对象要实现
写的比较粗糙,希望对那些看过源码的同学有所有帮助。有什么错误之处,也请大家积极指正。