MyBatis从入门到精通—源码剖析之Configuration、SqlSession、Executor、StatementHandler细节

网友投稿 502 2022-10-30

MyBatis从入门到精通—源码剖析之Configuration、SqlSession、Executor、StatementHandler细节

MyBatis从入门到精通—源码剖析之Configuration、SqlSession、Executor、StatementHandler细节

源码剖析-初始化

//获得核心配置文件 InputStream resourceAsStream = Resources.getResourceAsStream("sqlMapConfig.xml"); //获得session工厂对象,正是初始化⼯作的开始。 SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);

源码分析

//1.开始进入的build方法 public SqlSessionFactory build(InputStream inputStream) { //调用了build的重载方法 return build(inputStream, null, null); } //2.build的重载方法 public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) { try { // XMLConfigBuilder是XML配置构建器,专⻔解析mybatis配置⽂件的类 XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties); //这⾥⼜调⽤了⼀个重载⽅法。parser.parse()的返回值是Configuration对象 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. } } }

MyBatis在初始化的时候,会将MyBatis的配置信息全部加载到内存中,使用org.apache.ibatis.session.Configuration 实例来维护。下⾯进⼊对配置⽂件解析部分:

Configuration

⾸先对Configuration对象进⾏介绍:

Configuration对象的结构和xml配置⽂件的对象⼏乎相同。回顾⼀下xml中的配置标签有哪些:properties (属性),settings (设置),typeAliases (类型别名),typeHandlers (类型处理器),objectFactory (对象⼯⼚),mappers (映射器)等 Configuration也有对应的对象属性来封装它们也就是说,初始化配置⽂件信息的本质就是创建Configuration对象,将解析的xml数据封装到Configuration内部属性中。

继续跟踪上述的parse()方法

