Mybatis源码阅读系列文章:

MyBatis源码阅读(一) --- 源码阅读环境搭建

MyBatis源码阅读(二) --- 执行流程分析

MyBatis源码阅读(三) --- 配置信息的解析以及SqlSessionFactory构建过程

MyBatis源码阅读(四) --- SqlSession的创建过程

MyBatis源码阅读(五) ---Mapper接口的获取过程

MyBatis源码阅读(六) ---mapper方法具体执行流程分析

MyBatis源码阅读(七) --- 查询结果集封装流程

MyBatis源码阅读(八) --- Executor执行器

MyBatis源码阅读(九) --- 插件原理

MyBatis源码阅读(十) --- 一级缓存、二级缓存工作原理

MyBatis源码阅读(十一) --- MyBatis事务管理机制

MyBatis源码阅读(十二) --- Spring加载MyBatis过程

目录

一、概述

二、Mybatis核心类

三、MyBatis执行流程

四、执行流程图

五、总结


一、概述

前面一篇文章我们已经搭建好了Mybatis的源码调试环境,那么今天我们先来看看MyBatis整体的执行流程是怎样的,先对整体有个了解,后面我们再针对各个细节进行分析。在分析执行流程之前,我们先对Mybatis中几个核心类做个简单的介绍。

二、Mybatis核心类

Mybatis核心类主要有下面几个:

  • SqlSessionFactory

每个基于 MyBatis 的应用都是以一个 SqlSessionFactory 的实例为中心的。SqlSessionFactory 的实例可以通过 SqlSessionFactoryBuilder 获得。而 SqlSessionFactoryBuilder 则可以从 XML 配置文件或通过Java的方式构建出 SqlSessionFactory 的实例。SqlSessionFactory 一旦被创建就应该在应用的运行期间一直存在,建议使用单例模式或者静态单例模式。一个SqlSessionFactory对应配置文件中的一个环境(environment),如果你要使用多个数据库就配置多个环境分别对应一个SqlSessionFactory。SqlSessionFactory类的主要方法:

public interface SqlSessionFactory {SqlSession openSession();SqlSession openSession(boolean autoCommit);SqlSession openSession(Connection connection);SqlSession openSession(TransactionIsolationLevel level);SqlSession openSession(ExecutorType execType);SqlSession openSession(ExecutorType execType, boolean autoCommit);SqlSession openSession(ExecutorType execType, TransactionIsolationLevel level);SqlSession openSession(ExecutorType execType, Connection connection);Configuration getConfiguration();}

SqlSessionFactory其实是一个工厂,是为了创建SqlSession对象。SqlSession是MyBatis面向数据库的高级接口,其提供了执行查询sql,更新sql,提交事务,回滚事务,获取映射代理类等等方法。

  • SqlSession

SqlSession作为Mybatis的顶层API接口,作为会话访问,完成增删改查操作。它是一个接口,它有2个实现类,分别是DefaultSqlSession(默认使用)和SqlSessionManager。SqlSession通过内部存放的执行器(Executor)来对数据进行CRUD操作。此外SqlSession不是线程安全的,所以每一次操作完数据库后都要调用close对其进行关闭,官方建议通过try-finally来保证总是关闭SqlSession。

我们来看一下SqlSession类中主要的方法:

public interface SqlSession extends Closeable {/*** 查询一个结果对象**/ <T> T selectOne(String statement, Object parameter);/*** 查询一个结果集合**/ <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds);/*** 查询一个map**/ <K, V> Map<K, V> selectMap(String statement, Object parameter, String mapKey, RowBounds rowBounds);/*** 查询游标**/ <T> Cursor<T> selectCursor(String statement, Object parameter, RowBounds rowBounds);void select(String statement, Object parameter, RowBounds rowBounds, ResultHandler handler);/*** 插入**/ int insert(String statement, Object parameter);/*** 修改**/ int update(String statement, Object parameter);/*** 删除**/int delete(String statement, Object parameter);/*** 提交事物**/void commit(boolean force);/*** 回滚事物**/void rollback(boolean force);List<BatchResult> flushStatements();void close();void clearCache();Configuration getConfiguration();/*** 获取映射代理类**/<T> T getMapper(Class<T> type);/*** 获取数据库连接**/Connection getConnection();
}
  • Executor

