MyBatis源码分析SQL语句执行的完整流程.docx

上传人:b****4 文档编号:26783022 上传时间:2023-06-22 格式:DOCX 页数:21 大小:20.97KB
下载 相关 举报
MyBatis源码分析SQL语句执行的完整流程.docx_第1页
第1页 / 共21页
MyBatis源码分析SQL语句执行的完整流程.docx_第2页
第2页 / 共21页
MyBatis源码分析SQL语句执行的完整流程.docx_第3页
第3页 / 共21页
MyBatis源码分析SQL语句执行的完整流程.docx_第4页
第4页 / 共21页
MyBatis源码分析SQL语句执行的完整流程.docx_第5页
第5页 / 共21页
点击查看更多>>
下载资源
资源描述

MyBatis源码分析SQL语句执行的完整流程.docx

《MyBatis源码分析SQL语句执行的完整流程.docx》由会员分享,可在线阅读,更多相关《MyBatis源码分析SQL语句执行的完整流程.docx(21页珍藏版)》请在冰豆网上搜索。

MyBatis源码分析SQL语句执行的完整流程.docx

MyBatis源码分析SQL语句执行的完整流程

MyBatis源码分析-SQL语句执行的完整流程

  MyBatis是支持定制化SQL、存储过程以及高级映射的优秀的持久层框架。

MyBatis避免了几乎所有的JDBC代码和手动设置参数以及获取结果集。

MyBatis可以对配置和原生Map使用简单的XML或注解,将接口和Java的POJOs(PlainOldJavaObjects,普通的Java对象)映射成数据库中的记录。

如何新建MyBatis源码工程请点击MyBatis源码分析-IDEA新建MyBatis源码工程。

  MyBatis框架主要完成的是以下2件事情:

根据JDBC规范建立与数据库的连接。

通过反射打通Java对象与数据库参数交互之间相互转换的关系。

  MyBatis框架是一种典型的交互式框架,先准备好交互的必要条件,然后构建一个交互的环境,在交互环境中划分会话,在会话中与数据库进行交互数据。

1MyBatis主要的类

ConfigurationMyBatis所有的配置信息都维持在Configuration对象之中。

SqlSession作为MyBatis工作的主要顶层API,表示和数据库交互的会话,完成必要数据库增删改查功能

ExecutorMyBatis执行器,是MyBatis调度的核心,负责SQL语句的生成和查询缓存的维护

StatementHandler封装了JDBCStatement操作,负责对JDBCstatement的操作,如设置参数、将Statement结果集转换成List集合。

ParameterHandler负责对用户传递的参数转换成JDBCStatement所需要的参数,

ResultSetHandler负责将JDBC返回的ResultSet结果集对象转换成List类型的集合;

TypeHandler负责java数据类型和jdbc数据类型之间的映射和转换

MappedStatementMappedStatement维护了一条节点的封装,

SqlSource负责根据用户传递的parameterObject,动态地生成SQL语句,将信息封装到BoundSql对象中,并返回

BoundSql表示动态生成的SQL语句以及相应的参数信息

  以上几个类在SQL操作中都会涉及,在SQL操作中重点关注下SQL参数什么时候写入和结果集怎么转换为Java对象,这两个过程正好对应的类是PreparedStatementHandler和ResultSetHandler类。

 