/** *3.解析XML封装到Configuration对象中 **/ public Configuration parse() { //若已解析,抛出BuilderException异常 if (parsed) { throw new BuilderException("Each XMLConfigBuilder can only be used once."); } //标记已解析 parsed = true; // 解析 XML中的 configuration 节点 parseConfiguration(parser.evalNode("/configuration")); return configuration; } /** *4.解析XML **/ private void parseConfiguration(XNode root) { try { //issue #117 read properties first // 解析 标签 propertiesElement(root.evalNode("properties")); // 解析 标签 Properties settings = settingsAsProperties(root.evalNode("settings")); //加载⾃定义的VFS实现类 loadCustomVfs(settings); // 解析 标签 typeAliasesElement(root.evalNode("typeAliases")); // 解析 标签 pluginElement(root.evalNode("plugins")); // 解析 标签 objectFactoryElement(root.evalNode("objectFactory")); // 解析 标签 objectWrapperFactoryElement(root.evalNode("objectWrapperFactory")); // 解析 标签 reflectorFactoryElement(root.evalNode("reflectorFactory")); // 赋值 ⾄ Configuration 属性 settingsElement(settings); // read it after objectFactory and objectWrapperFactory issue #631 // 解析 标签 environmentsElement(root.evalNode("environments")); // 解析 标签 databaseIdProviderElement(root.evalNode("databaseIdProvider")); // 解析 标签 typeHandlerElement(root.evalNode("typeHandlers")); // 解析 标签 mapperElement(root.evalNode("mappers")); } catch (Exception e) { throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e); } }

MappedStatement

作⽤:MappedStatement与Mapper配置⽂件中的⼀个select/update/insert/delete节点相对应。mapper中配置的标签都被封装到了此对象中,主要⽤途是描述⼀条SQL语句。初始化过程:回顾刚开 始介绍的加载配置⽂件的过程中,会对mybatis-config.xm l中的各个标签都进⾏解析,其中有mappers 标签⽤来引⼊mapper.xml⽂件或者配置mapper接⼝的⽬录。

这样的⼀个select标签会在初始化配置⽂件时被解析封装成⼀个MappedStatement对象,然后存储在Configuration对象的mappedStatements属性中,mappedStatements 是⼀个HashMap,存储时key=全限定类名+⽅法名,value =对应的MappedStatement对象。在Configuration中对应的属性为

protected final Map mappedStatements = new StrictMap("Mapped Statements collection");

mappers在 XMLConfigBuilder 中的处理:

private void parseConfiguration(XNode root) { try { //省略其他标签的处理 mapperElement(root.evalNode("mappers")); } catch (Exception e) { throw new BuilderException("Error parsing SQL Mapper Configuration.Cause:" + e, e); } }

到此对xml配置⽂件的解析就结束了,回到步骤2.中调⽤的重载build⽅法

// 5.调⽤的重载⽅法 public SqlSessionFactory build(Configuration config) { //创建了 DefaultSqlSessionFactory 对象,传⼊ Configuration 对象。 return new DefaultSqlSessionFactory(config); }

源码剖析-执行SQL流程

SqlSession

SqlSession是⼀个接⼝,它有两个实现类:DefaultSqlSession (默认)和SqlSessionManager (弃⽤,不做介绍)SqlSession是MyBatis中⽤于和数据库交互的顶层类,通常将它与ThreadLocal绑定,⼀个会话使⽤⼀个SqlSession,并且在使⽤完毕后需要close。

public class DefaultSqlSession implements SqlSession { private final Configuration configuration; private final Executor executor; }

SqlSession中的两个最重要的参数,configuration与初始化时的相同,Executor为执⾏器Executor:Executor也是⼀个接⼝,他有三个常⽤的实现类:BatchExecutor (重⽤语句并执⾏批量更新)ReuseExecutor (重⽤预处理语句 prepared statements)SimpleExecutor (普通的执⾏器,默认)继续分析,初始化完毕后,我们就要执⾏SQL 了

//获得session回话对象 SqlSession sqlSession = sqlSessionFactory.openSession(); //执行操作 参数:namespace+id List userList = sqlSession.selectList("userMapper.findAll");

获得 sqlSession

SqlSessionFactory

public class DefaultSqlSessionFactory implements SqlSessionFactory { //6.进入openSession方法 @Override public SqlSession openSession() { //getDefaultExecutorType()传递的是SimpleExecutor return openSessionFromDataSource(configuration.getDefaultExecutorType(), null, false); } //7.进入openSessionFromDataSource,从数据源创建会话 //ExecutorType为Executor的类型,TransactionIsolationLevel为事务隔离级别,autoCommit是否开启事务 //openSession的多个重载⽅法可以指定获得的SqlSession的Executor类型和事务的处理 private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) { Transaction tx = null; try { final Environment environment = configuration.getEnvironment(); final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment); tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit); //根据参数创建指定类型的Executor final Executor executor = configuration.newExecutor(tx, execType); //返回DefaultSqlSession 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(); } } }

执⾏ sqlsession 中的 api

DefaultSqlSession

public class DefaultSqlSession implements SqlSession { //8.进⼊selectList⽅法,多个重载⽅法。 public List < E > selectList(String statement) { return this.selectList(statement, null); } public List < E > selectList(String statement, Object parameter) { return this.selectList(statement, parameter, RowBounds.DEFAULT); } public List < E > selectList(String statement, Object parameter, RowBounds rowBounds) { try { //根据传⼊的全限定名+⽅法名从映射的Map中取出MappedStatement对象 MappedStatement ms = configuration.getMappedStatement(statement); //调⽤Executor中的⽅法处理 //RowBounds是⽤来逻辑分⻚ // wrapCollection(parameter)是⽤来装饰集合或者数组参数 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(); } } }

源码剖析-Executor

继续源码中的步骤,进⼊executor.query()

BaseExecutor

public abstract class BaseExecutor implements Executor { //9.此⽅法在SimpleExecutor的⽗类BaseExecutor中实现 @Override public List query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException { //根据传⼊的参数动态获得SQL语句,最后返回⽤BoundSql对象表示 BoundSql boundSql = ms.getBoundSql(parameter); //为本次查询创建缓存的Key CacheKey key = createCacheKey(ms, parameter, rowBounds, boundSql); //进入重载方法 return query(ms, parameter, rowBounds, resultHandler, key, boundSql); } //10.进⼊query的重载⽅法中 @SuppressWarnings("unchecked") @Override public List 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 list; try { queryStack++; 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 (DeferredLoad deferredLoad : deferredLoads) { deferredLoad.load(); } // issue #601 deferredLoads.clear(); if (configuration.getLocalCacheScope() == LocalCacheScope.STATEMENT) { // issue #482 clearLocalCache(); } } return list; } //11.从数据库查询 private List queryFromDatabase(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException { List 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; } }

在SimpleExecutor实现对应的查询

SimpleExecutor

public class SimpleExecutor extends BaseExecutor { //12.SimpleExecutor中实现⽗类的doQuery抽象⽅法 @Override public List doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException { Statement stmt = null; try { Configuration configuration = ms.getConfiguration(); //传⼊参数创建StatementHanlder对象来执⾏查询 StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql); //创建jdbc中的statement对象 stmt = prepareStatement(handler, ms.getStatementLog()); // StatementHandler 进⾏处理 return handler.query(stmt, resultHandler); } finally { closeStatement(stmt); } } //13.创建Statement的⽅法 private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException { Statement stmt; //这条代码中的getConnection⽅法经过重重调⽤最后会调⽤openConnection⽅法,从连接池中获得连接。 Connection connection = getConnection(statementLog); stmt = handler.prepare(connection, transaction.getTimeout()); //进⼊到 StatementHandler 的 parameterize(statement)⽅法 handler.parameterize(stmt); return stmt; } }

public class JdbcTransaction implements Transaction { //14.从连接池中获取连接的方法 protected void openConnection() throws SQLException { if (log.isDebugEnabled()) { log.debug("Opening JDBC Connection"); } //从连接池获得连接 connection = dataSource.getConnection(); if (level != null) { connection.setTransactionIsolation(level.getLevel()); } setDesiredAutoCommit(autoCommmit); } }

上述的Executor.query()⽅法⼏经转折,最后会创建⼀个StatementHandler对象,然后将必要的参数传递给StatementHandler,使⽤StatementHandler来完成对数据库的查询,最终返回List结果集。从上⾯的代码中我们可以看出,Executor的功能和作用是:

根据传递的参数,完成SQL语句的动态解析,⽣成BoundSql对象,供StatementHandler使⽤; 为查询创建缓存,以提⾼性能 创建JDBC的Statement连接对象,传递给 StatementHandler对象,返回List查询结果。 源码剖析-StatementHandler StatementHandler对象主要完成两个⼯作:

对于JDBC的PreparedStatement类型的对象,创建的过程中,我们使⽤的是SQL语句字符串会包含若⼲个?占位符,我们其后再对占位符进⾏设值。StatementHandler通过parameterize(statement)⽅法对 Statement 进⾏设值; StatementHandler 通过 List query(Statement statement, ResultHandler resultHandler)⽅法来完成执⾏Statement,和将Statement对象返回的resultSet封装成List;

进⼊到 StatementHandler 的 parameterize(statement)⽅法的实现:

public interface StatementHandler { void parameterize(Statement statement) throws SQLException; } //15.StatementHandler 的 parameterize(statement)⽅法实现 public class PreparedStatementHandler extends BaseStatementHandler { @Override public void parameterize(Statement statement) throws SQLException { //使⽤ParameterHandler对象来完成对Statement的设值 parameterHandler.setParameters((PreparedStatement) statement); } }

ParameterHandler

public class DefaultParameterHandler implements ParameterHandler { /** 16.ParameterHandler 类的 setParameters(PreparedStatement ps) 实现 * 对某⼀个Statement进⾏设置参数 * */ @Override public void setParameters(PreparedStatement ps) { ErrorContext.instance().activity("setting parameters").object(mappedStatement.getParameterMap().getId()); List parameterMappings = boundSql.getParameterMappings(); if (parameterMappings != null) { for (int i = 0; i < parameterMappings.size(); i++) { ParameterMapping parameterMapping = parameterMappings.get(i); if (parameterMapping.getMode() != ParameterMode.OUT) { Object value; String propertyName = parameterMapping.getProperty(); if (boundSql.hasAdditionalParameter(propertyName)) { // issue #448 ask first for additional params value = boundSql.getAdditionalParameter(propertyName); } else if (parameterObject == null) { value = null; } else if (typeHandlerRegistry.hasTypeHandler(parameterObject.getClass())) { value = parameterObject; } else { MetaObject metaObject = configuration.newMetaObject(parameterObject); value = metaObject.getValue(propertyName); } // 每⼀个 Mapping都有⼀个 TypeHandler,根据 TypeHandler 来对preparedStatement 进 ⾏设置参数 TypeHandler typeHandler = parameterMapping.getTypeHandler(); JdbcType jdbcType = parameterMapping.getJdbcType(); if (value == null && jdbcType == null) { jdbcType = configuration.getJdbcTypeForNull(); } try { //设置参数 typeHandler.setParameter(ps, i + 1, value, jdbcType); } catch (TypeException e) { throw new TypeException("Could not set parameters for mapping: " + parameterMapping + ". Cause: " + e, e); } catch (SQLException e) { throw new TypeException("Could not set parameters for mapping: " + parameterMapping + ". Cause: " + e, e); } } } } } }

从上述的代码可以看到,StatementHandler的parameterize(Statement)⽅法调⽤了ParameterHandler的setParameters(statement)⽅法,ParameterHandler的setParameters(Statement )⽅法负责根据我们输⼊的参数,对statement对象的?占位符处进⾏赋值。进⼊到StatementHandler 的 List query(Statement statement, ResultHandler resultHandler)⽅法的实现:

StatementHandler

public interface StatementHandler { List query(Statement statement, ResultHandler resultHandler) throws SQLException; } public class PreparedStatementHandler extends BaseStatementHandler { //17.进入query方法 @Override public List query(Statement statement, ResultHandler resultHandler) throws SQLException { // 1.调⽤preparedStatemnt.execute()⽅法,然后将resultSet交给ResultSetHandler处理 PreparedStatement ps = (PreparedStatement) statement; ps.execute(); //2.使⽤ ResultHandler 来处理 ResultSet return resultSetHandler. handleResultSets(ps); } }

从上述代码我们可以看出,StatementHandler 的List query(Statement statement, ResultHandler resultHandler)⽅法的实现,是调⽤了 ResultSetHandler 的 handleResultSets(Statement)⽅法。ResultSetHandler 的 handleResultSets(Statement)⽅法会将 Statement 语句执⾏后⽣成的 resultSet结 果集转换成List结果集。

ResultSetHandler

public interface ResultSetHandler { List handleResultSets(Statement stmt) throws SQLException; } public class DefaultResultSetHandler implements ResultSetHandler { //18.进入handleResultSets方法 @Override public List handleResultSets(Statement stmt) throws SQLException { ErrorContext.instance().activity("handling results").object(mappedStatement.getId()); //多ResultSet的结果集合,每个ResultSet对应⼀个Object对象。⽽实际上,每 个 Object 是List 对象。 //在不考虑存储过程的多ResultSet的情况,普通的查询,实际就⼀个ResultSet,也 就是说,multipleResults最多就⼀个元素。 final List multipleResults = new ArrayList(); int resultSetCount = 0; //获得⾸个ResultSet对象,并封装成ResultSetWrapper对象 ResultSetWrapper rsw = getFirstResultSet(stmt); //获得ResultMap数组 //在不考虑存储过程的多ResultSet的情况,普通的查询,实际就⼀个ResultSet,也 就是说,resultMaps就⼀个元素。 List resultMaps = mappedStatement.getResultMaps(); int resultMapCount = resultMaps.size(); // 校验 validateResultMapsCount(rsw, resultMapCount); while (rsw != null && resultMapCount > resultSetCount) { //获得ResultMap对象 ResultMap resultMap = resultMaps.get(resultSetCount); //处理ResultSet,将结果添加到multipleResults中 handleResultSet(rsw, resultMap, multipleResults, null); //获得下⼀个ResultSet对象,并封装成ResultSetWrapper对象 rsw = getNextResultSet(stmt); //清理 cleanUpAfterHandlingResultSet(); // resultSetCount ++ resultSetCount++; } //因为'mappedStatement.resultSets'只在存储过程中使⽤,暂时不考虑 String[] resultSets = mappedStatement.getResultSets(); if (resultSets != null) { while (rsw != null && resultSetCount < resultSets.length) { ResultMapping parentMapping = nextResultMaps.get(resultSets[resultSetCount]); if (parentMapping != null) { String nestedResultMapId = parentMapping.getNestedResultMapId(); ResultMap resultMap = configuration.getResultMap(nestedResultMapId); handleResultSet(rsw, resultMap, null, parentMapping); } rsw = getNextResultSet(stmt); cleanUpAfterHandlingResultSet(); resultSetCount++; } } //如果是multipleResults单元素,则取⾸元素返回 return collapseSingleResultList(multipleResults); } }

版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。

上一篇:Flink1.7从安装到体验
下一篇:MyBatis从入门到精通—MyBatis架构原理
相关文章

 发表评论

暂时没有评论,来抢沙发吧~