一、准备工作


MyBatis 工作流程:应用程序首先加载 mybatis-config.xml 配置文件,并根据配置文件的内容创建 SqlSessionFactory 对象;然后,通过 SqlSessionFactory 对象创建 SqlSession 对象,SqlSession 接口中定义了执行 SQL 语句所需要的各种方法。之后,通过 SqlSession 对象执行映射配置文件中定义的 SQL 语句,完成相应的数据操作。最后通过 SqlSession 对象提交事务,关闭 SqlSession 对象,整个过程具体实现如下:就按照下面的流程进行源码分析

 1 public void test01() throws IOException {
2 // 1、获取sqlSessionFactory对象
3 String resource = "mybatis-config.xml";
4 InputStream inputStream = Resources.getResourceAsStream(resource);
5 SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
6 // 2、获取sqlSession对象
7 SqlSession openSession = sqlSessionFactory.openSession();
8 try {
9 // 3、获取接口的实现类对象
10 //会为接口自动的创建一个代理对象,代理对象去执行增删改查方法
11 EmployeeMapper mapper = openSession.getMapper(EmployeeMapper.class);
12 Employee employee = mapper.getEmpById(1);
13 System.out.println(mapper);
14 System.out.println(employee);
15 } finally {
16 openSession.close();
17 }
18
19 }

二、SqlSessionFactory 对象的初始化过程


SqlSessionFactory  对象的初始化序列图如下:

【1】 从这行代码入手,首先创建了一个 SqlSessionFactoryBuilder 工厂,这是一个建造者模式的设计思想,由 builder 建造者来创建 SqlSessionFactory 工厂。

SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);

【2】 然后调用 SqlSessionFactoryBuilder 中的 build方法传递一个InputStream 输入流,Inputstream 输入流就是配置文件 mybatis-config.xml,SqlSessionFactoryBuilder 根据传入的 InputStream 输入流和environmentproperties属性创建一个 XMLConfigBuilder对象(解析器)。SqlSessionFactoryBuilder 对象调用 XMLConfigBuilder 的 parse()方法。配置文件中的内容被解析后封装到 Configuration 对象中。同时解析 mapper 标签。返回一个 XPathParser 类型的实例。

 1 public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {
2 //创建文件的解析器 XPathParse
3 XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);
4 return build(parser.parse());
5 }
6
7 //进入上述的内部方法 parser.parse()
8 public Configuration parse() {
9 parsed = true;
10 //获取 configuration 标签,全局最大的标签
11 parseConfiguration(parser.evalNode("/configuration"));
12 return configuration;
13 }
14
15 //进入上述的内部方法 parseConfiguration(parser.evalNode("/configuration"));
16 // 此方法中的方法内部都是将解析的标签内容 set 进 configuration 对象中
17 private void parseConfiguration(XNode root) {
18 Properties settings = settingsAsPropertiess(root.evalNode("settings"));
19 propertiesElement(root.evalNode("properties"));
20 //......
21
22 //将 setting 标签中的全局变量都set 到configuration 对象中
23 settingsElement(settings);
24
25 environmentsElement(root.evalNode("environments"));
26 databaseIdProviderElement(root.evalNode("databaseIdProvider"));
27 typeHandlerElement(root.evalNode("typeHandlers"));
28
29 //解析mapper 标签:很重要
30 mapperElement(root.evalNode("mappers"));
31 }
32
33 //列举上述 settingsElement(settings); 内部的源码
34 private void settingsElement(Properties props) throws Exception {
35 configuration.setAutoMappingBehavior(AutoMappingBehavior.valueOf(props.getProperty("autoMappingBehavior", "PARTIAL")));
36
37 //false 表示默认值
38 configuration.setUseGeneratedKeys(booleanValueOf(props.getProperty("useGeneratedKeys"), false));
39 configuration.setDefaultExecutorType(ExecutorType.valueOf(props.getProperty("defaultExecutorType", "SIMPLE")));
40 configuration.setDefaultStatementTimeout(integerValueOf(props.getProperty("defaultStatementTimeout"), null));
41 //......
42 }