Mybatis执行器,负责SQL动态语句的生成和查询缓存的维护。它有两个实现类,其中BaseExecutor有三个继承类分别是:

  1. BatchExecutor批处理型执行器(批量操作);
  2. ReuseExecutor重用型执行器(重用预处理语句prepared statement,跟Simple的唯一区别就是内部缓存statement);
  3. SimpleExecutor简单型执行器(默认是SimpleExecutor,每次都会创建新的statement);
  • MappedStatement

MappedStatement用来封装我们mapper.xml映射文件中的信息,包括sql语句,输入参数,输出参数等。一个SQL节点对应一个MappedStatement对象。

  • Handler

处理器,Mybatis中有3种类型的Hanlder,分别为StatementHandler、ParameterHandler、ResultSetHandler。ParameterHandler主要解析参数,为Statement设置参数,ResultSetHandler主要是负责把ResultSet转换成Java对象。其实执行器Executor是调用StatementHandler来执行数据库操作。

  • StatementHandler:负责处理JDBC的statement的交互,包括对statement设置参数、以及将JDBC返回的结果集转换为List等;
  • ParameterHandler:负责根据传递的参数值,对statement对象设置参数;
  • ResultSetHandler:负责解析JDBC返回的结果集;
  • TypeHandler:负责jdbcType与javaType之间的数据转换;

接下来我们阅读源码的时候,也基本上是根据这几个关键的核心类为中心,总结一下各个组件分别发挥了什么作用。

三、MyBatis执行流程

public static void main(String[] args) {//1、读取配置文件String resource = "mybatis-config.xml";InputStream inputStream;SqlSession sqlSession = null;try {inputStream = Resources.getResourceAsStream(resource);//2、初始化mybatis,创建SqlSessionFactory类实例SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);System.out.println(sqlSessionFactory);//3、创建Session实例sqlSession = sqlSessionFactory.openSession();//4、获取Mapper接口UserMapper userMapper = sqlSession.getMapper(UserMapper.class);//5、执行SQL操作User user = userMapper.getById(1L);System.out.println(user);} catch (IOException e) {e.printStackTrace();} finally {//6、关闭sqlSession会话if (null != sqlSession) {sqlSession.close();}}
}

第一步:Resources.getResourceAsStream(resource)

首先我们在mybatis-config.xml中环境,映射文件路径、插件,反射工厂,类型处理器等等其它内容,所以第一步肯定也是去读取这个配置。通过Resources加载配置好的mybatis-config.xml配置文件。Resources是ibatis.io包下面的类,也就是一个io流,用于读写文件,通过getResourceAsStream把xml文件加载进来,把配置文件解析为一个流。解析生成的文件流对象用于构建SqlSessionFactory。

第二步:SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);

这里采用了构建者模式,通过上一步解析生成的配置文件流对象,调用SqlSessionFactoryBuilder的build()创建SqlSessionFactory。

我们看一下build()方法的源码:

