在mybatis中StatementType的值决定了由什么对象来执行我们的SQL语句。本文来分析下在mybatis中具体是怎么处理的。

StatementType

1.StatementType枚举

  StatementType是一个枚举类型。如下:

/**
* @author Clinton Begin
*/
public enum StatementType {
STATEMENT, PREPARED, CALLABLE
}
选项 说明
STATEMENT 对应于Statement对象,有SQL注入的风险
PREPARED PreparedStatement,预编译处理
CALLABLE CallableStatement一般调用存储过程的时候使用

2.设置StatementType

  我们可以在映射文件中通过‘statementType’属性设置,如下:

  注意默认是'PREPARED ',通过源码分析我们可以查看到。

3.源码跟踪查看

  我们要从源码中发现StatementType的处理,应该是从加载配置文件的地方开始查看,所以我们从'new SqlSessionFactoryBuilder().build(in);'这行代码入手

//InputStream in = Resources.getResourceAsStream("mybatis-config.xml");
InputStream in = TestMybatis.class.getResourceAsStream("/mybatis-config.xml");
// 获取SqlSessionFactory对象
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in);

进入build方法中查看

  public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {
try {
XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);
// 配置文件解析的重点是 'parser.parse()'这行代码
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.
}
}
}

进入parse方法中

  public Configuration parse() {
if (parsed) {
throw new BuilderException("Each XMLConfigBuilder can only be used once.");
}
parsed = true;
// 进入
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);
}
}

mapperElement方法,该方法会根据我们在mappers中不同的配置做出不同的处理。我们直接看resource的方式

  private void mapperElement(XNode parent) throws Exception {
if (parent != null) {
for (XNode child : parent.getChildren()) {
if ("package".equals(child.getName())) {
String mapperPackage = child.getStringAttribute("name");
configuration.addMappers(mapperPackage);
} else {
String resource = child.getStringAttribute("resource");
String url = child.getStringAttribute("url");
String mapperClass = child.getStringAttribute("class");
if (resource != null && url == null && mapperClass == null) {
ErrorContext.instance().resource(resource);
// 加载配置文件
InputStream inputStream = Resources.getResourceAsStream(resource);
XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, resource, configuration.getSqlFragments());
// 解析配置文件的方法 进入
mapperParser.parse();
} else if (resource == null && url != null && mapperClass == null) {
ErrorContext.instance().resource(url);
InputStream inputStream = Resources.getUrlAsStream(url);
XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, url, configuration.getSqlFragments());
mapperParser.parse();
} else if (resource == null && url == null && mapperClass != null) {
Class<?> mapperInterface = Resources.classForName(mapperClass);
configuration.addMapper(mapperInterface);
} else {
throw new BuilderException("A mapper element may only specify a url, resource or class, but not more than one.");
}
}
}
}
}

进入 mapperParser.parse() 方法查看

  public void parse() {
if (!configuration.isResourceLoaded(resource)) {
// 解析根节点
configurationElement(parser.evalNode("/mapper"));
configuration.addLoadedResource(resource);
// 绑定名称空间
bindMapperForNamespace();
} parsePendingResultMaps();
parsePendingCacheRefs();
// 重点本方法,解析未处理的Statements
parsePendingStatements();
}

进入parsePendingStatements方法:

  private void parsePendingStatements() {
Collection<XMLStatementBuilder> incompleteStatements = configuration.getIncompleteStatements();
synchronized (incompleteStatements) {
Iterator<XMLStatementBuilder> iter = incompleteStatements.iterator();
while (iter.hasNext()) {
try {
// 解析我们的select|update|insert|delete节点
iter.next().parseStatementNode();
iter.remove();
} catch (IncompleteElementException e) {
// Statement is still missing a resource...
}
}
}
}

进入parseStatementNode 方法