【3】解析 mapper.xml 文件,也是通过 XPathParser 类型的解析器,具体源码如下:将结果保存在 Configuration 中

 1 /**    配置信息如下:
2 <mappers>
3 <mapper resource="EmployeeMapper.xml" />
4 </mappers>
5 **/
6 private void mapperElement(XNode parent) throws Exception {
7 if (parent != null) {
8 for (XNode child : parent.getChildren()) {
9 String resource = child.getStringAttribute("resource");
10 String url = child.getStringAttribute("url");
11 String mapperClass = child.getStringAttribute("class");
12 if (resource != null && url == null && mapperClass == null) {
13 ErrorContext.instance().resource(resource);
14 //将配置文件转化为流文件,mapper.xml 文件
15 InputStream inputStream = Resources.getResourceAsStream(resource);
16 XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, resource, configuration.getSqlFragments());
17 //解析 mapper.xml 文件
18 mapperParser.parse();
19 }
20 }
21 }
22 }
23
24 //进入 mapperParser.parse(); 方法
25 public void parse() {
26 if (!configuration.isResourceLoaded(resource)) {
27 //解析 mapper标签中的内容
28 configurationElement(parser.evalNode("/mapper"));
29 configuration.addLoadedResource(resource);
30 bindMapperForNamespace();
31 }
32
33 parsePendingResultMaps();
34 parsePendingChacheRefs();
35 parsePendingStatements();
36 }
37
38 //进入 configurationElement(parser.evalNode("/mapper")); 方法
39 //解析mapper中的标签内容
40 private void configurationElement(XNode context) {
41 String namespace = context.getStringAttribute("namespace");
42 builderAssistant.setCurrentNamespace(namespace);
43 cacheRefElement(context.evalNode("cache-ref"));
44 cacheElement(context.evalNode("cache"));
45 parameterMapElement(context.evalNodes("/mapper/parameterMap"));
46 resultMapElements(context.evalNodes("/mapper/resultMap"));
47 sqlElement(context.evalNodes("/mapper/sql"));
48 //解析增删改查标签
49 buildStatementFromContext(context.evalNodes("select|insert|update|delete"));
50 }

【4】解析 mapper 中的增删改查标签:拿到所有标签能写的属性,将详细信息保存进 MappedStatement(是一个Map,Key 存放的是 命令空间+id) 中,一个 MappedStatement 就代表一个增删改查标签的详细信息。

 1 // 进入增删改查标签的源码 buildStatementFromContext
2 private void buildStatementFromContext(List<XNode> list, String requiredDatabaseId) {
3 for (XNode context : list) {
4 //获取到 解析增删改查标签的解析器 statementParser
5 final XMLStatementBuilder statementParser = new XMLStatementBuilder(configuration, builderAssistant, context, requiredDatabaseId);
6 //解析标签中的内容
7 statementParser.parseStatementNode();
8 }
9 }
10 public void parseStatementNode() {
11 String id = context.getStringAttribute("id");
12 String databaseId = context.getStringAttribute("databaseId");
13
14 if (!databaseIdMatchesCurrent(id, databaseId, this.requiredDatabaseId)) {
15 return;
16 }
17
18 Integer fetchSize = context.getIntAttribute("fetchSize");
19 Integer timeout = context.getIntAttribute("timeout");
20 String parameterMap = context.getStringAttribute("parameterMap");
21 String parameterType = context.getStringAttribute("parameterType");
22 //....
23
24 //将解析的结果进行封装
25 builderAssistant.addMappedStatement(id, sqlSource, statementType, sqlCommandType,
26 fetchSize, timeout, parameterMap, parameterTypeClass, resultMap, resultTypeClass,
27 resultSetTypeEnum, flushCache, useCache, resultOrdered,
28 keyGenerator, keyProperty, keyColumn, databaseId, langDriver, resultSets);
29 }
30 }
31
32 //进入 builderAssistant.addMappedStatement 方法
33 public MappedStatement addMappedStatement(
34 //......
35 MappedStatement statement = statementBuilder.build();
36 //并将结果添加到 Configuration 中,MappedStatement 是一个 map 对象
37 configuration.addMappedStatement(statement);
38 return statement;
39 }