public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {try {//通过XMLConfigBuilder解析配置文件,解析的配置相关信息都会封装为一个Configuration对象XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);//build()方法创建DefaultSessionFactory对象return build(parser.parse());} catch (Exception e) {throw ExceptionFactory.wrapException("Error building SqlSession.", e);} finally {ErrorContext.instance().reset();try {inputStream.close();} catch (IOException e) {// Intentionally ignore. Prefer previous error.}}
}

XMLConfigBuilder 是用来解析XML文件的一个构建者,通过他的parse()方法解析mybatis配置文件。

我们看一下parse()方法的源码,parse方法返回的是一个configuration对象,我们之前配置的插件,对象工厂,反射工厂,映射文件,类型解析器等等解析完成后都存储在Configuration对象中,方便后续各个对象直接获取。

public Configuration parse() {if (parsed) {throw new BuilderException("Each XMLConfigBuilder can only be used once.");}parsed = true;//解析configuration标签下的所有配置parseConfiguration(parser.evalNode("/configuration"));return configuration;
}

可以看到,里面具体调用了parseConfiguration()方法对configuration标签下的子节点的所有配置进行解析。继续看一下parseConfiguration()方法:

//org.apache.ibatis.builder.xml.XMLConfigBuilder#parseConfiguration
private void parseConfiguration(XNode root) {try {//先解析properties. 这些属性都是可外部配置且可动态替换的,既可以在典型的 Java 属性文件中配置,亦可通过 properties 元素的子元素来传递。propertiesElement(root.evalNode("properties"));//解析全局配置settings相关配置Properties settings = settingsAsProperties(root.evalNode("settings"));loadCustomVfs(settings);loadCustomLogImpl(settings);//typeAliases别名配置解析, 别名配置有两种方式: a.指定单个实体; b.指定包路径//类型别名是为Java类型设置一个短的名字, 用来减少类完全限定名的冗余typeAliasesElement(root.evalNode("typeAliases"));//插件解析pluginElement(root.evalNode("plugins"));objectFactoryElement(root.evalNode("objectFactory"));objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));reflectorFactoryElement(root.evalNode("reflectorFactory"));settingsElement(settings);// environments里面其实就包含我们的数据库连接信息等的配置,重点,下面详细介绍.environmentsElement(root.evalNode("environments"));databaseIdProviderElement(root.evalNode("databaseIdProvider"));//类型转换器相关配置解析,用于数据库类型和Java数据类型的转换typeHandlerElement(root.evalNode("typeHandlers"));//mapper接口配置解析,重点,下面详细介绍mapperElement(root.evalNode("mappers"));} catch (Exception e) {throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e);}
}

如上可以看到,parseConfiguration()方法其实就是对我们mybatis-config.xml中具体的标签内容属性的解析。如environment、mapper、setting等标签的解析,解析之后的所有配置都将会保存在了全局配置对象configuration中。

parseConfiguration()方法执行完后,意味着parse()方法也执行完成,我们发现parse()最后返回了一个configuration对象,它是用来存放mybatis核心配置文件解析完成后的结果。继续看源码,我们看到这个configuration配置对象返回给了SqlSessionFactory的build()方法,如下所示:

public SqlSessionFactory build(Configuration config) {return new DefaultSqlSessionFactory(config);
}

可以看到, build()方法返回了一个DefaultSqlSessionFactory对象,这是SqlSessionFactory的实现类。如下图,SqlSessionFactory有两个实现类,其中一个就是DefaultSqlSessionFactory,另外一个是SqlSessionManager,默认返回的是DefaultSqlSessionFactory。

到这里,我们的第二步就算完成了。

第三步:SqlSession sqlSession = sqlSessionFactory.openSession();

这一步主要的目的是生成SqlSession会话,通过sqlSessionFactory的openSession()方法开启一个会话。注意,SqlSession不是线程安全的,所以每次使用完记得都关闭它。SqlSession是MyBatis面向数据库的高级接口,其提供了执行查询sql,更新sql,提交事物,回滚事物,获取映射代理类等方法。

下面我们来看openSession()的源码:

获取sqlSession有两种方式,一种是从数据源中获取的,还有一种是从连接中获取,默认是从数据源中进行获取,如下:

//org.apache.ibatis.session.defaults.DefaultSqlSessionFactory#openSession()
public SqlSession openSession() {//默认是从数据库连接中获取一个SqlSession会话return openSessionFromDataSource(configuration.getDefaultExecutorType(), null, false);
}//org.apache.ibatis.session.defaults.DefaultSqlSessionFactory#openSessionFromDataSource
private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) {Transaction tx = null;try {//通过Confuguration对象去获取Mybatis相关配置信息, Environment对象包含了数据源和事务的配置final Environment environment = configuration.getEnvironment();//事务工厂,这里是JbdcTransactionFactory工厂类final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment);//通过事务工厂创建JbdcTransaction事务tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);//创建CachingExecutor执行器final Executor executor = configuration.newExecutor(tx, execType);//创建DefaultSqlSession会话,属性主要包括Configuration、Executor对象return new DefaultSqlSession(configuration, executor, autoCommit);} catch (Exception e) {closeTransaction(tx); // may have fetched a connection so lets call close()throw ExceptionFactory.wrapException("Error opening session.  Cause: " + e, e);} finally {ErrorContext.instance().reset();}
}

可以看到,上面的方法返回了一个DefaultSqlSession对象。看看它里面有些什么。

  • 【a】environment

环境对象,这是在mybatis-config.xml中配置的,主要用来生成TransactionFactory,而TransactionFactory是用来生成Transaction事务的。

  • 【b】Transaction

事务对象,我们都知道sql执行时涉及到事务,需要提交或回滚什么的。创建Transaction事务对象,需要传入我们在mybatis-config.xml中配置的数据源信息(从environment获取,因为之前解析XML的时候保存进去了),通过这些参数,transactionFactory就可以生成Transaction。

  • 【c】executor

执行器,非常重要,它是一个接口,是Mybatis的核心执行器,相当于jdbc中的statement,发送sql语句并执行。executor的继承图如下所示,默认使用的是SimpleExecutor简单类型的执行器。

第四步:UserMapper userMapper = sqlSession.getMapper(UserMapper.class)

这一步主要的工作就是通过sqlSession获取接口,然后通过JDK动态代理获取对Mapper接口进行代理,从而能够实现对mapper相关方法的拦截调用。

我们看一下getMapper()方法:

//org.apache.ibatis.session.defaults.DefaultSqlSession#getMapper
public <T> T getMapper(Class<T> type) {return configuration.<T>getMapper(type, this);
}

可以看到,实际上调用的是Configuration类的getMapper方法:

public <T> T getMapper(Class<T> type, SqlSession sqlSession) {//通过mapperRegistry对象中,拿type去找  return mapperRegistry.getMapper(type, sqlSession);
}

我们来看一下mapperRegistry, mapperRegistry 其实是Configuration类的一个成员属性,

protected final MapperRegistry mapperRegistry = new MapperRegistry(this);

它里面是一个knownMappers的HashMap,用于存储配置文件中的mappers标签的每个mapper接口。

knownMappers是个key-value形式的缓存,key是mapper接口的class对象,value是MapperProxyFactory代理工厂,用来创建MapperProxy代理类。至于我们的mapper接口是如何加入knownMappers中的,本篇文章我们暂且不去深入分析,后面单独分析,这里只需要知道在解析XML的时候,就已经添加进去了即可。

继续追踪MapperRegistry的getMapper方法:

//org.apache.ibatis.binding.MapperRegistry#getMapper
public <T> T getMapper(Class<T> type, SqlSession sqlSession) {//从knownMappers的Map里根据mapper接口取出对应的MapperProxyFactory工厂类final MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory<T>) knownMappers.get(type);if (mapperProxyFactory == null) {throw new BindingException("Type " + type + " is not known to the MapperRegistry.");}try {return mapperProxyFactory.newInstance(sqlSession);} catch (Exception e) {throw new BindingException("Error getting mapper instance. Cause: " + e, e);}
}//org.apache.ibatis.binding.MapperProxyFactory#newInstance(org.apache.ibatis.session.SqlSession)
public T newInstance(SqlSession sqlSession) {//构造一个MapperProxy代理final MapperProxy<T> mapperProxy = new MapperProxy<>(sqlSession, mapperInterface, methodCache);//动态代理获取Mapper接口实例return newInstance(mapperProxy);
}//实际上底层就是使用的JDK动态代理,对Mapper接口进行代理
//熟悉JDK动态代理的同学看到这个代码应该不陌生
protected T newInstance(MapperProxy<T> mapperProxy) {return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy);
}

到这里,我们的Mapper就算获取到了。

第五步:User user = userMapper.getById(1L)

执行mapper接口方法,前面讲到,获取mapper对象时,是会获取到一个MapperProxyFactory工厂类,并创建一个MapperProxy代理类,因为MapperProxy实现了InvocationHandler接口,所以在执行Mapper接口的方法时,会调用MapperProxy的invoke方法。

那么我们就从MapperProxy类的invoke方法开始看:

public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {try {if (Object.class.equals(method.getDeclaringClass())) {return method.invoke(this, args);} else if (isDefaultMethod(method)) {return invokeDefaultMethod(proxy, method, args);}} catch (Throwable t) {throw ExceptionUtil.unwrapThrowable(t);}//实际执行的还是MapperMethod的execute方法final MapperMethod mapperMethod = cachedMapperMethod(method);return mapperMethod.execute(sqlSession, args);
}

可以看到,invoke方法实际执行的还是MapperMethod的execute方法:

//org.apache.ibatis.binding.MapperMethod#execute
public Object execute(SqlSession sqlSession, Object[] args) {Object result;//判断当前SQL的类型,是插入还是删除、还是更新、查询等switch (command.getType()) {case INSERT: {Object param = method.convertArgsToSqlCommandParam(args);result = rowCountResult(sqlSession.insert(command.getName(), param));break;}case UPDATE: {Object param = method.convertArgsToSqlCommandParam(args);result = rowCountResult(sqlSession.update(command.getName(), param));break;}case DELETE: {Object param = method.convertArgsToSqlCommandParam(args);result = rowCountResult(sqlSession.delete(command.getName(), param));break;}case SELECT:if (method.returnsVoid() && method.hasResultHandler()) {executeWithResultHandler(sqlSession, args);result = null;} else if (method.returnsMany()) {result = executeForMany(sqlSession, args);} else if (method.returnsMap()) {result = executeForMap(sqlSession, args);} else if (method.returnsCursor()) {result = executeForCursor(sqlSession, args);} else {//将参数转换为SQL语句中参数Object param = method.convertArgsToSqlCommandParam(args);result = sqlSession.selectOne(command.getName(), param);if (method.returnsOptional() &&(result == null || !method.getReturnType().equals(result.getClass()))) {result = Optional.ofNullable(result);}}break;case FLUSH:result = sqlSession.flushStatements();break;default:throw new BindingException("Unknown execution method for: " + command.getName());}if (result == null && method.getReturnType().isPrimitive() && !method.returnsVoid()) {throw new BindingException("Mapper method '" + command.getName()+ " attempted to return null from a method with a primitive return type (" + method.getReturnType() + ").");}return result;
}

执行sqlSession.selectOne()方法,selectOne()又是调用的selectList方法,里面通过executor执行器的query方法执行查询。

public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) {try {//从configuration中获取到MappedStatement对象,里面包含有sql语句等信息MappedStatement ms = configuration.getMappedStatement(statement);//执行器调用query方法进行查询return executor.query(ms, wrapCollection(parameter), rowBounds, Executor.NO_RESULT_HANDLER);} catch (Exception e) {throw ExceptionFactory.wrapException("Error querying database.  Cause: " + e, e);} finally {ErrorContext.instance().reset();}
}

然后执行CachingExecutor#query方法,具体源码如下:

//org.apache.ibatis.executor.CachingExecutor#query(org.apache.ibatis.mapping.MappedStatement, java.lang.Object, org.apache.ibatis.session.RowBounds, org.apache.ibatis.session.ResultHandler)
public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {//获取到BoundSql对象BoundSql boundSql = ms.getBoundSql(parameterObject);//生成缓存key//例如:1067688838:2080526037:com.wsh.mybatis.mybatisdemo.mapper.UserMapper.getById:0:2147483647:select * from user where id = ?:1:developmentCacheKey key = createCacheKey(ms, parameterObject, rowBounds, boundSql);return query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
}

继续追踪源码:

//org.apache.ibatis.executor.CachingExecutor#query(org.apache.ibatis.mapping.MappedStatement, java.lang.Object, org.apache.ibatis.session.RowBounds, org.apache.ibatis.session.ResultHandler, org.apache.ibatis.cache.CacheKey, org.apache.ibatis.mapping.BoundSql)
public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql)throws SQLException {//先从二级缓存中查找      Cache cache = ms.getCache();if (cache != null) {flushCacheIfRequired(ms);if (ms.isUseCache() && resultHandler == null) {ensureNoOutParams(ms, boundSql);@SuppressWarnings("unchecked")List<E> list = (List<E>) tcm.getObject(cache, key);if (list == null) {//二级缓存中没有的话,执行delegate.query()进行普通查询list = delegate.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);tcm.putObject(cache, key, list); // issue #578 and #116}return list;}}return delegate.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
}