public void parseStatementNode() {
String id = context.getStringAttribute("id");
String databaseId = context.getStringAttribute("databaseId"); if (!databaseIdMatchesCurrent(id, databaseId, this.requiredDatabaseId)) {
return;
} Integer fetchSize = context.getIntAttribute("fetchSize");
Integer timeout = context.getIntAttribute("timeout");
String parameterMap = context.getStringAttribute("parameterMap");
String parameterType = context.getStringAttribute("parameterType");
Class<?> parameterTypeClass = resolveClass(parameterType);
String resultMap = context.getStringAttribute("resultMap");
String resultType = context.getStringAttribute("resultType");
String lang = context.getStringAttribute("lang");
LanguageDriver langDriver = getLanguageDriver(lang); Class<?> resultTypeClass = resolveClass(resultType);
String resultSetType = context.getStringAttribute("resultSetType");
// 重点代码,看截图
StatementType statementType = StatementType.valueOf(context.getStringAttribute("statementType", StatementType.PREPARED.toString()));
ResultSetType resultSetTypeEnum = resolveResultSetType(resultSetType); String nodeName = context.getNode().getNodeName();
SqlCommandType sqlCommandType = SqlCommandType.valueOf(nodeName.toUpperCase(Locale.ENGLISH));
boolean isSelect = sqlCommandType == SqlCommandType.SELECT;
boolean flushCache = context.getBooleanAttribute("flushCache", !isSelect);
boolean useCache = context.getBooleanAttribute("useCache", isSelect);
boolean resultOrdered = context.getBooleanAttribute("resultOrdered", false); // Include Fragments before parsing
XMLIncludeTransformer includeParser = new XMLIncludeTransformer(configuration, builderAssistant);
includeParser.applyIncludes(context.getNode()); // Parse selectKey after includes and remove them.
processSelectKeyNodes(id, parameterTypeClass, langDriver); // Parse the SQL (pre: <selectKey> and <include> were parsed and removed)
SqlSource sqlSource = langDriver.createSqlSource(configuration, context, parameterTypeClass);
String resultSets = context.getStringAttribute("resultSets");
String keyProperty = context.getStringAttribute("keyProperty");
String keyColumn = context.getStringAttribute("keyColumn");
KeyGenerator keyGenerator;
String keyStatementId = id + SelectKeyGenerator.SELECT_KEY_SUFFIX;
keyStatementId = builderAssistant.applyCurrentNamespace(keyStatementId, true);
if (configuration.hasKeyGenerator(keyStatementId)) {
keyGenerator = configuration.getKeyGenerator(keyStatementId);
} else {
keyGenerator = context.getBooleanAttribute("useGeneratedKeys",
configuration.isUseGeneratedKeys() && SqlCommandType.INSERT.equals(sqlCommandType))
? Jdbc3KeyGenerator.INSTANCE : NoKeyGenerator.INSTANCE;
} builderAssistant.addMappedStatement(id, sqlSource, statementType, sqlCommandType,
fetchSize, timeout, parameterMap, parameterTypeClass, resultMap, resultTypeClass,
resultSetTypeEnum, flushCache, useCache, resultOrdered,
keyGenerator, keyProperty, keyColumn, databaseId, langDriver, resultSets);
}

最后解析完这个select|insert|update|delete节点的信息被封装到了一个MapperedStatement对象中。

至此StatementType解析阶段的内容分析完了~

