diff --git "a/second/week_08/87/01-\345\210\235\345\247\213\345\214\226sqlSessionFactory.jpg" "b/second/week_08/87/01-\345\210\235\345\247\213\345\214\226sqlSessionFactory.jpg"
new file mode 100644
index 0000000000000000000000000000000000000000..b907fa539d53878dcfb61e7cc26b482f40978073
Binary files /dev/null and "b/second/week_08/87/01-\345\210\235\345\247\213\345\214\226sqlSessionFactory.jpg" differ
diff --git a/second/week_08/87/02-openSession.jpg b/second/week_08/87/02-openSession.jpg
new file mode 100644
index 0000000000000000000000000000000000000000..3fe27f09285a780d8bb6647066f2773c65e5bd5c
Binary files /dev/null and b/second/week_08/87/02-openSession.jpg differ
diff --git a/second/week_08/87/03-getMapper.jpg b/second/week_08/87/03-getMapper.jpg
new file mode 100644
index 0000000000000000000000000000000000000000..3ad81154a84e13493e11194c043a33e9473f3c2c
Binary files /dev/null and b/second/week_08/87/03-getMapper.jpg differ
diff --git "a/second/week_08/87/04-\346\237\245\350\257\242\346\265\201\347\250\213.jpg" "b/second/week_08/87/04-\346\237\245\350\257\242\346\265\201\347\250\213.jpg"
new file mode 100644
index 0000000000000000000000000000000000000000..684e8ea2ba2baeb2fb4ac84f95249354a02d2d8f
Binary files /dev/null and "b/second/week_08/87/04-\346\237\245\350\257\242\346\265\201\347\250\213.jpg" differ
diff --git "a/second/week_08/87/Mybatis-01\346\265\201\347\250\213.txt" "b/second/week_08/87/Mybatis-01\346\265\201\347\250\213.txt"
new file mode 100644
index 0000000000000000000000000000000000000000..33a30c42663b975a9fa228ace10dd4ba81f3fd23
--- /dev/null
+++ "b/second/week_08/87/Mybatis-01\346\265\201\347\250\213.txt"
@@ -0,0 +1,82 @@
+Main#test
+ Resources#getResourceAsStream: 获取资源,传入资源地址返回InputStream
+ ClassLoaderWrapper#getResourceAsStream
+
+ SqlSessionFactoryBuilder#new: 构建者模式,构建的是SqlSession工厂
+ SqlSessionFactoryBuilder#build: 传入InputStream,返回SqlSessionFactory
+ XMLConfigBuilder#new: 传入InputStream,返回XML配置文件构建者(解析器)
+ XPathParser#new: XPath的解析器
+ XMLMapperEntityResolver#new: 做DTD校验
+ commonConstructor: 调用通用构造方法
+ XPathFactory#newInstance#newXPath: XPath 通过工厂类来创建
+ createDocument:
+ DocumentBuilderFactory#newInstance: DOM解析工厂
+ DocumentBuilderFactory#newDocumentBuilder: 创建文档构建者
+ super#Configuration#new: 将Configuration初始化(进行了别名的注册,初始化操作),super调用是因为这些基本信息都存储在父类(BaseBuilder)中.
+ ErrorContext#resource: 错误上下文设置
+ Configuration#setVariables: 将Properties设置到Configuration中
+ parser,enviroment: 这些成员变量传入。
+
+ XMLConfigBuilder#parse
+ XPathParser#evalNode: 获取结点/configuration
+ evalNode
+ evaluate: 根据表达式获取相对应的Node
+ XNode#new: 将Node封装入XNode中
+ parseConfiguration: 解析配置文件Xnode (如下相关拆解到另一部分分析,其中就是将解析出的数据封装入configruation中)
+ 1. propertiesElement: 解析properties
+ 2. typeAliasesElement: 解析typeAliases
+ 3. pluginElement: 解析plugins
+ 4. objectFactoryElement: 解析objectFactory
+ 5. objectWrapperFactoryElement: 解析objectWrapperFactory 对象包装工厂
+ 6. settingsElement: 解析settings
+ 7. environmentsElement: 解析environments
+ 8. databaseIdProviderElement: 解析 databaseIdProvider
+ 9. typeHandlerElement: 解析typeHandlers
+ 10. mapperElement: 解析mappers
+
+ build
+ DefaultSqlSessionFactory#new: 通过传入配置Configuration 默认SqlSession工厂构造器
+
+ SqlSessionFactory#openSession: 通过openSession 获取SqlSession
+ openSessionFromDataSource: 从数据源对象中获取Session会话
+ getTransactionFactoryFromEnvironment: 从Environment中获取事务工厂
+ TransactionFactory#newTransaction: 工厂生成事务
+ Configuration#newExecutor: 获取执行器
+ $ChooseBy@ExecutorType {BatchExecutor,ReuseExecutor,SimpleExecutor}
+ $WhetherBy@cacheEnabled {CachingExecutor}
+ InterceptorChain#pluginAll: 将所有的插件装载入
+ DefaultSqlSession#new
+
+ SqlSession#selectOne: 会话调用查询语句
+ selectList
+ Configuration#getMappedStatement: 根据statementId找到对应的mappedStatement
+ Executor#query: 用执行器来查询结果
+ MappedStatement#getBoundSql: 得到绑定的sql
+ createCacheKey: 创建缓存key
+ query: 查询
+ PerpetualCache#getObject: 根据cacheKey从localCache去查询
+ $WhetherBy@localCache!=null {handleLocallyCachedOutputParameters: 从缓存中查询localCache ,queryFromDatabase: 从数据库查询}
+ 1. handleLocallyCachedOutputParameters: 若查到缓存,只有statementType时Callable时才能处理
+
+ 2. queryFromDatabase: 从数据库查
+ PerpetualCache#putObject: 先放入占位
+ doQuery: 真正的查询操作 有如下几种执行器
+ 1. SimpleExecutor
+ Configuration#newStatementHandler: 获取StatementHandler
+ prepareStatement: 准备语句
+ StatementHandler.query: 做查询
+ closeStatement: 关闭语句
+ 2. BatchExecutor
+ flushStatements: 做刷新
+ Configuration#newStatementHandler: 获取StatementHandler
+ getConnection
+ StatementHandler#prepare
+ StatementHandler#parameterize
+ closeStatement
+ 3. ReuseExecutor
+ Configuration#newStatementHandler
+ prepareStatement
+ PrepareStatment#query
+ PerepetualCache#removeObject
+ PerepetualCache#putObject
+
diff --git "a/second/week_08/87/Mybatis-\347\256\200\345\215\225\346\265\201\347\250\213.jpg" "b/second/week_08/87/Mybatis-\347\256\200\345\215\225\346\265\201\347\250\213.jpg"
new file mode 100644
index 0000000000000000000000000000000000000000..42ab07c8336297103583b7b230e721a12a43aa35
Binary files /dev/null and "b/second/week_08/87/Mybatis-\347\256\200\345\215\225\346\265\201\347\250\213.jpg" differ
diff --git a/second/week_08/87/Mybatis.md b/second/week_08/87/Mybatis.md
new file mode 100644
index 0000000000000000000000000000000000000000..40751060a4731e8ec809bf648123c588e9806a1b
--- /dev/null
+++ b/second/week_08/87/Mybatis.md
@@ -0,0 +1,843 @@
+# Mybatis
+
+## 解析配置文件阶段
+
+### 解析入口最终获取`sqlSessionFactory`
+
+> 如下是我们常用的Mybatis SqlSession的获取方式。
+>
+> 最终通过`sqlSessionFactory`来获取sqlSession
+
+```java
+// 1. 先构造SqlSessionFactory 通过SqlSessionFactoryBuilder#build
+SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
+// 通过Resources#getResourceAsStream
+InputStream inputStream = Resources.getResourceAsStream("config.xml");
+// 通过建造者来建造 sqlSessionFactory
+sqlSessionFactory = sqlSessionFactoryBuilder.build(inputStream);
+// 我们通过sqlSessionFactory来获取sqlSession
+```
+
+#### Resources#getResourceAsStream
+
+```java
+InputStream getResourceAsStream(String resource, ClassLoader[] classLoader) {
+ for (ClassLoader cl : classLoader) {
+ if (null != cl) {
+
+ // try to find the resource as passed
+ InputStream returnValue = cl.getResourceAsStream(resource);
+
+ // now, some class loaders want this leading "/", so we'll add it and try again if we didn't find the resource
+ if (null == returnValue) {
+ returnValue = cl.getResourceAsStream("/" + resource);
+ }
+
+ if (null != returnValue) {
+ return returnValue;
+ }
+ }
+ }
+ return null;
+}
+```
+
+...
+
+#### sqlSessionFactoryBuilder#build
+
+##### SqlSessionFactoryBuilder 参数
+
+无参数
+
+##### SqlSessionFactoryBuilder#build
+
+```java
+public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {
+ try {
+ // 创建配置文件解析器
+ XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);
+ // parser解析出对应的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.
+ }
+ }
+}
+// 最终返回的SqlSessionFactory
+public SqlSessionFactory build(Configuration config) {
+ return new DefaultSqlSessionFactory(config);
+}
+```
+
+##### XMLConfigBuilder#new
+
+```java
+public abstract class BaseBuilder {
+ protected final Configuration configuration;
+ protected final TypeAliasRegistry typeAliasRegistry;
+ protected final TypeHandlerRegistry typeHandlerRegistry;
+
+ public BaseBuilder(Configuration configuration) {
+ this.configuration = configuration;
+ this.typeAliasRegistry = this.configuration.getTypeAliasRegistry();
+ this.typeHandlerRegistry = this.configuration.getTypeHandlerRegistry();
+ }
+ // ...
+}
+```
+
+
+
+```java
+public class XMLConfigBuilder extends BaseBuilder {
+
+ private boolean parsed;
+ private final XPathParser parser;
+ private String environment;
+ private final ReflectorFactory localReflectorFactory = new DefaultReflectorFactory();
+
+ // ...
+}
+```
+
+##### XMLConfigBuilder#parse
+
+```java
+public Configuration parse() {
+ if (parsed) {
+ throw new BuilderException("Each XMLConfigBuilder can only be used once.");
+ }
+ parsed = true;
+ // 解析配置 取出/configuration下的结点
+ parseConfiguration(parser.evalNode("/configuration"));
+ return configuration;
+}
+
+private void parseConfiguration(XNode root) {
+ try {
+ //issue #117 read properties first
+ //
+ propertiesElement(root.evalNode("properties"));
+ Properties settings = settingsAsProperties(root.evalNode("settings"));
+ loadCustomVfs(settings);
+ typeAliasesElement(root.evalNode("typeAliases"));
+ pluginElement(root.evalNode("plugins"));
+ objectFactoryElement(root.evalNode("objectFactory"));
+ objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));
+ reflectorFactoryElement(root.evalNode("reflectorFactory"));
+ 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);
+ }
+}
+```
+
+
+
+```java
+private void propertiesElement(XNode context) throws Exception {
+ if (context != null) {
+ // 先获取子属性` `
+ Properties defaults = context.getChildrenAsProperties();
+ // 获取资源位置 resource 、 url
+ String resource = context.getStringAttribute("resource");
+ String url = context.getStringAttribute("url");
+ if (resource != null && url != null) {
+ throw new BuilderException("The properties element cannot specify both a URL and a resource based property file reference. Please specify one or the other.");
+ }
+ if (resource != null) {
+ // 获取resource中的作为Properties
+ defaults.putAll(Resources.getResourceAsProperties(resource));
+ } else if (url != null) {
+ // 通过url加载并解析属性文件
+ defaults.putAll(Resources.getUrlAsProperties(url));
+ }
+ Properties vars = configuration.getVariables();
+ if (vars != null) {
+ defaults.putAll(vars);
+ }
+ // 将解析的属性填充入解析器
+ parser.setVariables(defaults);
+ // 将解析的属性填充入配置中
+ configuration.setVariables(defaults);
+ }
+}
+```
+
+###### XNode#getChildrenAsProperties
+
+```java
+public Properties getChildrenAsProperties() {
+ Properties properties = new Properties();
+ for (XNode child : getChildren()) {
+ // 找到所有的孩子结点 获取其"name" 和 "value"
+ String name = child.getStringAttribute("name");
+ String value = child.getStringAttribute("value");
+ if (name != null && value != null) {
+ properties.setProperty(name, value);
+ }
+ }
+ return properties;
+}
+
+public List getChildren() {
+ // 最终封装成XNode
+ List children = new ArrayList();
+ // 调用Node#getChildNodes获取子元素们
+ NodeList nodeList = node.getChildNodes();
+ if (nodeList != null) {
+ for (int i = 0, n = nodeList.getLength(); i < n; i++) {
+ Node node = nodeList.item(i);
+ if (node.getNodeType() == Node.ELEMENT_NODE) {
+ children.add(new XNode(xpathParser, node, variables));
+ }
+ }
+ }
+ return children;
+}
+```
+
+```java
+public class XNode {
+
+ private final Node node;
+ private final String name;
+ private final String body;
+ private final Properties attributes;
+ private final Properties variables;
+ private final XPathParser xpathParser;
+
+ public XNode(XPathParser xpathParser, Node node, Properties variables) {
+ this.xpathParser = xpathParser;
+ this.node = node;
+ this.name = node.getNodeName();
+ this.variables = variables;
+ this.attributes = parseAttributes(node);
+ this.body = parseBody(node);
+ }
+ //...
+}
+```
+
+
+
+```java
+public XNode evalNode(String expression) {
+ return xpathParser.evalNode(node, expression);
+}
+```
+
+
+
+```java
+public XNode evalNode(Object root, String expression) {
+ Node node = (Node) evaluate(expression, root, XPathConstants.NODE);
+ if (node == null) {
+ return null;
+ }
+ return new XNode(this, node, variables);
+}
+```
+
+
+
+
+
+##### DefaultSqlSessionFactory#new
+
+> 接口是SqlSessionFactory的
+
+```java
+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();
+}
+```
+
+```java
+public class DefaultSqlSessionFactory implements SqlSessionFactory {
+
+ private final Configuration configuration;
+
+ public DefaultSqlSessionFactory(Configuration configuration) {
+ this.configuration = configuration;
+ }
+
+ //...
+}
+```
+
+
+
+这个就能将最终需要的**sqlSessionFactory**的
+
+
+
+### DefaultSqlSessionFactory#openSession
+
+#### openSessionFromDataSource
+
+```java
+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);
+ //生成一个执行器(事务包含在执行器里)
+ 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();
+ }
+}
+```
+
+> 那么这里环境中的datasource是哪里来的呢?
+
+##### XMLConfigBuilder#parseConfiguration
+
+`environmentsElement(root.evalNode("environments"));`
+
+```java
+private void environmentsElement(XNode context) throws Exception {
+ if (context != null) {
+ if (environment == null) {
+ environment = context.getStringAttribute("default");
+ }
+ for (XNode child : context.getChildren()) {
+ String id = child.getStringAttribute("id");
+//循环比较id是否就是指定的environment
+ if (isSpecifiedEnvironment(id)) {
+ //7.1事务管理器
+ TransactionFactory txFactory = transactionManagerElement(child.evalNode("transactionManager"));
+ //7.2数据源 获取工厂类
+ DataSourceFactory dsFactory = dataSourceElement(child.evalNode("dataSource"));
+ DataSource dataSource = dsFactory.getDataSource();
+ // 通过建造者模型来创建environment
+ Environment.Builder environmentBuilder = new Environment.Builder(id)
+ .transactionFactory(txFactory)
+ .dataSource(dataSource);
+ // 将environment设置入configuration
+ configuration.setEnvironment(environmentBuilder.build());
+ }
+ }
+ }
+}
+```
+
+
+
+```java
+//产生执行器
+public Executor newExecutor(Transaction transaction, ExecutorType executorType) {
+ executorType = executorType == null ? defaultExecutorType : executorType;
+ //这句再做一下保护,囧,防止粗心大意的人将defaultExecutorType设成null?
+ executorType = executorType == null ? ExecutorType.SIMPLE : executorType;
+ Executor executor;
+ //然后就是简单的3个分支,产生3种执行器BatchExecutor/ReuseExecutor/SimpleExecutor
+ if (ExecutorType.BATCH == executorType) {
+ executor = new BatchExecutor(this, transaction);
+ } else if (ExecutorType.REUSE == executorType) {
+ executor = new ReuseExecutor(this, transaction);
+ } else {
+ executor = new SimpleExecutor(this, transaction);
+ }
+ //如果要求缓存,生成另一种CachingExecutor(默认就是有缓存),装饰者模式,所以默认都是返回CachingExecutor
+ if (cacheEnabled) {
+ executor = new CachingExecutor(executor);
+ }
+ //此处调用插件,通过插件可以改变Executor行为
+ executor = (Executor) interceptorChain.pluginAll(executor);
+ return executor;
+}
+```
+
+#### openSessionFromConnection
+
+```java
+private SqlSession openSessionFromConnection(ExecutorType execType, Connection connection) {
+ try {
+ boolean autoCommit;
+ try {
+ autoCommit = connection.getAutoCommit();
+ } catch (SQLException e) {
+ // Failover to true, as most poor drivers
+ // or databases won't support transactions
+ autoCommit = true;
+ }
+ final Environment environment = configuration.getEnvironment();
+ final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment);
+ final Transaction tx = transactionFactory.newTransaction(connection);
+ final Executor executor = configuration.newExecutor(tx, execType);
+ return new DefaultSqlSession(configuration, executor, autoCommit);
+ } catch (Exception e) {
+ throw ExceptionFactory.wrapException("Error opening session. Cause: " + e, e);
+ } finally {
+ ErrorContext.instance().reset();
+ }
+}
+```
+
+
+
+
+
+## 获取到sqlSession执行sql
+
+
+
+> 如下将介绍获取到sqlSession后如何执行sql 及sql信息的解析等
+>
+> ```java
+> SqlSession sqlSession = sqlSessionFactory.openSession();
+> Blog selectBlog = (Blog) sqlSession.selectOne("selectBlog", 1);
+> System.out.println(selectBlog);
+> ```
+
+### sqlSessionFactory#openSession
+
+此块之前有浅析过[跳转到之前的代码分析](#DefaultSqlSessionFactory#openSession)
+
+### sqlSession#selectOne
+
+#### DefaultSqlSession#selectOne
+
+```java
+public T selectOne(String statement, Object parameter) {
+ // Popular vote was to return null on 0 results and throw exception on too many.
+ //转而去调用selectList,很简单的,如果得到0条则返回null,得到1条则返回1条,得到多条报TooManyResultsException错
+ // 特别需要主要的是当没有查询到结果的时候就会返回null。因此一般建议在mapper中编写resultType的时候使用包装类型
+ //而不是基本类型,比如推荐使用Integer而不是int。这样就可以避免NPE
+ List list = this.selectList(statement, parameter);
+ if (list.size() == 1) {
+ return list.get(0);
+ } else if (list.size() > 1) {
+ throw new TooManyResultsException("Expected one result (or null) to be returned by selectOne(), but found: " + list.size());
+ } else {
+ return null;
+ }
+}
+```
+
+#### DefaultSqlSession#selectList
+
+```java
+//核心selectList
+@Override
+public List selectList(String statement, Object parameter, RowBounds rowBounds) {
+ try {
+ //从configuration 根据statement id找到对应的MappedStatement
+ MappedStatement ms = configuration.getMappedStatement(statement);
+ //转而用执行器来查询结果,注意这里传入的ResultHandler是null
+ 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();
+ }
+}
+```
+
+
+
+#### Configuration#getMappedStatement
+
+```java
+protected final Map mappedStatements = new StrictMap("Mapped Statements collection");
+
+// ....
+
+public MappedStatement getMappedStatement(String id) {
+ return this.getMappedStatement(id, true);
+}
+
+public MappedStatement getMappedStatement(String id, boolean validateIncompleteStatements) {
+ //先构建所有语句,再返回语句
+ if (validateIncompleteStatements) {
+ // 什么时候会进行调用?
+ buildAllStatements();
+ }
+ return mappedStatements.get(id);
+}
+```
+
+> 1. [这块需要解释MappedStatement是封装了什么?](#MappedStatement解析)
+> 2. [何时往mappedStatements这个map中存入信息?](#mappedStatements信息的存储)
+> 3. 什么时候会进行调用`buildAllStatements`?
+
+##### MappedStatement解析
+
+```java
+public final class MappedStatement {
+
+ private String resource;
+ // 配置信息
+ private Configuration configuration;
+ private String id;
+ private Integer fetchSize;
+ private Integer timeout;
+ // 语句类型
+ private StatementType statementType;
+ // 结果集类型
+ private ResultSetType resultSetType;
+ // SQL源码 主要看这个
+ private SqlSource sqlSource;
+ private Cache cache;
+ private ParameterMap parameterMap;
+ private List resultMaps;
+ private boolean flushCacheRequired;
+ private boolean useCache;
+ private boolean resultOrdered;
+ private SqlCommandType sqlCommandType;
+ private KeyGenerator keyGenerator;
+ private String[] keyProperties;
+ private String[] keyColumns;
+ private boolean hasNestedResultMaps;
+ private String databaseId;
+ private Log statementLog;
+ private LanguageDriver lang;
+ private String[] resultSets;
+
+ MappedStatement() {
+ // constructor disabled 通过builder来创建
+
+ }
+ // ...
+ //静态内部类,建造者模式
+public static class Builder {
+ private MappedStatement mappedStatement = new MappedStatement();
+
+ public Builder(Configuration configuration, String id, SqlSource sqlSource, SqlCommandType sqlCommandType) {
+ mappedStatement.configuration = configuration;
+ mappedStatement.id = id;
+ mappedStatement.sqlSource = sqlSource;
+ mappedStatement.statementType = StatementType.PREPARED;
+ mappedStatement.parameterMap = new ParameterMap.Builder(configuration, "defaultParameterMap", null, new ArrayList()).build();
+ mappedStatement.resultMaps = new ArrayList();
+ mappedStatement.timeout = configuration.getDefaultStatementTimeout();
+ mappedStatement.sqlCommandType = sqlCommandType;
+ mappedStatement.keyGenerator = configuration.isUseGeneratedKeys() && SqlCommandType.INSERT.equals(sqlCommandType) ? new Jdbc3KeyGenerator() : new NoKeyGenerator();
+ String logId = id;
+ if (configuration.getLogPrefix() != null) {
+ logId = configuration.getLogPrefix() + id;
+ }
+ mappedStatement.statementLog = LogFactory.getLog(logId);
+ mappedStatement.lang = configuration.getDefaultScriptingLanuageInstance();
+ }
+
+ // ...
+
+ public MappedStatement build() {
+ // configuration,id,sqlSource,lang 不为空
+ assert mappedStatement.configuration != null;
+ assert mappedStatement.id != null;
+ assert mappedStatement.sqlSource != null;
+ assert mappedStatement.lang != null;
+ mappedStatement.resultMaps = Collections.unmodifiableList(mappedStatement.resultMaps);
+ return mappedStatement;
+ }
+}
+}
+```
+
+###### SqlSource Sql源码类
+
+```java
+public interface SqlSource {
+ BoundSql getBoundSql(Object parameterObject);
+}
+```
+
+```java
+// 绑定的SQL,是从SqlSource而来,将动态内容都处理完成得到的SQL语句字符串,其中包括?,还有绑定的参数
+public class BoundSql {
+ // 封装Sql信息 SELECT * FROM blog WHERE id = ?
+ private String sql;
+ // 参数映射 每一个参数名称和对应的位置
+ private List parameterMappings;
+ // 参数对象 这个像是 传入的为对象时放入的 通过ognl来获取其中参数
+ private Object parameterObject;
+ //todo 这个map又是用于做什么的呢
+ private Map additionalParameters;
+ // 元对象参数
+ private MetaObject metaParameters;
+ // ... 省去构造和设置参数的
+}
+```
+
+```java
+public class ParameterMapping {
+ private Configuration configuration;
+ //例子:#{property,javaType=int,jdbcType=NUMERIC}
+ //property
+ private String property;
+ //mode
+ private ParameterMode mode;
+ //javaType=int
+ private Class> javaType = Object.class;
+ //jdbcType=NUMERIC
+ private JdbcType jdbcType;
+ //numericScale
+ private Integer numericScale;
+ private TypeHandler> typeHandler;
+ private String resultMapId;
+ //jdbcType=NUMERIC
+ private String jdbcTypeName;
+ private String expression;
+}
+```
+
+###### sqlSource的多个实现
+
+> 1. StaticSqlSource
+> 2. DynamicSqlSource
+> 3. RawSqlSource
+> 4. ProviderSqlSource
+> 5. VelocitySqlSource # test class
+
+我们主要分析前三个
+
+1. StaticSqlSource
+
+ > 此SqlSource时最后的静态sql源文件 其封装的sql信息是类似于 `SELECT * FROM blog WHERE id = ?`
+ >
+ > 其他几个SqlSource 最终都会变成这个对象(通过SqlSourceBuilder#parse)
+
+ ```java
+ public class StaticSqlSource implements SqlSource {
+ // 封装sql信息
+ private String sql;
+ // 封装传入的参数的信息
+ private List parameterMappings;
+ // 配置信息
+ private Configuration configuration;
+
+ public StaticSqlSource(Configuration configuration, String sql) {
+ this(configuration, sql, null);
+ }
+
+ public StaticSqlSource(Configuration configuration, String sql, List parameterMappings) {
+ this.sql = sql;
+ this.parameterMappings = parameterMappings;
+ this.configuration = configuration;
+ }
+
+ @Override
+ public BoundSql getBoundSql(Object parameterObject) {
+ return new BoundSql(configuration, sql, parameterMappings, parameterObject);
+ }
+
+ }
+ ```
+
+2. DynamicSqlSource
+
+ ```java
+ public class DynamicSqlSource implements SqlSource {
+
+ private Configuration configuration;
+ private SqlNode rootSqlNode;
+
+ public DynamicSqlSource(Configuration configuration, SqlNode rootSqlNode) {
+ this.configuration = configuration;
+ this.rootSqlNode = rootSqlNode;
+ }
+
+ //得到绑定的SQL
+ @Override
+ public BoundSql getBoundSql(Object parameterObject) {
+ //生成一个动态上下文
+ DynamicContext context = new DynamicContext(configuration, parameterObject);
+ //这里SqlNode.apply只是将${}这种参数替换掉,并没有替换#{}这种参数此apply方法可后续分析
+ rootSqlNode.apply(context);
+ //调用SqlSourceBuilder
+ SqlSourceBuilder sqlSourceParser = new SqlSourceBuilder(configuration);
+ Class> parameterType = parameterObject == null ? Object.class : parameterObject.getClass();
+ //SqlSourceBuilder.parse,注意这里返回的是StaticSqlSource,解析完了就把那些参数都替换成?了,也就是最基本的JDBC的SQL写法
+ SqlSource sqlSource = sqlSourceParser.parse(context.getSql(), parameterType, context.getBindings());
+ //看似是又去递归调用SqlSource.getBoundSql,其实因为是StaticSqlSource,所以没问题,不是递归调用
+ BoundSql boundSql = sqlSource.getBoundSql(parameterObject);
+ for (Map.Entry entry : context.getBindings().entrySet()) {
+ boundSql.setAdditionalParameter(entry.getKey(), entry.getValue());
+ }
+ return boundSql;
+ }
+
+ }
+ ```
+
+3. RawSqlSource
+
+ ```java
+ public class RawSqlSource implements SqlSource {
+
+ private final SqlSource sqlSource;
+
+ public RawSqlSource(Configuration configuration, SqlNode rootSqlNode, Class> parameterType) {
+ this(configuration, getSql(configuration, rootSqlNode), parameterType);
+ }
+
+ public RawSqlSource(Configuration configuration, String sql, Class> parameterType) {
+ SqlSourceBuilder sqlSourceParser = new SqlSourceBuilder(configuration);
+ Class> clazz = parameterType == null ? Object.class : parameterType;
+ sqlSource = sqlSourceParser.parse(sql, clazz, new HashMap());
+ }
+
+ private static String getSql(Configuration configuration, SqlNode rootSqlNode) {
+ DynamicContext context = new DynamicContext(configuration, null);
+ rootSqlNode.apply(context);
+ return context.getSql();
+ }
+
+ @Override
+ public BoundSql getBoundSql(Object parameterObject) {
+ return sqlSource.getBoundSql(parameterObject);
+ }
+
+ }
+ ```
+
+##### mappedStatements信息的存储
+
+通过Configuration#addMappedStatement存储MappedStatement信息。
+
+调用方法 MapperBuilderAssistant#addMappedStatement
+
+再查看调用这个MapperBuilderAssistant#addMappedStatement的类是 XMLStatementBuilder类
+
+```java
+public MappedStatement addMappedStatement(
+ String id,
+ SqlSource sqlSource,
+ StatementType statementType,
+ SqlCommandType sqlCommandType,
+ Integer fetchSize,
+ Integer timeout,
+ String parameterMap,
+ Class> parameterType,
+ String resultMap,
+ Class> resultType,
+ ResultSetType resultSetType,
+ boolean flushCache,
+ boolean useCache,
+ boolean resultOrdered,
+ KeyGenerator keyGenerator,
+ String keyProperty,
+ String keyColumn,
+ String databaseId,
+ LanguageDriver lang,
+ String resultSets) {
+
+ if (unresolvedCacheRef) {
+ throw new IncompleteElementException("Cache-ref not yet resolved");
+ }
+
+ //为id加上namespace前缀
+ id = applyCurrentNamespace(id, false);
+ //是否是select语句
+ boolean isSelect = sqlCommandType == SqlCommandType.SELECT;
+
+ //建造者模式
+ MappedStatement.Builder statementBuilder = new MappedStatement.Builder(configuration, id, sqlSource, sqlCommandType);
+ statementBuilder.resource(resource);
+ statementBuilder.fetchSize(fetchSize);
+ statementBuilder.statementType(statementType);
+ statementBuilder.keyGenerator(keyGenerator);
+ statementBuilder.keyProperty(keyProperty);
+ statementBuilder.keyColumn(keyColumn);
+ statementBuilder.databaseId(databaseId);
+ statementBuilder.lang(lang);
+ statementBuilder.resultOrdered(resultOrdered);
+ statementBuilder.resulSets(resultSets);
+ setStatementTimeout(timeout, statementBuilder);
+
+ //1.参数映射
+ setStatementParameterMap(parameterMap, parameterType, statementBuilder);
+ //2.结果映射
+ setStatementResultMap(resultMap, resultType, resultSetType, statementBuilder);
+ setStatementCache(isSelect, flushCache, useCache, currentCache, statementBuilder);
+
+ MappedStatement statement = statementBuilder.build();
+ //建造好调用configuration.addMappedStatement
+ configuration.addMappedStatement(statement);
+ return statement;
+}
+```
+
+
+
+###### XMLStatementBuilder类 XML语句构建者
+
+1. 成员变量
+
+ ```java
+ // 映射构建助手 extends BaseBuilder
+ private MapperBuilderAssistant builderAssistant;
+ // 结点信息
+ private XNode context;
+ // 需要的databaseId
+ private String requiredDatabaseId;
+ ```
+
+2. 构造方法
+
+ ```java
+ public XMLStatementBuilder(Configuration configuration, MapperBuilderAssistant builderAssistant, XNode context, String databaseId) {
+ // 调用父类构造
+ super(configuration);
+ this.builderAssistant = builderAssistant;
+ // 上下文Node信息
+ this.context = context;
+ this.requiredDatabaseId = databaseId;
+ }
+ ```
+
+3. 主要方法
+
+ 1. parseStatementNode
+
+ **功能**:解析语句结点 select,insert,update,delete的信息,这里面就会把sql的信息进行封装入Configuration中了。(`configuration.addMappedStatement(statement);`)
+
+ 最后通过`MapperBuilderAssistant`这个类去封装这些信息,其内部是用构建者模式。
+
+ 2. parseSelectKeyNode
+
+ **功能**:
+
+### 执行SQL Executor
+
+> 用了执行器`Executor`
+
+#### MapperStatement#getBoundSql
\ No newline at end of file