CachingExecutor的query方法,先从二级缓存中获取,如果二级缓存中没有数据,执行delegate(BaseExecutor)的query方法 ,也就是一级缓存localCache,如果一级缓存没有数据,则走queryFromDatabase方法查数据库。

public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {ErrorContext.instance().resource(ms.getResource()).activity("executing a query").object(ms.getId());if (closed) {throw new ExecutorException("Executor was closed.");}if (queryStack == 0 && ms.isFlushCacheRequired()) {clearLocalCache();}List<E> list;try {queryStack++;//判断一级缓存中是否存在此数据,没有的话,就从数据库查询list = resultHandler == null ? (List<E>) 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 (DeferredLoad deferredLoad : deferredLoads) {deferredLoad.load();}// issue #601deferredLoads.clear();if (configuration.getLocalCacheScope() == LocalCacheScope.STATEMENT) {// issue #482clearLocalCache();}}return list;
}//org.apache.ibatis.executor.BaseExecutor#queryFromDatabase
private <E> List<E> queryFromDatabase(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {List<E> list;//放入占位符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);}return list;
}

queryFromDatabase()的作用:从数据库查到数据,放入到一级缓存,即localCache中。 关于一级缓存、二级缓存后面专门用一篇文章详细总结,这里先知道有它的存在即可。

继续追踪doQuery()方法源码:

public <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {Statement stmt = null;try {//获取全局配置对象Configuration configuration = ms.getConfiguration();//通过configuration对象创建StatementHandlerStatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql);//SQL查询参数的设置stmt = prepareStatement(handler, ms.getStatementLog());//SQL查询操作和结果集的封装return handler.query(stmt, resultHandler);} finally {closeStatement(stmt);}
}

追踪handler.query()方法,我们来到PreparedStatementHandler的query方法:

//org.apache.ibatis.executor.statement.PreparedStatementHandler#query
public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {//将statement包装为预编译Statement -> PreparedStatementPreparedStatement ps = (PreparedStatement) statement;//通过PreparedStatement的execute()执行SQL查询操作ps.execute();//通过resultSetHandler对查询结果进行解析,并将解析的结果返回出去return resultSetHandler.handleResultSets(ps);
}

这里暂且先跳过resultSetHandler.handleResultSets具体是如何处理结果集的,后面分一篇文章介绍。到这里,Mybatis整体的执行流程就差不多了,因为我们首先先了解其大概的执行过程,并没有其中很多的细节进行详细的分析,先整体,然后再对各个细节逐个去抠。

四、执行流程图

下面我们简单画一下Mybatis整体的执行过程图:

五、总结

前面我们详细分析了Mybatis执行的大致流程,当然其中很多细节都还没有详细介绍,比如如何解析XML的、Mapper接口又是如何加入knownMappers的,参数又是在哪里替换成?占位符的,一级缓存、二级缓存如何实现的等等。由于是Mybatis源码的第一篇,先对整体有个大概的认识之后,我们再去对各个细节一点一点分析,这样印象可能会深一点,不会说一上来直接就抠细节,自己都绕懵了,看完这么长的方法调用栈,都不记得自己从哪个方法点进来的。因此,接下来会分好几篇文章对一些关键流程做详细介绍。

鉴于笔者水平有限,如果文章有什么错误或者需要补充的,希望小伙伴们指出来,希望这篇文章对大家有帮助。

MyBatis源码阅读(二) --- 执行流程分析相关推荐

  1. mybatis源码阅读(二):mybatis初始化上

    转载自  mybatis源码阅读(二):mybatis初始化上 1.初始化入口 //Mybatis 通过SqlSessionFactory获取SqlSession, 然后才能通过SqlSession与 ...

  2. 3.MyBatis源码解析-CRUD执行流程--阿呆中二

    CRUD执行流程 MyBatis CRUD执行流程 与我联系 MyBatis 本文是对mybatis 3.x源码深度解析与最佳实践学习的总结,包括XML文件解析流程.SqlSession构建流程.CR ...

  3. Mybatis源码阅读(二)

    本文主要介绍Java中,不使用XML和使用XML构建SqlSessionFactory,通过SqlSessionFactory 中获取SqlSession的方法,使用SqlsessionManager ...

  4. Mybatis源码阅读之二——模板方法模式与Executor

    [系列目录] Mybatis源码阅读之一--工厂模式与SqlSessionFactory 文章目录 一. 模板方法模式 二. 同步回调与匿名函数 三. Executor BaseExecutor与其子 ...

  5. mybatis源码阅读(四):mapper(dao)实例化

    转载自   mybatis源码阅读(四):mapper(dao)实例化 在开始分析之前,先来了解一下这个模块中的核心组件之间的关系,如图: 1.MapperRegistry&MapperPro ...

  6. mybatis源码考究二(sqlsession线程安全和缓存失效)

    mybatis源码考究二 1.mybatis整合spring解决sqlsession线程安全问题 2.mybatis整合spring一级缓存失效问题 mybatis结合spring使用 1.项目依赖 ...

  7. Android 源码 Camera2 预览流程分析四

    <Android 源码 Camera2 预览流程分析二>中进行了流启动,这是调用 QCamera3Channel start() 方法实现的,对应于 HAL_PIXEL_FORMAT_YC ...

  8. Mybatis 源码阅读环境搭建

    Mybatis源码阅读环境搭建 前言 一.下载mybatis的源码 二.编译源码 三.创建测试项目 前言     mybatis源码阅读环境搭建还是比较简单的,接下来我们讲解一下如何搭建该源码阅读环境 ...

  9. mybatis源码阅读(八) ---Interceptor了解一下

    转载自  mybatis源码阅读(八) ---Interceptor了解一下 1 Intercetor MyBatis 允许你在已映射语句执行过程中的某一点进行拦截调用.默认情况下,MyBatis允许 ...

  10. mybatis源码阅读(七) ---ResultSetHandler了解一下

    转载自  mybatis源码阅读(七) ---ResultSetHandler了解一下 1.MetaObject MetaObject用于反射创建对象.反射从对象中获取属性值.反射给对象设置属性值,参 ...

最新文章

  1. SQL Server2008及以上 表分区操作详解
  2. java多线程的同步
  3. Java基础学习(1)
  4. python爬虫项目教程_Python 爬虫速成教程,还有35个实战项目送给你
  5. (五)Docker查看容器ip及指定固定IP
  6. 一个典型的SAP Commerce extension目录结构
  7. oracle之set运算符和练习
  8. sleep(),wait(),yield(),notify()
  9. Vuex状态管理方式
  10. linux libz-dev,Linux-based Development
  11. 运行超时:您的程序未能在规定时间内运行结束,请检查是否循环有错或算法复杂度过大。
  12. 【指数编制系列六】景气指数编制方法
  13. 苹果cms v10官网源码下载
  14. Chrome FeHelper 插件下载地址
  15. PS 做动态图视频教程
  16. 转:IV值和WOE值的理解
  17. blast的替代品,使用hmmer寻找同源序列
  18. CE修改器学习历程之下载、安装和汉化
  19. addon@Node.js之新手上路
  20. AVI文件结构分析及举例验证

热门文章

  1. html响应式布局media,JS中使用media实现响应式布局_飛雲_前端开发者
  2. C/C++[codeup 2043]小白鼠排队
  3. python函数 range()和arange()
  4. CSDN-markdown编辑器语法说明
  5. K8S学习笔记之为什么需要Pod?
  6. TF-IDF来源及理论推导 熵推导出
  7. 2017.7.31.生活记录
  8. Lagrangian乘子法 对偶问题 KKT条件 Slater条件 与凸优化
  9. 贝叶斯公式和共轭分布
  10. 笔记本计算机外壳的制作过程,笔记本电脑外壳选材不可小视