Mybatis源码之StatementType的更多相关文章

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

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

  2. 【MyBatis源码分析】select源码分析及小结

    示例代码 之前的文章说过,对于MyBatis来说insert.update.delete是一组的,因为对于MyBatis来说它们都是update:select是一组的,因为对于MyBatis来说它就是 ...

  3. Spring mybatis源码篇章-MybatisDAO文件解析(二)

    前言:通过阅读源码对实现机制进行了解有利于陶冶情操,承接前文Spring mybatis源码篇章-MybatisDAO文件解析(一) 默认加载mybatis主文件方式 XMLConfigBuilder ...

  4. Spring mybatis源码篇章-MybatisDAO文件解析(一)

    前言:通过阅读源码对实现机制进行了解有利于陶冶情操,承接前文Spring mybatis源码篇章-SqlSessionFactory 加载指定的mybatis主文件 Mybatis模板文件,其中的属性 ...

  5. Mybatis源码分析-BaseExecutor

    根据前文Mybatis源码分析-SqlSessionTemplate的简单分析,对于SqlSession的CURD操作都需要经过Executor接口的update/query方法,本文将分析下Base ...

  6. Mybatis源码分析-StatementHandler

    承接前文Mybatis源码分析-BaseExecutor,本文则对通过StatementHandler接口完成数据库的CRUD操作作简单的分析 StatementHandler#接口列表 //获取St ...

  7. mybatis源码分析(一)

    mybatis源码分析(sqlSessionFactory生成过程) 1. mybatis框架在现在各个IT公司的使用不用多说,这几天看了mybatis的一些源码,赶紧做个笔记. 2. 看源码从一个d ...

  8. Mybatis源码分析之存储过程调用

    这一篇博客我们学习一下Mybatis调用存储过程的使用和运行流程.首先我们先创建一个简单的存储过程 DELIMITER $ CREATE PROCEDURE mybatis.ges_user_coun ...

  9. MyBatis 源码分析 - 缓存原理

    1.简介 在 Web 应用中,缓存是必不可少的组件.通常我们都会用 Redis 或 memcached 等缓存中间件,拦截大量奔向数据库的请求,减轻数据库压力.作为一个重要的组件,MyBatis 自然 ...

随机推荐

  1. PHP通过经纬坐标计算两个地址的距离

    <?php /** *求两个已知经纬度之间的距离,单位为米 * *@param lng1,lng2 经度 * *@param lat1,lat2 纬度 * *@return float 距离,单 ...

  2. input标签实现小数点后两位保留小数

    短短一行代码就可以实现 <input type="number" min="0" max="100" step="0.01& ...

  3. VS之设置文件编码格式

    VS2012默认格式为 "GB2312-80",很多时候可能出现乱码情况,就是编码问题,如何在VS里修改呢? 文件->“高级保存选项 ”  选择gb2312

  4. ubuntu/deepin 下 Sha 哈 dow 哈 socks 全局配置

    1. 安装编译环境 Ubuntu在默认情况下没有提供C/C++的编译环境,因此需要手动安装.如果单独安装gcc以及g++比较麻烦,为了方便,我们直接安装Ubuntu提供的build-essential ...

  5. 用JS来判断版本号比如v21.2.2.2和v21.2.2.2.15

    当判断两个版本号的时候,不能单纯的去点然后相互比较,版本比较可能比较特殊,但是分析起来又挺简单的,既然不能直接去点比较,那就拆分成数组,一组一组对应的去比,为了能正常的进行比较 如果上一个版本长度小于 ...

  6. 【高并发架构】Redis特点及构件模型

    数据结构 redis 相比 memcached 来说,拥有更多的数据结构,能支持更丰富的数据操作.如果需要缓存能够支持更复杂的结构和操作, redis 会是不错的选择. redis 主要有以下几种数据 ...

  7. 使用Bandwagon服务器ftp解决git clone速度慢的问题

    写在前面 git clone速度往往很慢,我们可以先在身处美国的服务器上git clone,然后把文件用ftp传回来即可. 开始 我们以opencv为例 git clone https://githu ...

  8. 大数相加 Big Num

    代码: #include<stdio.h>#include<algorithm>#include<iostream>#include<string.h> ...

  9. Kotlin基础

    1.函数也是对象,可以作为参数和返回值 2.使用驼峰命名,尽量避免下划线 3.public函数应当有说明文档 4.lambda中花括号内前后都应该有空格 5.空值安全检查  var s: String ...

  10. 操作系统中 heap 和 stack 的区别

    操作系统中 heap 和 stack 的区别heap 和 stack是什么堆栈是两种数据结构.堆栈都是一种数据项按序排列的数据结构,只能在一端(称为栈顶(top))对数据项进行插入和删除.==在单片机 ...