(图片来自《深入理解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类

publicListselectList(Stringstatement,Objectparameter,RowBoundsrowBounds){

try{

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类

publicListquery(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方法中。

复制代码

//CachingExecutor类

@Override

publicListquery(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")

Listlist=(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(ms,parameterObject,rowBounds,resultHandler,key,boundSql);

}

复制代码

  这里真正执行query操作的是SimplyExecutor代理来完成的,接着就进入到了SimplyExecutor的父类BaseExecutor的query方法中。

复制代码

//SimplyExecutor的父类BaseExecutor类

@SuppressWarnings("unchecked")

@Override

publicListquery(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();

}

Listlist;

try{

queryStack++;

/**

*localCache是一级缓存,如果找不到就调用queryFromDatabase从数据库中查找

*/

list=resultHandler==null?

(List)localCache.getObject(key):

null;

if(list!

=null){

handleLocallyCachedOutputParameters(ms,key,parameter,boundSql);

}else{

list=queryFromDatabase(ms,parameter,rowBounds,resultHandler,key,boundSql);

}

}finally{

queryStack--;

}

if(queryStack==0){

for(DeferredLoaddeferredLoad:

deferredLoads){

deferredLoad.load();

}

//issue#601

deferredLoads.clear();

if(configuration.getLocalCacheScope()==LocalCacheScope.STATEMENT){

//issue#482

clearLocalCache();

}

}

returnlist;

}

复制代码

  因为是第一次SQL查询操作,所以会调用queryFromDatabase方法来执行查询。

复制代码

//SimplyExecutor的父类BaseExecutor类

privateListqueryFromDatabase(MappedStatementms,Objectparameter,RowBoundsrowBounds,ResultHandlerresultHandler,CacheKeykey,BoundSqlboundSql)throwsSQLException{

Listlist;

localCache.putObject(key,EXECUTION_PLACEHOLDER);

try{

list=doQuery(ms,parameter,rowBounds,resultHandler,boundSql);

}finally{

localCache.removeObject(key);

}

localCache.putObject(key,list);

if(ms.getStatementType()==StatementType.CALLABLE){

localOutputParameterCache.putObject(key,parameter);

}

returnlist;

}

复制代码

  从数据库中查询数据,进入到SimplyExecutor中进行操作。

复制代码

//SimplyExecutor类

publicListdoQuery(MappedStatementms,Objectparameter,RowBoundsrowBounds,ResultHandlerresultHandler,BoundSqlboundSql)throwsSQLException{

Statementstmt=null;

try{

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);

}finally{

closeStatement(stmt);

}

}

复制代码

  注意,在prepareStatement方法中会进行SQL查询参数的设置,也就是咱们最开始传递进来的参数,其值为1。

handler.query(stmt)方法中会进行实际的SQL查询操作和结果集的封装(封装成Java对象)。

当流程走到这里时,程序已经压栈有一定深度了,因为接下来程序分析会兵分两路,一方面深入到SQL查询及结果集的设置子流程1中,然后再深入到SQL查询操作和结果集的封装子流程2,因为还会回到这里,所以就来一张调用栈的特写吧:

 

子流程1:

SQL查询参数的设置

复制代码

//SimplyExecutor类

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类

@Override

publicvoidparameterize(Statementstatement)throwsSQLException{

parameterHandler.setParameters((PreparedStatement)statement);

}

复制代码

//DefaultParameterHandler类

@Override

publicvoidsetParameters(PreparedStatementps){

ErrorContext.instance().activity("settingparameters").object(mappedStatement.getParameterMap().getId());

ListparameterMappings=boundSql.getParameterMappings();

if(parameterMappings!

=null){

for(inti=0;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;

}else{

MetaObjectmetaObject=configuration.newMetaObject(parameterObject);

value=metaObject.getValue(propertyName);

}

TypeHandlertypeHandler=parameterMapping.getTypeHandler();

JdbcTypejdbcType=parameterMapping.getJdbcType();

if(value==null&&jdbcType==null){

jdbcType=configuration.getJdbcTypeForNull();

}

try{

typeHandler.setParameter(ps,i+1,value,jdbcType);

}catch(TypeExceptione){

thrownewTypeException("Couldnotsetparametersformapping:

"+parameterMapping+".Cause:

"+e,e);

}catch(SQLExceptione){

thrownewTypeException("Couldnotsetparametersformapping:

"+parameterMapping+".Cause:

"+e,e);

}

}

}

}

}

复制代码

  到这里为止,已经给Statement设置了最初传递进去的参数(值为1)了,那么接着分析流程2:

流程2:

SQL查询及结果集的设置

//RoutingStat

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > 总结汇报 > 学习总结

copyright@ 2008-2022 冰豆网网站版权所有

经营许可证编号:鄂ICP备2022015515号-1