【5】Configuration 对象保存了所有配置文件的详细信息,包括全局配置文件和 sql 映射文件。
Configuration  中包含的 MappedStatement 对象信息:
Configuration  中包含的 MapperRegistory 对象,其中的 knownMappers 包含的是接口的代理对象。

【6】DefaultSqlSessionFactory传入上面返回的 Configuration 对象,通过 build 的方法创建一个 DefaultSqlSessionFactory 包含配置了全局信息的 Configuration;

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

DefaultSqlSessionFactory : SqlSessionFactory 的默认实现类,是真正生产会话的工厂类,这个类的实例的生命周期是全局的,它只会在首次调用时生成一个实例(单例模式),就一直存在直到服务器关闭。

三、SqlSession 对象的初始化过程


SqlSession 对象的初始化序列图如下:会创建 Executor

SqlSession 对象是 MyBatis 中最重要的一个对象,这个接口能够让你执行命令,获取映射,管理事务。SqlSession 中定义了一系列模版方法,让你能够执行简单的 CRUD 操作,也可以通过 getMapper 获取 Mapper 层,执行自定义 SQL 语句,因为 SqlSession 在执行 SQL 语句之前是需要先开启一个会话,涉及到事务操作,所以还会有 commitrollbackclose 等方法。这也是模版设计模式的一种应用。

【1】通过 DefaultSqlSessionFactory 的 openSession 方法获取 SqlSession;

SqlSession openSession = sqlSessionFactory.openSession();

【2】进入 openSession方法:需要传入 Executor 的类型,在配置文件中可以指定,默认是 simple;

1 public SqlSession openSession() {
2 //configuration.getDefaultExecutorType() 执行器Executor 的默认类型 simple(总共三种类型 reuse、simple、batch)
3 return openSessionFromDataSource(configuration.getDefaultExecutorType(), null, false);
4 }

【3】进入 openSessionFromDataSource 方法:重点是创建了 Executor执行器对象和 SqlSession 对象,传入事务和类型。

 1 private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) {
2 Transaction tx = null;
3 try {
4 final Environment environment = configuration.getEnvironment();
5 final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment);
6
7 //创建事务
8 tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);
9
10 //四大组件之一 Executor
11 final Executor executor = configuration.newExecutor(tx, execType);
12 return new DefaultSqlSession(configuration, executor, autoCommit);
13 }
14 }

【4】进入创建 Executor 的方法:configuration.newExecutor(tx, execType); Exeutor 是用来做增删改查的,里面包含了 query等方法;

 1 public Executor newExecutor(Transaction transaction, ExecutorType executorType) {
2 executorType = executorType == null ? defaultExecutorType : executorType;
3 executorType = executorType == null ? ExecutorType.SIMPLE : executorType;
4 Executor executor;
5 //根据类型创建对应的 Executor
6 if (ExecutorType.BATCH == executorType) {
7 executor = new BatchExecutor(this, transaction);
8 } else if (ExecutorType.REUSE == executorType) {
9 executor = new ReuseExecutor(this, transaction);
10 } else {
11 //默认的 executor 是 SimpleExecutor
12 executor = new SimpleExecutor(this, transaction);
13 }
14 //是否开启的二级缓存,好处是查询之前会先从缓存中获取
15 if (cacheEnabled) {
16 executor = new CachingExecutor(executor);
17 }
18
19 //每一个 executor 都需要通过 拦截器进行重新包括(插件使用。。。。重要)
20 executor = (Executor) interceptorChain.pluginAll(executor);
21 return executor;
22 }

