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