MyBatis源码分析SQL语句执行的完整流程Word文档下载推荐.docx
《MyBatis源码分析SQL语句执行的完整流程Word文档下载推荐.docx》由会员分享,可在线阅读,更多相关《MyBatis源码分析SQL语句执行的完整流程Word文档下载推荐.docx(17页珍藏版)》请在冰豆网上搜索。
(图片来自《深入理解mybatis原理》MyBatis的架构设计以及实例分析)
2SQL执行流程
MyBatis主要设计目的还是为了让我们在执行SQL时对输入输出的数据的管理更加方便,所以方便的让我们写出SQL和方便的获取SQL的执行结果是MyBatis的核心竞争力。
下面就用一个例子来从源码角度看一下SQL的完整执行流程。
新建配置文件conf.xml:
conf.xml
首先建立数据表,这里就以user表为例:
复制代码
DROPTABLEIFEXISTSuser;
CREATETABLEuser(
idINTNOTNULLPRIMARYKEYAUTO_INCREMENT,
nameVARCHAR(32)NOTNULL,
passwordVARCHAR(32)NOTNULL,
sexint,
emailVARCHAR(32),
phoneVARCHAR(16),
adminVARCHAR(16)
);
然后新建与数据表对应的类User:
User
再新建usre表的配置文件:
userMapper.xml
最后新建测试类:
/**
*MyBatis测试类
*/
publicclassTestMain{
publicstaticvoidmain(String[]args)throwsIOException{
Stringresouce="
conf.xml"
;
InputStreamis=Resources.getResourceAsStream(resouce);
//构建sqlSession工厂
SqlSessionFactorysqlSessionFactory=newSqlSessionFactoryBuilder().build(is);
//获取sqlSession
SqlSessionsession=sqlSessionFactory.openSession();
Useruser;
try{
/**
*第一种方式:
直接执行已映射的SQL语句
Stringstatement="
com.luoxn28.dao.UserDao.getById"
user=session.selectOne(statement,1);
System.out.println(user);
}
finally{
session.close();
*第二种方式:
执行更清晰和类型安全的代码
//UserDaouserDao=session.getMapper(UserDao.class);
//user=userDao.getById
(1);
//System.out.println(user);
}
由于我们分析的是SQL的执行流程,那就重点关注下user=session.selectOne(statement,1);
这行代码~注意,传进去的参数是1。
session是DefaultSqlSession类型的,因为sqlSessionFactory默认生成的SqlSession是DefaultSqlSession类型。
selectOne()会调用selectList()。
//DefaultSqlSession类
public<
E>
List<
selectList(Stringstatement,Objectparameter,RowBoundsrowBounds){
MappedStatementms=configuration.getMappedStatement(statement);
//CURD操作是交给Excetor去处理的
returnexecutor.query(ms,wrapCollection(parameter),rowBounds,Executor.NO_RESULT_HANDLER);
}catch(Exceptione){
throwExceptionFactory.wrapException("
Errorqueryingdatabase.Cause:
"
+e,e);
}finally{
ErrorContext.instance().reset();
在DefaultSqlSession.selectList中的各种CURD操作都是通多Executor进行的,这里executor的类型是CachingExecutor,接着跳转到其中的query方法中。
//CachingExecutor类
query(MappedStatementms,ObjectparameterObject,RowBoundsrowBounds,ResultHandlerresultHandler)throwsSQLException{
BoundSqlboundSql=ms.getBoundSql(parameterObject);
//获取绑定的sql命令,比如"
SELECT*FROMxxx"
CacheKeykey=createCacheKey(ms,parameterObject,rowBounds,boundSql);
returnquery(ms,parameterObject,rowBounds,resultHandler,key,boundSql);
getBoundSql为了获取绑定的sql命令,在创建完cacheKey之后,就进入到CachingExecutor类中的另一个query方法中。
@Override
query(MappedStatementms,ObjectparameterObject,RowBoundsrowBounds,ResultHandlerresultHandler,CacheKeykey,BoundSqlboundSql)
throwsSQLException{
Cachecache=ms.getCache();
if(cache!
=null){
flushCacheIfRequired(ms);
if(ms.isUseCache()&
&
resultHandler==null){
ensureNoOutParams(ms,parameterObject,boundSql);
@SuppressWarnings("
unchecked"
)
list=(List<
)tcm.getObject(cache,key);
if(list==null){
list=delegate.<
query(ms,parameterObject,rowBounds,resultHandler,key,boundSql);
tcm.putObject(cache,key,list);
//issue#578and#116
returnlist;
returndelegate.<
这里真正执行query操作的是SimplyExecutor代理来完成的,接着就进入到了SimplyExecutor的父类BaseExecutor的query方法中。
//SimplyExecutor的父类BaseExecutor类
@SuppressWarnings("
query(MappedStatementms,Objectparameter,RowBoundsrowBounds,ResultHandlerresultHandler,CacheKeykey,BoundSqlboundSql)throwsSQLException{
ErrorContext.instance().resource(ms.getResource()).activity("
executingaquery"
).object(ms.getId());
if(closed){
thrownewExecutorException("
Executorwasclosed."
if(queryStack==0&
ms.isFlushCacheRequired()){
clearLocalCache();
list;
queryStack++;
*localCache是一级缓存,如果找不到就调用queryFromDatabase从数据库中查找
list=resultHandler==null?
(List<
)localCache.getObject(key):
null;
if(list!
handleLocallyCachedOutputParameters(ms,key,parameter,boundSql);
}else{
list=queryFromDatabase(ms,parameter,rowBounds,resultHandler,key,boundSql);
queryStack--;
if(queryStack==0){
for(DeferredLoaddeferredLoad:
deferredLoads){
deferredLoad.load();
//issue#601
deferredLoads.clear();
if(configuration.getLocalCacheScope()==LocalCacheScope.STATEMENT){
//issue#482
因为是第一次SQL查询操作,所以会调用queryFromDatabase方法来执行查询。
private<
queryFromDatabase(MappedStatementms,Objectparameter,RowBoundsrowBounds,ResultHandlerresultHandler,CacheKeykey,BoundSqlboundSql)throwsSQLException{
localCache.putObject(key,EXECUTION_PLACEHOLDER);
list=doQuery(ms,parameter,rowBounds,resultHandler,boundSql);
localCache.removeObject(key);
localCache.putObject(key,list);
if(ms.getStatementType()==StatementType.CALLABLE){
localOutputParameterCache.putObject(key,parameter);
从数据库中查询数据,进入到SimplyExecutor中进行操作。
//SimplyExecutor类
doQuery(MappedStatementms,Objectparameter,RowBoundsrowBounds,ResultHandlerresultHandler,BoundSqlboundSql)throwsSQLException{
Statementstmt=null;
Configurationconfiguration=ms.getConfiguration();
StatementHandlerhandler=configuration.newStatementHandler(wrapper,ms,parameter,rowBounds,resultHandler,boundSql);
//子流程1:
SQL查询参数的设置
stmt=prepareStatement(handler,ms.getStatementLog());
//StatementHandler封装了Statement
//子流程2:
SQL查询操作和结果集的封装
returnhandler.<
query(stmt);
closeStatement(stmt);
注意,在prepareStatement方法中会进行SQL查询参数的设置,也就是咱们最开始传递进来的参数,其值为1。
handler.<
query(stmt)方法中会进行实际的SQL查询操作和结果集的封装(封装成Java对象)。
当流程走到这里时,程序已经压栈有一定深度了,因为接下来程序分析会兵分两路,一方面深入到SQL查询及结果集的设置子流程1中,然后再深入到SQL查询操作和结果集的封装子流程2,因为还会回到这里,所以就来一张调用栈的特写吧:
子流程1:
privateStatementprepareStatement(StatementHandlerhandler,LogstatementLog)throwsSQLException{
Statementstmt;
//获取一个Connection
Connectionconnection=getConnection(statementLog);
stmt=handler.prepare(connection,transaction.getTimeout());
handler.parameterize(stmt);
//设置SQL查询中的参数值
returnstmt;
通过getConnection方法来获取一个Connection,调用prepare方法来获取一个Statement(这里的handler类型是RoutingStatementHandler,RoutingStatementHandler的prepare方法调用的是PrepareStatementHandler的prepare方法,因为PrepareStatementHandler并没有覆盖其父类的prepare方法,其实最后调用的是BaseStatementHandler中的prepare方法。
是不是绕晕了,那就再看一遍吧:
))。
调用parameterize方法来设置SQL的参数值(这里最后调用的是PrepareStatementHandler中的parameterize方法,而PrepareStatementHandler.parameterize方法调用的是DefaultParameterHandler中的setParameters方法)。
//PrepareStatementHandler类
publicvoidparameterize(Statementstatement)throwsSQLException{
parameterHandler.setParameters((PreparedStatement)statement);
//DefaultParameterHandler类
publicvoidsetParameters(PreparedStatementps){
ErrorContext.instance().activity("
settingparameters"
).object(mappedStatement.getParameterMap().getId());
ParameterMapping>
parameterMappings=boundSql.getParameterMappings();
if(parameterMappings!
for(inti=0;
i<
parameterMappings.size();
i++){
ParameterMappingparameterMapping=parameterMappings.get(i);
if(parameterMapping.getMode()!
=ParameterMode.OUT){
Objectvalue;
StringpropertyName=parameterMapping.getProperty();
if(boundSql.hasAdditionalParameter(propertyName)){//issue#448askfirstforadditionalparams
value=boundSql.getAdditionalParameter(propertyName);
}elseif(parameterObject==null){
value=null;
}elseif(typeHandlerRegistry.hasTypeHandler(parameterObject.getClass())){
value=parameterObject;
MetaObjectmetaObject=configuration.newMetaObject(parameterObject);
value=metaObject.getValue(propertyName);
TypeHandlertypeHandler=parameterMapping.getTypeHandler();
JdbcTypejdbcType=parameterMapping.getJdbcType();
if(value==null&
jdbcType==null){
jdbcType=configuration.getJdbcTypeForNull();
typeHandler.setParameter(ps,i+1,value,jdbcType);
}catch(TypeExceptione){
thrownewTypeException("
Couldnotsetparametersformapping:
+parameterMapping+"
.Cause:
}catch(SQLExceptione){
到这里为止,已经给Statement设置了最初传递进去的参数(值为1)了,那么接着分析流程2:
流程2:
SQL查询及结果集的设置
//RoutingStat