【5】展示 Executor 对象中的信息:

 1 public interface Executor {
2 //增删改查操作
3 int update(MappedStatement ms, Object parameter) throws SQLException;
4 <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey cacheKey, BoundSql boundSql) throws SQLException;
5 List<BatchResult> flushStatements() throws SQLException;
6
7 //事务相关操作
8 void commit(boolean required) throws SQLException;
9 void rollback(boolean required) throws SQLException;
10 //......
11 }

【6】创建 DefaultSqlSession 并将上述创建的 Executor传入,并包含 Configuration;

return new DefaultSqlSession(configuration, executor, autoCommit);

四、getMapper 对象的初始化过程


getMapper 对象的初始化序列图如下:  会创建 MapperProxy代理对象;MapperProxy 是 Mapper 映射 SQL 语句的关键对象,我们写的 Dao 层或者 Mapper 层都是通过 MapperProxy来和对应的 SQL语句进行绑定的。下面我们就来解释一下绑定过程。
 

【1】通过 getMapper 获取代理对象;

EmployeeMapper mapper = openSession.getMapper(EmployeeMapper.class);

【2】进入 getMapper 方法内部,发现调用的是 Configuration 对象的 getMapper 方法,并将 SqlSession 作为参数传入;

public <T> T getMapper(Class<T> type) {
return configuration.<T>getMapper(type, this);
}

【3】进入 Configuration 对象的 getMapper 方法,通过调用 mapperRegistry 对象的 getMapper 方法;

public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
return mapperRegistry.getMapper(type, sqlSession);
}

【4】进入 MapperRegistry 的 getMapper 方法,根据接口类型获取其代理对象工厂 mapperProxyFactory;

1 @SuppressWarnings("unchecked")
2 public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
3 //根据接口类型获取其代理对象
4 final MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory<T>) knownMappers.get(type);
5 // 会创建一个 mapperProxy
6 return mapperProxyFactory.newInstance(sqlSession);
7 }

【5】进入 mapperProxyFactory.newInstance(sqlSession); 方法,其主要是创建 MapperProxy 代理对象;

1 public T newInstance(SqlSession sqlSession) {
2 //获取代理对象
3 final MapperProxy<T> mapperProxy = new MapperProxy<T>(sqlSession, mapperInterface, methodCache);
4 return newInstance(mapperProxy);
5 }

【6】进入 MapperProxy 代理对象,主要包含如下三个属性:并且实现了 InvocationHandler,是JDK 动态代理的一部分;

1 public class MapperProxy<T> implements InvocationHandler, Serializable {
2
3 private static final long serialVersionUID = -6424540398559729838L;
4 //主要包含 sqlSession 、 mapper接口和 其中的方法
5 private final SqlSession sqlSession;
6 private final Class<T> mapperInterface;
7 private final Map<Method, MapperMethod> methodCache;
8 //......
9 }

【7】我们进入 5 中的 newInstance 方法:使用 JDK 实现动态代理 Proxy.newProxyInstance(....),创建 MapperProxy 代理对象。内部包含 SqlSession 进行增删改查。也就是说,MyBatis 中 Mapper 和 SQL 语句的绑定正是通过动态代理来完成的。通过动态代理,我们就可以方便的在 Dao 层或者 Mapper 层定义接口,实现自定义的增删改查操作了。

1 @SuppressWarnings("unchecked")
2 //JDK 动态代理
3 protected T newInstance(MapperProxy<T> mapperProxy) {
4 return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy);
5 }

【8】代理对象展示:

五、通过代理对象调用目标方法的初始化过程


mapper.getEmpById(1) 查询执行的序列图如下:

【1】进入代理类调用目标方法入口:

Employee employee = mapper.getEmpById(1);

【2】首先会进入代理方法的 invoke 方法:

 1 //调用代理对象的入口
