题记:
废话不多,慢慢写吧
写的可能有点乱,但思路是按照insert操作来写的。
问题1:线程安全
在整个app多线程中,可以共用一个SqlMapClient来执行操作。原理是利用了ThreadLocal,ThreadLocal实际上是不同的Thread以ThreadLocal自身对象为key的一份ThreadLocalMap拷贝,每个thread创建了各自的Map,这个Map的key就是ThreadLocal的hashcode,所以,同一个ThreadLocal存储的不同value会存储在Map中table数组的同一个index上,并组成了链表。不同的ThreadLocal则存储在Thread中不同的index处,这个Map也不可能大小总为1;
所以为了用ThreadLocal,我想一般需要增加一个胶合层来做一些工作,看上去像门面模式。在ibatis中,这个类就是SqlMapClientImpl,如下:
public class SqlMapClientImpl implements ExtendedSqlMapClient {
/**
* Delegate for SQL execution
*/
public SqlMapExecutorDelegate delegate;
protected ThreadLocal localSqlMapSession = new ThreadLocal();
protected SqlMapSessionImpl getLocalSqlMapSession() {
SqlMapSessionImpl sqlMapSession = (SqlMapSessionImpl) localSqlMapSession.get();
if (sqlMapSession == null || sqlMapSession.isClosed()) {
sqlMapSession = new SqlMapSessionImpl(this);
localSqlMapSession.set(sqlMapSession);
}
return sqlMapSession;
}
...
}
这里看到,真正做事的是SqlMapExecutorDelegate这个类,并且还注意到,这里其实把SqlMapExecutorDelegate这个实现类传递给了sqlMapSession,又封装了一层。
问题2:Session上下文(事务控制)
不是很深刻理解SessionScope,但从SessionScope的成员变量上看,每个SessionScope管理自己的事务,在这个事务中执行过的所有sql语句。
去看了看SqlMapSessionImpl ,并且集成了SqlMapExecutor接口,这里应该是代理模式,因为SqlMapExecutorDelegate和SqlMapSessionImpl的接口是“相同”,如下:
public class SqlMapSessionImpl implements SqlMapSession {
protected SqlMapExecutorDelegate delegate;
protected SessionScope session;
protected boolean closed;
/**
* Constructor
*
* @param client - the client that will use the session
*/
public SqlMapSessionImpl(ExtendedSqlMapClient client) {
this.delegate = client.getDelegate();
this.session = this.delegate.popSession();
this.session.setSqlMapClient(client);
this.session.setSqlMapExecutor(client);
this.session.setSqlMapTxMgr(client);
this.closed = false;
}
多出了个SessionScope,这个SessionScope类似装饰模式,而SessionScope应该是上下文的主要工作类了。SessionScope被SqlMapExecutorDelegate中的一个pool维护着,到此,可以看到很多操作都包装在了SqlMapExecutorDelegate类里。
SessionScope对象池用Collections.synchronizedList(new ArrayList(size));线程安全的List来存储,因为所有SessionScope都公用同一个SqlMapExecutorDelegate。
总结下关系是:SqlMapSessionImpl 封装了主要工作类SessionScope 和 接收 外部引用SqlMapExecutorDelegate,SessionScope正是由SqlMapExecutorDelegate的对象池来管理。SqlMapSessionImpl只是做了代理工作。
目前我感觉不用SqlMapSessionImpl代理类,直接用delegate.popSession();来获得一个SessionScope也应该是可以的,为什么要这样设计呢?理由在哪里呢?
问题3:insert操作
我们继续,看到每个线程把持着自己的SqlMapSessionImpl,并且在SqlMapSessionImpl初始化的时候从对象池中的List拿到了一个SessionScope,SessionScope里包含了Transaction,接着就可以进行数据库操作了就看SqlMapSessionImpl 的insert方法:
public Object insert(String id, Object param) throws SQLException {
return delegate.insert(session, id, param);
}
看到,insert操作回传给了公共的SqlMapExecutorDelegate,并传递了SessionScope来区分。
insert代码如下:
public Object insert(SessionScope session, String id, Object param) throws SQLException {
Object generatedKey = null;
MappedStatement ms = getMappedStatement(id);
Transaction trans = getTransaction(session);
boolean autoStart = trans == null;
try {
trans = autoStartTransaction(session, autoStart, trans);
SelectKeyStatement selectKeyStatement = null;
if (ms instanceof InsertStatement) {
selectKeyStatement = ((InsertStatement) ms).getSelectKeyStatement();
}
if (selectKeyStatement != null && !selectKeyStatement.isAfter()) {
generatedKey = executeSelectKey(session, trans, ms, param);
}
RequestScope request = popRequest(session, ms);
try {
ms.executeUpdate(request, trans, param);
} finally {
pushRequest(request);
}
if (selectKeyStatement != null && selectKeyStatement.isAfter()) {
generatedKey = executeSelectKey(session, trans, ms, param);
}
autoCommitTransaction(session, autoStart);
} finally {
autoEndTransaction(session, autoStart);
}
return generatedKey;
}
id是XML中定义SQL语句的名称,param是参数。用getMappedStatement方法,根据id来获取需要执行的MappedStatement,XML文件中所有定义的Statement都存储在一个叫mappedStatements的HashMap中,并以XML定义的名称(即Id)为key,MappedStatement对象为value,这样根据名称就取到了对应的MappedStatement。
然后根据session来获取Transaction,如下:
public Transaction getTransaction(SessionScope session) { return session.getTransaction(); }
很显然,在session初始化的时候就已经定义好了一个Transaction,但这个Transaction不一定有值。
拿到Transaction后,判断是否自动提交事务,如果是的,则开始session中的事务,个人觉得一个好纠结的函数,希望有大大来解释下:
protected Transaction autoStartTransaction(SessionScope session, boolean autoStart, Transaction trans) throws SQLException {
Transaction transaction = trans;
if (autoStart) {
session.getSqlMapTxMgr().startTransaction();
transaction = getTransaction(session);
}
return transaction;
}
然后看到session.getSqlMapTxMgr().startTransaction();这个方法,根据之前的分析,SqlMapSessionImpl管理这一个SessionScope和一个SqlMapExecutorDelegate,而getSqlMapTxMgr()实际上指向了SqlMapSessionImpl,而SqlMapSessionImpl只是一个代理类,这时SqlMapSessionImpl的startTransaction()方法实际指向了SqlMapExecutorDelegate的startTransaction(Session)方法,因为SqlMapSessionImpl把持这SessionScope和SqlMapExecutorDelegate对象,所以很轻松把当前SqlMapSessionImpl中的SessionScope为参数调用SqlMapExecutorDelegate的startTransaction(Session)了,最后SqlMapExecutorDelegate调用自己的事务管理类TransactionManager把Session参数传进去,并做事务操作,startTransaction这时新建立一个Transaction对象,并把new出来的Transaction对象赋值给Session,且事务池+1,设定状态。
总结一下:SqlMapSessionImpl作为了SessionScope和SqlMapExecutorDelegate中间沟通的桥梁。
executeUpdate被封装在GeneralStatement对象里面,并其子类有DeleteStatement,SelectStatement,InsertStatement等,所有的SQL解析,执行,返回结果集都集中在这里。
最后提交事务:autoCommitTransaction(session, autoStart);如果autoStart为true,则执行session中的commitTransaction()方法,类似startTransaction()一样实际调用SqlMapExecutorDelegate的commitTransaction()方法,利用TransactionManager类的public void commit(SessionScope session)方法提交事务,如下:
public void commit(SessionScope session) throws SQLException, TransactionException {
Transaction trans = session.getTransaction();
TransactionState state = session.getTransactionState();
if (state == TransactionState.STATE_USER_PROVIDED) {
} else if (state != TransactionState.STATE_STARTED && state != TransactionState.STATE_COMMITTED ) {
throw new TransactionException("TransactionManager could not commit. No transaction is started.");
}
if (session.isCommitRequired() || forceCommit) {
trans.commit();
session.setCommitRequired(false);
}
session.setTransactionState(TransactionState.STATE_COMMITTED);
}
总结一下:所有的事务控制都转接给公共SqlMapExecutorDelegate类中的TransactionManager类操作,TransactionManager自己维护着一个事务池,事务的新增和销毁都在这里集中管理。
问题4:执行SQL语句
之前分析到SQL语句的校验、执行和结果集的返回都封装在了XXXStatement类中,BaseStatement是个抽象类,也是个很重要的类,GeneralStatement是extend这个抽象类的基础类。
其中执行sql的语句为:
ms.executeUpdate(request, trans, param);
request是从request对象池中取到的(request对象池?what's that?),trans只是用来传递Connection的,param是参数对象了。
首先建立点关键日志信息:ErrorContext;
然后校验param参数,通过GeneralStatement的validateParameter方法,不过我没看明白;
然后通过Sql接口的getParameterMap方法取sql语句中映射的元素,返回ParameterMap;
取返回的元素,通过getResultMap方法,和上面类似,返回ResultMap;所以可以看到ParameterMap和ResultMap这两组类是存储ibatis的映射的,进一步发现ParameterMap和ResultMap其实在初始化XML的时候就解析好了,并储存在这些BaseStatement里,一条sql语句一个BaseStatement对象,而这些BaseStatement对象缓存SqlMapExecutorDelegate里的一个HashMap里,要用的时候就拿出来,并非用时再临时创建。另外这里的Sql接口被设计成为支持动态sql和静态sql,接收的参数为RequestScopes(存储了ParameterMap和ResultMap)、parameterObject。
然后开始取parameterObject的值(也就是param对象里的成员变量值),通过ParameterMap的getParameterObjectValues方法,ParameterMap内部则调用DataExchange的getData方法(不同的param,比如JavaBean,Map,List需要不同的取值方式),所以每个ParameterMap都指向一个自己特定的DataExchange方式,有ComplexDataExchange,DomDataExchange,JavaBeanDataExchange,ListDataExchange等,用来支持XML中指定不同的parameter。每个DataExchange处理方式当然就不一样了,但都返回一个Object []数组。
然后对于动态sql,需要重新构造一个新的sql,调用String sqlString = sql.getSql(request, parameterObject);方法;
然后开始执行sql,调用rows = sqlExecuteUpdate(request, trans.getConnection(), sqlString, parameters);实际上是调用SqlExecutor这个实现类来执行所有sql的。
首先构造PreparedStatement,先从SessionScope中取是否有缓存,设置PreparedStatement超时时间,然后通过ParameterMap的setParameters方法将之前取的parameterObject值set到PreparedStatement里面去。
最后执行execute,取到返回行数。
最后清理下现场。其中有个executeListeners,没看太明白。
总结一下:在ibatis初始化的时候,也就是解析XML配置文件的时候,就对XML中每一个sql语句创建了一个BaseStatement对象存储在SqlMapExecutorDelegate的一个HashMap里面,在创建BaseStatement的时候还额外创建了ParameterMap和ResultMap对象,ParameterMap是用来管理sql中的输入参数的,根据parameterClass的不同,创建用于处理不同的DomDataExchange,而ResultMap则是管理sql中的输出结果的。在执行insert操作的时候,只需要从HashMap中取到对应的BaseStatement,并校验外部传来的param,获取param里的值,并重新构造一个sql语句,然后创建PreparedStatement,往PreparedStatement中注入这些values,最后执行,整个过程就基本执行了。
整段代码如下:
public int executeUpdate(RequestScope request, Transaction trans, Object parameterObject)
throws SQLException {
ErrorContext errorContext = request.getErrorContext();
errorContext.setActivity("preparing the mapped statement for execution");
errorContext.setObjectId(this.getId());
errorContext.setResource(this.getResource());
request.getSession().setCommitRequired(true);
try {
parameterObject = validateParameter(parameterObject);
Sql sql = getSql();
errorContext.setMoreInfo("Check the parameter map.");
ParameterMap parameterMap = sql.getParameterMap(request, parameterObject);
errorContext.setMoreInfo("Check the result map.");
ResultMap resultMap = sql.getResultMap(request, parameterObject);
request.setResultMap(resultMap);
request.setParameterMap(parameterMap);
int rows = 0;
errorContext.setMoreInfo("Check the parameter map.");
Object[] parameters = parameterMap.getParameterObjectValues(request, parameterObject);
errorContext.setMoreInfo("Check the SQL statement.");
String sqlString = sql.getSql(request, parameterObject);
errorContext.setActivity("executing mapped statement");
errorContext.setMoreInfo("Check the statement or the result map.");
rows = sqlExecuteUpdate(request, trans.getConnection(), sqlString, parameters);
errorContext.setMoreInfo("Check the output parameters.");
if (parameterObject != null) {
postProcessParameterObject(request, parameterObject, parameters);
}
errorContext.reset();
sql.cleanup(request);
notifyListeners();
return rows;
} catch (SQLException e) {
errorContext.setCause(e);
throw new NestedSQLException(errorContext.toString(), e.getSQLState(), e.getErrorCode(), e);
} catch (Exception e) {
errorContext.setCause(e);
throw new NestedSQLException(errorContext.toString(), e);
}
}
Update
update跟insert就都是从一个娘胎肚子里出来的,只是SqlMapExecutorDelegate的update方法里,没有executeSelectKey的操作。
Query
query就复杂点,因为要处理skipResults,maxResults,以及结果集,所以多出了RowHandler。
分享到:
相关推荐
在这个2.3.4.726版本的源码中,我们可以深入理解iBATIS DAO的工作原理,并通过添加注释来帮助我们更好地掌握其实现细节。 首先,iBATIS DAO的核心概念是SQL Maps,它们定义了数据库操作的SQL语句,并将其映射到Java...
这里提到的是iBATIS的三个不同版本的jar包:ibatis-2.3.4.726.jar、ibatis-2.3.0.677.jar以及ibatis-2.3.3.720.jar。 首先,让我们深入了解iBATIS的核心概念和功能: 1. **SQL Map配置**:iBATIS的核心是SQL Map...
*架构struts2_spring2.5_ibatis2.3 *mysql5.0 *jdk 1.6 *带有所有jar包,可直接运行 本实例实现了用户登陆,用户信息CRUD相关操作。让你感受到了ibatis做o/r mapping的方便快捷。 下次集成dwr进来 create ...
ibatis-2.3.3.720.jar
在本主题中,我们将深入探讨Ibatis的两个特定版本:ibatis2.3.4.8.jar和ibatis-2.3.4.726.jar。 Ibatis的核心概念包括映射器(Mapper)、SQL映射文件和SqlSession。映射器是Ibatis的主要组件,它定义了数据库操作与...
ibatis-2.3.3.7.jar
标题 "ibatis UPDATE 动态XML与insert" 涉及到的是MyBatis框架中对数据库数据进行更新(UPDATE)和插入(INSERT)操作的动态XML配置方式。MyBatis是一个优秀的持久层框架,它支持定制化SQL、存储过程以及高级映射,...
《深入解析iBatis 2.3源码》 iBatis,作为一款经典的Java持久层框架,以其轻量级、灵活的特性深受开发者喜爱。本文将深入剖析iBatis 2.3的源码,帮助读者理解其内部机制,提升开发与优化数据库访问的能力。 1. **...
标题中的“iBATIS_Spring_struts_demo.rar_DEMO_Struct spring ibatis_ibatis j”表明这是一个关于集成iBATIS、Spring和Struts的演示项目。这个DEMO旨在展示如何在Java应用中有效地整合这三个框架,从而实现数据持久...
ibatis-2.3.4.732.jar
标题 "ibatis-2.3.0.677.jar" 指向的是一个特定版本的 iBATIS 库,即版本号为 2.3.0.677 的 JAR 文件。iBATIS 是一个开源的 Java 框架,主要用于简化数据库与应用程序之间的交互。它在早期广泛应用于企业级应用开发...
总之,iBatis 2.3.2.715的源代码不仅为我们提供了深入学习和理解iBatis工作原理的机会,还为开发者提供了丰富的示例和详尽的文档,有助于我们在实际项目中更高效、更灵活地使用iBatis。通过对这些源代码的探索和学习...
在Java代码中,我们通过定义接口并使用`@Select`, `@Insert`, `@Update`, `@Delete`等注解,或者直接继承SqlMapClientTemplate,来实现对数据库的操作。例如: ```java public interface UserService { @Select...
ibatis-2.3.4.726最新API chm格式 非常好用
在 iBATIS 中,添加数据通常是通过调用 SQL Map 中的 `<insert>` 标签来完成的。开发者定义一个 SQL 语句,并将 Java 对象的属性映射到 SQL 语句的占位符上,iBATIS 自动处理参数绑定和事务提交。 2. **删除...
这个压缩包提供了对iBatis 2.x版本的深入理解,特别是对于那些希望查看或学习iBatis 2.2.0.638源码的开发者来说,极具价值。 首先,让我们深入了解iBatis的核心概念。iBatis的主要功能是作为一个SQL映射框架,它将...
在2.3版本中,Ibatis提供了许多关键功能,帮助开发人员更高效地处理数据库操作。 Ibatis的核心概念是Mapper,它是一个XML配置文件或者注解,用来定义SQL语句、存储过程以及参数映射和结果映射。这种分离使得SQL逻辑...
《深入解析iBatis 2.3.4.726源码》 iBatis,作为一款轻量级的Java持久层框架,以其灵活、高效的特点,在许多项目中得到了广泛应用。本文将针对iBatis 2.3.4.726版本的源代码进行详尽解读,帮助开发者深入了解其内部...
【标题】"ibatis2.3源码"指的是开源的SQL映射框架iBATIS的2.3版本的源代码。iBATIS是Java平台上的一种轻量级持久层框架,它将SQL语句与Java代码分离,使得开发者可以更加灵活地处理数据库操作。 【描述】中的"可以...
《深入解析ibatis-2.3.2.715.jar》 在Java开发领域,Ibatis作为一个轻量级的持久层框架,以其灵活、易用的特点深受开发者喜爱。本次我们将详细探讨`ibatis-2.3.2.715.jar`这个特定版本的Ibatis库,它为开发者提供了...