2 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
3 //首先判断调用的是不是 Object 对象的方法
4 if (Object.class.equals(method.getDeclaringClass())) {
5 try {
6 return method.invoke(this, args);
7 } catch (Throwable t) {
8 throw ExceptionUtil.unwrapThrowable(t);
9 }
10 }
11 //将当前方法包装成 MapperMethod
12 final MapperMethod mapperMethod = cachedMapperMethod(method);
13 return mapperMethod.execute(sqlSession, args);
14 }

【3】进入  mapperMethod.execute(sqlSession, args); 目标方法执行,传入 sqlSession 和参数;

 1 public Object execute(SqlSession sqlSession, Object[] args) {
2 Object result;
3 switch (command.getType()) {
4 case INSERT: {
5 Object param = method.convertArgsToSqlCommandParam(args);
6 result = rowCountResult(sqlSession.insert(command.getName(), param));
7 break;
8 }
9 case UPDATE: {
10 Object param = method.convertArgsToSqlCommandParam(args);
11 result = rowCountResult(sqlSession.update(command.getName(), param));
12 break;
13 }
14 case DELETE: {
15 Object param = method.convertArgsToSqlCommandParam(args);
16 result = rowCountResult(sqlSession.delete(command.getName(), param));
17 break;
18 }
19 case SELECT:
20 //当前方法无返回值时执行
21 if (method.returnsVoid() && method.hasResultHandler()) {
22 executeWithResultHandler(sqlSession, args);
23 result = null;
24 //返回多个执行方法
25 } else if (method.returnsMany()) {
26 result = executeForMany(sqlSession, args);
27 //返回Map执行
28 } else if (method.returnsMap()) {
29 result = executeForMap(sqlSession, args);
30 //返回游标执行
31 } else if (method.returnsCursor()) {
32 result = executeForCursor(sqlSession, args);
33 } else {
34 //其余执行此方法
35 //该方法是将当前参数:如果是1个参数则返回,如果多个则组装成 map 返回
36 Object param = method.convertArgsToSqlCommandParam(args);
37 result = sqlSession.selectOne(command.getName(), param);
38 }
39 break;
40 return result;
41 }

【4】调用单个查询 sqlSession.selectOne 方法:调用查询多个的方法,返回 list的第一个元素即可;statement 就是当前sql 的唯一表示,对应 xml 中的 namespace;

 1 public <T> T selectOne(String statement, Object parameter) {
2 List<T> list = this.<T>selectList(statement, parameter);
3 if (list.size() == 1) {
4 return list.get(0);
5 } else if (list.size() > 1) {
6 throw new TooManyResultsException("Expected one result (or null) to be returned by selectOne(), but found: " + list.size());
7 } else {
8 return null;
9 }
10 }

【5】进入 selectList(statement, parameter); 方法:会获取 mapperedStatement 对象,同时调用 Executor 的query 方法执行

1 @Override
2 public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) {
3 try {
4 //首先获取 mapperedStatement 对象
5 MappedStatement ms = configuration.getMappedStatement(statement);
6 //通过 executor 的query 方法执行
7 return executor.query(ms, wrapCollection(parameter), rowBounds, Executor.NO_RESULT_HANDLER);
8 }
9 }

【6】获取 mapperedStatement 通过 BoundSql 它代表 sql 语句的详细信息:通过query 方法进行查询;

1 public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {
2 BoundSql boundSql = ms.getBoundSql(parameter);
3 CacheKey key = createCacheKey(ms, parameter, rowBounds, boundSql);
4 return query(ms, parameter, rowBounds, resultHandler, key, boundSql);
5 }

【7】进入上述的 query 方法,先调用 cacheExecutor 缓存,如果不存在则调用全局配置 Executor ,我们这里使用的默认 simpleExecutor 方法:先会调用二级缓存,再调用一级缓存;如果不存在则执行 queryFromDatabase 查出以后也会保存在以及缓存中;

 1 public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {
2 //先调用本地缓存
3 list = resultHandler == null ? (List<E>) localCache.getObject(key) : null;
4 if (list != null) {
5 handleLocallyCachedOutputParameters(ms, key, parameter, boundSql);
6 } else {
7 //如果缓存中不存在,则调用 queryFromDatabase
8 list = queryFromDatabase(ms, parameter, rowBounds, resultHandler, key, boundSql);
9 }
10 return list;
11 }

【8】进入 queryFromDatabase 方法:调用 BaseExecutor 对象中的 doQuery方法   ms 当前增删改查标签的详细信息

1 private <E> List<E> queryFromDatabase(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {
2 try {
3 //调用 BaseExecutor对象中的 doQuery方法 ms 当前增删改查标签的详细信息
4 list = doQuery(ms, parameter, rowBounds, resultHandler, boundSql);
5 }
6 return list;
7 }

【9】进入 SimpleExecutor 对象的 doQuery 方法:底层包含了 Statement 对象,表示对 JDBC 的封装,同时创建了四大对象之一 StatementHandler 作用:创建 Statement 对象;在创建 StatementHandler 的时候构造器里面会同时创建 ParameterHandler和 ResultSetHandler。

 1 public <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {
2 //原生 JDBC 的 Statement对象
3 Statement stmt = null;
4 try {
5 Configuration configuration = ms.getConfiguration();
6 //创建四个对象之一 StatementHandler
7 StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql);
8 //从 StatementHandler 中获取 Statement 对象
9 stmt = prepareStatement(handler, ms.getStatementLog());
10 return handler.<E>query(stmt, resultHandler);
11 }
12 }

【10】进入 newStatementHandler 方法,查看 StatementHandler 对象的创建;

1 public StatementHandler newStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
2 StatementHandler statementHandler = new RoutingStatementHandler(executor, mappedStatement, parameterObject, rowBounds, resultHandler, boundSql);
3 //使用拦截器链包装 StatementHandler
4 statementHandler = (StatementHandler) interceptorChain.pluginAll(statementHandler);
5 return statementHandler;
6 }

【11】进入 RoutingStatementHandler 方法:我们可以在查询标签中设置 StatementType 就根据如下方法创建我们需要的 Statement 对象;默认使用的是 PREPARED(预编译的形式),会创建一个 PreparedStatementHandler;

 1 public RoutingStatementHandler(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
2
3 switch (ms.getStatementType()) {
4 case STATEMENT:
5 delegate = new SimpleStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql);
6 break;
7 case PREPARED:
8 delegate = new PreparedStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql);
9 break;
10 case CALLABLE:
11 delegate = new CallableStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql);
12 break;
13 default:
14 throw new ExecutorException("Unknown statement type: " + ms.getStatementType());
15 }
16 }

【12】通过 StatementHandler 创建 Statement 对象,进入 stmt = prepareStatement(handler, ms.getStatementLog()); 方法:预编译 sql 产生的 PreparedStatement 对象,调用四大对象之一 ParameterHandler 进行参数的预编译;也是 JDBC 原生的对象;

 1 private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException {
2 Statement stmt;
3 //创建一个链接
4 Connection connection = getConnection(statementLog);
5 //进行预编译
6 stmt = handler.prepare(connection, transaction.getTimeout());
7 //进行参数预编译
8 handler.parameterize(stmt);
9 return stmt;
10 }

【13】进入创建 ParameterHandler 的方法:newParameterHandler(mappedStatement, parameterObject, boundSql); 同时也调用了 pluginAll 方法,通过拦截器链对 ParameterHandler  进行封装;

1 public ParameterHandler newParameterHandler(MappedStatement mappedStatement, Object parameterObject, BoundSql boundSql) {
2 ParameterHandler parameterHandler = mappedStatement.getLang().createParameterHandler(mappedStatement, parameterObject, boundSql);
3 //也调用了拦截器链
4 parameterHandler = (ParameterHandler) interceptorChain.pluginAll(parameterHandler);
5 return parameterHandler;
6 }

【14】进入创建 ResultSetHandler 的方法:configuration.newResultSetHandler(executor, mappedStatement, rowBounds, parameterHandler, resultHandler, boundSql); 同时也调用了 pluginAll 方法,通过拦截器链对 ParameterHandler  进行封装;也输入四大对象之一;用于处理查询得到的数据;

1 public ResultSetHandler newResultSetHandler(Executor executor, MappedStatement mappedStatement, RowBounds rowBounds, ParameterHandler parameterHandler,
2 ResultHandler resultHandler, BoundSql boundSql) {
3 ResultSetHandler resultSetHandler = new DefaultResultSetHandler(executor, mappedStatement, parameterHandler, resultHandler, boundSql, rowBounds);
4 //也调用了拦截器链
5 resultSetHandler = (ResultSetHandler) interceptorChain.pluginAll(resultSetHandler);
6 return resultSetHandler;
7 }

【15】最终返回执行的结果和关闭连接,以及查询流程总结:
    ■ StatementHandler处理 sql语句预编译,设置参数等相关工作;
    ■ ParameterHandler设置预编译参数用的;
    ■ResultHandler处理结果集;
    ■TypeHandler在整个过程中,进行数据库类型和 javaBean类型的映射;

六、总结


【1】根据配置文件(全局,sql映射)初始化出 Configuration 对象;
【2】创建一个 DefaultSqlSession对象,他里面包含 Configuration以及 Executor(根据全局配置文件中的 defaultExecutorType创建出对应的 Executor);
【3】DefaultSqlSession.getMapper():拿到 Mapper接口对应的 MapperProxy;
【4】MapperProxy里面有(DefaultSqlSession);
【5】执行增删改查方法:
    1)、代理对象调用 DefaultSqlSession 的增删改查(最终调用 Executor 的增删改查);
    2)、会创建一个 StatementHandler 对象。(同时也会创建出 ParameterHandler 和 ResultSetHandler)
    3)、调用 StatementHandler预编译参数以及设置参数值,使用 ParameterHandler 来给 sql设置参数;
    4)、调用 StatementHandler 的增删改查方法;
    5)、ResultSetHandler 封装结果;
注意:四大对象每个创建的时候都有一个 interceptorChain.pluginAll(parameterHandler);后面的插件应用中使用;

----关注公众号,获取更多内容----

 

MyBatis 源码的更多相关文章

  1. MyBatis源码分析(一)开篇

    源码学习的好处不用多说,Mybatis源码量少.逻辑简单,将写个系列文章来学习. SqlSession Mybatis的使用入口位于org.apache.ibatis.session包中的SqlSes ...

  2. MyBatis源码分析-MyBatis初始化流程

    MyBatis 是支持定制化 SQL.存储过程以及高级映射的优秀的持久层框架.MyBatis 避免了几乎所有的 JDBC 代码和手动设置参数以及获取结果集.MyBatis 可以对配置和原生Map使用简 ...

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

    MyBatis 是支持定制化 SQL.存储过程以及高级映射的优秀的持久层框架.MyBatis 避免了几乎所有的 JDBC 代码和手动设置参数以及获取结果集.MyBatis 可以对配置和原生Map使用简 ...

  4. MyBatis源码分析-IDEA新建MyBatis源码工程

    MyBatis 是支持定制化 SQL.存储过程以及高级映射的优秀的持久层框架.MyBatis 避免了几乎所有的 JDBC 代码和手动设置参数以及获取结果集.MyBatis 可以对配置和原生Map使用简 ...

  5. MyBatis源码分析(5)——内置DataSource实现

    @(MyBatis)[DataSource] MyBatis源码分析(5)--内置DataSource实现 MyBatis内置了两个DataSource的实现:UnpooledDataSource,该 ...

  6. MyBatis源码分析(4)—— Cache构建以及应用

    @(MyBatis)[Cache] MyBatis源码分析--Cache构建以及应用 SqlSession使用缓存流程 如果开启了二级缓存,而Executor会使用CachingExecutor来装饰 ...

  7. MyBatis源码分析(3)—— Cache接口以及实现

    @(MyBatis)[Cache] MyBatis源码分析--Cache接口以及实现 Cache接口 MyBatis中的Cache以SPI实现,给需要集成其它Cache或者自定义Cache提供了接口. ...

  8. MyBatis源码分析(2)—— Plugin原理

    @(MyBatis)[Plugin] MyBatis源码分析--Plugin原理 Plugin原理 Plugin的实现采用了Java的动态代理,应用了责任链设计模式 InterceptorChain ...

  9. 深入浅出Mybatis系列(五)---TypeHandler简介及配置(mybatis源码篇)

    上篇文章<深入浅出Mybatis系列(四)---配置详解之typeAliases别名(mybatis源码篇)>为大家介绍了mybatis中别名的使用,以及其源码.本篇将为大家介绍TypeH ...

  10. 深入浅出Mybatis系列(四)---配置详解之typeAliases别名(mybatis源码篇)

    上篇文章<深入浅出Mybatis系列(三)---配置详解之properties与environments(mybatis源码篇)> 介绍了properties与environments, ...

随机推荐

  1. iframe 嵌套别的系统不显示,父窗口不响应

    显示不全,没有登录界面,检查了代码渲染了,只是display:none :换了网址 ,别的都可以,只有这个不行 搜索 复制

  2. phpmyadmin scripts/setup.php 反序列化漏洞(WooYun-2016-199433)(Kali)

    ​ phpmyadmin 2.x版本中存在一处反序列化漏洞,通过该漏洞,攻击者可以读取任意文件或执行任意代码. 通过vulhub靶场进行复现操作 1.首先搭建靶场环境(采用Kali) cd vulhu ...

  3. ssh免密码登录服务器

    A机为本地主机(即用于控制其他主机的机器) B机为远程主机(即被控制的机器server)B机:192.168.3.145 假如A机无密码登录B机器     A机上的命令: 1,ssh-keygen - ...

  4. JDBC与JPA--初学JPA

      最近工程中用到JPA,头一次接触,踩了不少坑.刚好复习到JDBC,发现JPA用起来真是很简单.就对比一下这两者的区别 总结:JDBC是更接近数据库SQL的抽象,使用时依然使用的是SQL.优点是靠近 ...

  5. PLC入门笔记12

    1.边沿应用 (1)边沿开关 (2)上升沿触发 下降沿触发 (3) MOVP K4M0 D0 传送比较 movp (=mov) 脉冲型指令 前面条件成立只能执行一次,仅执行一次扫描周期 不带P MOV ...

  6. solidworks卸载方法,怎么完全彻底卸载删除清理干净solidworks各种残留注册表和文件?

    标题:solidworks重新安装方法,利用卸载清理工具完全彻底排查删除干净solidworks各种残留注册表和文件.solidworks显示已安装或者报错出现提示安装未完成某些产品无法安装的问题,怎 ...

  7. echarts 图表动态刷新数据

    需求:每次重新加载数据,图表柱状图从零开始加载 用 myChart.clear(); 这个方式解决. 在setOption 之前用 示例: myChart.clear(); myChart.setOp ...

  8. K8S的基础概念

    一.Kubernetes介绍 1.什么是Kubernetes? Kubernetes(通常称为K8s,K8s是将8个字母"ubernete"替换为"8"的缩写) ...

  9. C# Http 服务器get pos 请求 获取头信息 iOS 客户端联调

    using System; using System.Collections.Generic; using System.IO; using System.Net; using System.Text ...

  10. AJAX请求的基本操作

    1 const { request, response } = require('express'); 2 //引入express 3 const express = require('express ...