【1】测试用例

mybatis-config.xml

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration><!-- 1)properteis1.1、mybatis 可以使用properties 标签来引入外部properties配置文件的内容;1.2、resource 引入类路径下 的资源;1.3、url 是引入网络路径或磁盘路径下的资源;--><properties resource="dbconfig.properties"></properties><!-- 2)settings 包含很多重要的设置项2.1、setting 用来设置每一个设置项:name: 设置项名称, value:设置项取值;--><!-- 开启驼峰命名规则,数据库表字段 a_b 可以解析为 类成员变量 aB --><!-- 显示指定每个需要更改的配置项,即使有默认值,防止版本更新带来的问题 --><settings><!-- 开启驼峰命名法 --><setting name="mapUnderscoreToCamelCase" value="false"/><!-- 懒加载 --><setting name="lazyLoadingEnabled" value="true"/><!-- 侵入型懒加载 --><setting name="aggressiveLazyLoading" value="false"/></settings><!-- 3)别名处理器:可以为java类型起别名(注意:别名不区分大小写)3.1) typeAlias为某一个java类型取别名, type:指定要起别名的类型全类名,默认别名就是类名小写,employeealias: 指定新的 别名;3.3)--><typeAliases><!-- 3.1) typeAlias为某一个java类型取别名, type:指定要起别名的类型全类名,默认别名就是类名小写,employeealias: 指定新的 别名;<typeAlias type="com.swjtu.mybatis.bean.Employee" alias="emp"/>--><!-- 3.2)package: 为某个包下的所有类批量起别名:name: 指定包名,为当前包以及所有的子包的每个类都起一个默认别名(类名小写);--><!-- 3.3) 批量起别名的情况下,可以使用注解 Alias()为某个类型指定新的别名;存在 a 包 和 a.b 包下都有 Employee类的情况;--><package name="com.swjtu.mybatis.bean"/></typeAliases><!-- 4、environments: 环境们, mybatis可以配置多种环境, default指定使用某种环境。可以达到快速切换环境。4.1)environment:配置一个具体的环境信息;必须有两个标签:id代表当前环境的唯一标识4.1.1) transactionManager 必须有, 事务管理器,type 事务管理器类型,type="[JDBC|MANAGED]" 自定义事务管理器:实现 TransactionFactory 接口, type指定全类名 Configuration类中定义了别名: org.apache.ibatis.session.ConfigurationtypeAliasRegistry.registerAlias("JDBC", JdbcTransactionFactory.class);typeAliasRegistry.registerAlias("MANAGED", ManagedTransactionFactory.class);4.1.2) dataSource 数据源:type:数据源类型 type="[UNPOOLED|POOLED|JNDI]"typeAliasRegistry.registerAlias("JNDI", JndiDataSourceFactory.class);typeAliasRegistry.registerAlias("POOLED", PooledDataSourceFactory.class);typeAliasRegistry.registerAlias("UNPOOLED", UnpooledDataSourceFactory.class);自定义数据源: 实现 DataSourceFactory 接口 , type 为自定义数据源的全类名                                           -->                        <environments default="development"><!-- 测试环境 --><environment id="test"><transactionManager type="JDBC"/><dataSource type="POOLED"><property name="driver" value="${jdbc.driver}"/><property name="url" value="${jdbc.url}"/><property name="username" value="${jdbc.username}"/><property name="password" value="${jdbc.password}"/></dataSource></environment><!-- 开发环境 --><environment id="development"><transactionManager type="JDBC"/><dataSource type="POOLED"><property name="driver" value="${jdbc.driver}"/><property name="url" value="${jdbc.url}"/><property name="username" value="${jdbc.username}"/><property name="password" value="${jdbc.password}"/></dataSource></environment></environments><!-- 5、databaseIdProvider :支持多数据库厂商的;type=DB_VENDOR: VendorDatabaseIdProvider作用就是得到数据库厂商标识(由数据库驱动自己附带的 getDatabaseProductName),mybatis根据该标识执行不同的sql;并在 sql 标签如<select> 添加属性 databaseId 属性--> <databaseIdProvider type="DB_VENDOR"><!-- 为不同数据库厂商起别名 --><property name="Mysql" value="mysql"/><property name="Oracle" value="oracle"/><property name="SQL Server" value="sqlserver"/></databaseIdProvider> <!-- 将我们写好的sql 映射文件 EmployeeMapper.xml 一定要注册到全局配置文件 mybatis-config.xml 中;6、mappers:将sql映射注册到全局配置中;--> <mappers><!-- mapper标签: 注册一个sql映射注册配置文件的两种方法:方法1:resource: 引用类路径下的sql 映射文件;方法2:url: 引用网络路径或磁盘路径下的 sql 映射文件;注册接口的方法:方法1 class: 直接引用(注册)接口, 写接口的全类名;补充1:  有sql映射文件,映射文件名必须和接口同名,并且仿造同一个目录下;补充2: 没有sql映射文件, 所有sql 利用注解修饰接口上;补充3: 不推荐使用注解,而使用sql映射文件的方式来 进行sql 映射。--><mapper resource="com\swjtu\mybatis\dao\EmployeeMapper.xml"/> <!-- 批量注册: 前提是要把 Mapper类文件和Mapper sql 映射文件放在同目录下荔枝:<package name="com.swjtu.mybatis.dao"/> 可以吧 该包下的所有mapper 映射文件注册到全局配置环境中;--><!-- <mapper class="com.swjtu.mybatis.dao.cwn.MYCwnOprDAO"/> --></mappers></configuration>

执行sql;

public static void main1() {/*1. 获取 SqlSessionFactory 对象*/SqlSessionFactory sqlSessionFactory = MyBatisUtils.getSqlSessionFactory();/*2.获取SqlSession对象, 不会自动提交数据*/SqlSession session = sqlSessionFactory.openSession();try { String flag = "0911A"; List<Map<String, Object>> list = new ArrayList<>();for (int i = 0; i < 5; i++) {  String indexFormat = String.format("%8d", i);Map<String, Object> map = new HashMap<>(); map.put("RCRD_ID", flag + indexFormat); map.put("CUST_NUM", "CUST_NUM" +flag+ indexFormat);map.put("CUST_NAME", "CUST_NAME" +flag+ indexFormat);map.put("WARN_TIMES", i); list.add(map); }
//          mapper.insert2CustWarn(list);session.insert("com.swjtu.mybatis.dao.EmployeeMapper.insert2CustWarn", list); session.commit();} finally { session.close();}System.out.println("bingo!");} 
mapper文件中的 dml 元素
<insert id="insert2CustWarn">INSERT INTO my_cust_warn_tbl(rcrd_id, cust_num, cust_name , warn_times) VALUES <foreach collection="list" item="item" separator=",">(#{item.RCRD_ID}, #{item.CUST_NUM}, #{item.CUST_NAME}, #{item.WARN_TIMES})</foreach></insert> 

MyBatisUtils.getSqlSessionFactory();

/*** 获取SqlSessionFactory * @return*/
public final static SqlSessionFactory getSqlSessionFactory() {String resource = "mybatis-config.xml";InputStream inputStream;try { inputStream = Resources.getResourceAsStream(resource);SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);return sqlSessionFactory;} catch (IOException e) {e.printStackTrace();}return null;
}

【2】创建 SqlSessionFactory 对象

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

实际上返回的是  DefaultSqlSessionFactory;

// SqlSessionFactoryBuilder
public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {try {XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);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.}}}public SqlSessionFactory build(Configuration config) {return new DefaultSqlSessionFactory(config);}

而 build(Configuration config) 中的 config 是从mybatis-config.xml文件解析而来的;


【2.1】XMLConfigBuilder.parse() 产生Configuration

parser.parse() 方法的逻辑如下(XMLConfigBuilder构造器中新建了 Configuration对象):

// SqlSessionFactoryBuilder
private XMLConfigBuilder(XPathParser parser, String environment, Properties props) {super(new Configuration());// 新建了一个 configuration对象ErrorContext.instance().resource("SQL Mapper Configuration");this.configuration.setVariables(props);this.parsed = false;this.environment = environment;this.parser = parser;}public Configuration parse() {if (parsed) {throw new BuilderException("Each XMLConfigBuilder can only be used once.");}parsed = true;parseConfiguration(parser.evalNode("/configuration"));// 解析xml节点return configuration;}private void parseConfiguration(XNode root) {// 解析xml节点try {Properties settings = settingsAsPropertiess(root.evalNode("settings"));//issue #117 read properties firstpropertiesElement(root.evalNode("properties"));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 #631environmentsElement(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(..)  方法,它是解析 mybatis-config.xml 文件中在 mapper标签中配置的resource属性指定的sql mapper文件,比如:

<mappers>               <mapper resource="com\swjtu\mybatis\dao\EmployeeMapper.xml"/>     <mapper class="com.swjtu.mybatis.dao.EmployeeMapperAnnotation"/>
</mappers>

其中 root.evalNode("mappers") 获取的值就是 mapper 数组;

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.");}}}}}

Xnode parent 长这样(就是 mybatis-config.xml 中 mappers元素内容):

以上代码,我们发现,解析 mapper有三种方式;

  • 1.通过resource 配置;
  • 2.通过url;
  • 3.通过mapper class;

下面,我们以resource 为例来讲解;

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();
}

以上代码发现,每一个mapper文件,都会创建一个 XMLMapperBuilder 对象来解析它;

String resource=“com\swjtu\mybatis\dao\EmployeeMapper.xml”;

XMLMapperBuilder构造器传入了4个参数

  1. mapper文件的输入流(com\swjtu\mybatis\dao\EmployeeMapper.xml文件流);
  2. mybatis配置对象Configuration;
  3. resource(sql文件名称com\swjtu\mybatis\dao\EmployeeMapper.xml);
  4. sqlFragments-sql片段为空;

XMLMapperBuilder构造器;该构造器新建了 MapperBuilderAssistant对象(解析mapper文件的助手);

public XMLMapperBuilder(InputStream inputStream, Configuration configuration, String resource, Map<String, XNode> sqlFragments) {this(new XPathParser(inputStream, true, configuration.getVariables(), new XMLMapperEntityResolver()),configuration, resource, sqlFragments);}private XMLMapperBuilder(XPathParser parser, Configuration configuration, String resource, Map<String, XNode> sqlFragments) {super(configuration);this.builderAssistant = new MapperBuilderAssistant(configuration, resource);this.parser = parser;this.sqlFragments = sqlFragments;this.resource = resource;}

MapperBuilderAssistant() 长这个样子;


【2.2】XmlMapperBuilder().parse()  解析mappers下的每个mapper文件内容

接着,进入  XmlMapperBuilder().parse() 方法,解析mappers下的mapper元素的内容,mapper元素如     <mapper resource="com\swjtu\mybatis\dao\EmployeeMapper.xml"/>    ;如下:

代码步骤:

  • step1)解析mapper文件下 mapper元素内容(主要是 dml标签元素内容);
  • step2)把mapper文件名添加到 configuration,以避免二次解析;
  • step3)构建命名空间;

【2.2.1】 XmlMapperBuilder.configurationElement(parser.evalNode("/mapper"))如下:

其中 parser.evalNode("/mapper")或context就是 文件 com\swjtu\mybatis\dao\EmployeeMapper.xml  的内容,如下:

代码步骤:

  • step1)设置命名空间;
  • step2)解析缓存;
  • step3)解析 parameterMap 标签内容;
  • step4)解析 resultMap 标签内容;
  • step5)解析 sql 标签内容;
  • step6) 解析 sql 的dml 标签内容(select或insert或update 或 delete);

最后一步, buildStatementFromContext(context.evalNodes("select|insert|update|delete")); 就是真正解析 sql的 dml语句的标签了,即解析出sql文本并加载到内存中;

List<XNode>  list 包含了多个 dml标签的内容数组;

接着 buildStatementFromContext(List<XNode> list, String requiredDatabaseId) 遍历list中的每个dml 标签元素;  如下:

private void buildStatementFromContext(List<XNode> list) {if (configuration.getDatabaseId() != null) {buildStatementFromContext(list, configuration.getDatabaseId());}buildStatementFromContext(list, null);}private void buildStatementFromContext(List<XNode> list, String requiredDatabaseId) {for (XNode context : list) {final XMLStatementBuilder statementParser = new XMLStatementBuilder(configuration, builderAssistant, context, requiredDatabaseId);try {statementParser.parseStatementNode();} catch (IncompleteElementException e) {configuration.addIncompleteStatement(statementParser);}}}

以上代码,每个 dml标签元素 就会创建一个 XMLStatementBuilder 对象;

statementParser.parseStatementNode();

就用于解析 context 文本(dml标签元素内容); 如下:

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 parsingXMLIncludeTransformer 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))? new Jdbc3KeyGenerator() : new NoKeyGenerator();}builderAssistant.addMappedStatement(id, sqlSource, statementType, sqlCommandType,fetchSize, timeout, parameterMap, parameterTypeClass, resultMap, resultTypeClass,resultSetTypeEnum, flushCache, useCache, resultOrdered, keyGenerator, keyProperty, keyColumn, databaseId, langDriver, resultSets);}

以上代码的步骤如下:

step1)解析 dml元素中各种属性,包括 parameterMap,parameterType,resultMap,resultType等;

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");

resultType 为map;因为 dml标签元素如下:

<select id="getEmpByLastNameLikeReturnMap" resultType="map">SELECT id AS ID, last_name AS LASTNAME, email AS EMAIL, gender AS GENDERFROM emp_tbl WHERE last_name like #{lastName}</select>

step2)获取语言驱动器, LanguageDriver langDriver = getLanguageDriver(lang);

step3)    Class<?> resultTypeClass = resolveClass(resultType);:返回 resultType的class类型;如 resultType为map,则返回 Map.class ;

protected Class<?> resolveClass(String alias) { // 以alias为map为例if (alias == null) {return null;}try {return resolveAlias(alias);} catch (Exception e) {throw new BuilderException("Error resolving class. Cause: " + e, e);}}
// BaseBuilder.java
protected Class<?> resolveAlias(String alias) {return typeAliasRegistry.resolveAlias(alias);}public <T> Class<T> resolveAlias(String string) {try {if (string == null) {return null;}// issue #748String key = string.toLowerCase(Locale.ENGLISH);Class<T> value;if (TYPE_ALIASES.containsKey(key)) {value = (Class<T>) TYPE_ALIASES.get(key);} else {value = (Class<T>) Resources.classForName(string);}return value;} catch (ClassNotFoundException e) {throw new TypeException("Could not resolve type alias '" + string + "'.  Cause: " + e, e);}}

typeAliasRegistry 为类型别名注册器,如下:

public TypeAliasRegistry() {registerAlias("string", String.class);registerAlias("byte", Byte.class);registerAlias("long", Long.class);registerAlias("short", Short.class);registerAlias("int", Integer.class);registerAlias("integer", Integer.class);registerAlias("double", Double.class);registerAlias("float", Float.class);registerAlias("boolean", Boolean.class);registerAlias("byte[]", Byte[].class);registerAlias("long[]", Long[].class);registerAlias("short[]", Short[].class);registerAlias("int[]", Integer[].class);registerAlias("integer[]", Integer[].class);registerAlias("double[]", Double[].class);registerAlias("float[]", Float[].class);registerAlias("boolean[]", Boolean[].class);registerAlias("_byte", byte.class);registerAlias("_long", long.class);registerAlias("_short", short.class);registerAlias("_int", int.class);registerAlias("_integer", int.class);registerAlias("_double", double.class);registerAlias("_float", float.class);registerAlias("_boolean", boolean.class);registerAlias("_byte[]", byte[].class);registerAlias("_long[]", long[].class);registerAlias("_short[]", short[].class);registerAlias("_int[]", int[].class);registerAlias("_integer[]", int[].class);registerAlias("_double[]", double[].class);registerAlias("_float[]", float[].class);registerAlias("_boolean[]", boolean[].class);registerAlias("date", Date.class);registerAlias("decimal", BigDecimal.class);registerAlias("bigdecimal", BigDecimal.class);registerAlias("biginteger", BigInteger.class);registerAlias("object", Object.class);registerAlias("date[]", Date[].class);registerAlias("decimal[]", BigDecimal[].class);registerAlias("bigdecimal[]", BigDecimal[].class);registerAlias("biginteger[]", BigInteger[].class);registerAlias("object[]", Object[].class);registerAlias("map", Map.class);registerAlias("hashmap", HashMap.class);registerAlias("list", List.class);registerAlias("arraylist", ArrayList.class);registerAlias("collection", Collection.class);registerAlias("iterator", Iterator.class);registerAlias("ResultSet", ResultSet.class);}

step4)获取sql语句类型, PREPARED ;

step5)获取sql命令类型 SqlCommandType -SELECT

String nodeName = context.getNode().getNodeName(); // nodeName为selectSqlCommandType sqlCommandType = SqlCommandType.valueOf(nodeName.toUpperCase(Locale.ENGLISH));public enum SqlCommandType {UNKNOWN, INSERT, UPDATE, DELETE, SELECT, FLUSH;
}

step6)解析是否需要使用缓存

boolean flushCache = context.getBooleanAttribute("flushCache", !isSelect);//false
boolean useCache = context.getBooleanAttribute("useCache", isSelect);//true
boolean resultOrdered = context.getBooleanAttribute("resultOrdered", false);//false

step7)解析dml元素中 include元素内容(比如 include sql);

 XMLIncludeTransformer includeParser = new XMLIncludeTransformer(configuration, builderAssistant);includeParser.applyIncludes(context.getNode());

public void applyIncludes(Node source) {Properties variablesContext = new Properties();Properties configurationVariables = configuration.getVariables();if (configurationVariables != null) {variablesContext.putAll(configurationVariables);}applyIncludes(source, variablesContext);}

step8)解析 selectKey;sql中没有,不用考虑;

processSelectKeyNodes(id, parameterTypeClass, langDriver);

step9)创建sql 映射对象;

SqlSource sqlSource = langDriver.createSqlSource(configuration, context, parameterTypeClass);

langDriver 指向了 org.apache.ibatis.scripting.xmltags.XMLLanguageDriver

public class XMLLanguageDriver implements LanguageDriver {@Overridepublic ParameterHandler createParameterHandler(MappedStatement mappedStatement, Object parameterObject, BoundSql boundSql) {return new DefaultParameterHandler(mappedStatement, parameterObject, boundSql);}@Overridepublic SqlSource createSqlSource(Configuration configuration, XNode script, Class<?> parameterType) {XMLScriptBuilder builder = new XMLScriptBuilder(configuration, script, parameterType);return builder.parseScriptNode();}@Overridepublic SqlSource createSqlSource(Configuration configuration, String script, Class<?> parameterType) {// issue #3if (script.startsWith("<script>")) {XPathParser parser = new XPathParser(script, false, configuration.getVariables(), new XMLMapperEntityResolver());return createSqlSource(configuration, parser.evalNode("/script"), parameterType);} else {// issue #127script = PropertyParser.parse(script, configuration.getVariables());TextSqlNode textSqlNode = new TextSqlNode(script);if (textSqlNode.isDynamic()) {return new DynamicSqlSource(configuration, textSqlNode);} else {return new RawSqlSource(configuration, script, parameterType);}}}}

builder.parseScriptNode()  调用的是 XMLScriptBuilder.parseScriptNode() ; 解析动态sql (用${} 包裹的sql );

 public SqlSource parseScriptNode() {List<SqlNode> contents = parseDynamicTags(context);MixedSqlNode rootSqlNode = new MixedSqlNode(contents);SqlSource sqlSource = null;if (isDynamic) {sqlSource = new DynamicSqlSource(configuration, rootSqlNode);} else {sqlSource = new RawSqlSource(configuration, rootSqlNode, parameterType);}return sqlSource;}List<SqlNode> parseDynamicTags(XNode node) {List<SqlNode> contents = new ArrayList<SqlNode>();NodeList children = node.getNode().getChildNodes();for (int i = 0; i < children.getLength(); i++) {XNode child = node.newXNode(children.item(i));if (child.getNode().getNodeType() == Node.CDATA_SECTION_NODE || child.getNode().getNodeType() == Node.TEXT_NODE) {String data = child.getStringBody("");TextSqlNode textSqlNode = new TextSqlNode(data);if (textSqlNode.isDynamic()) {contents.add(textSqlNode);isDynamic = true;} else {contents.add(new StaticTextSqlNode(data));}} else if (child.getNode().getNodeType() == Node.ELEMENT_NODE) { // issue #628String nodeName = child.getNode().getNodeName();NodeHandler handler = nodeHandlers(nodeName);if (handler == null) {throw new BuilderException("Unknown element <" + nodeName + "> in SQL statement.");}handler.handleNode(child, contents);isDynamic = true;}}return contents;}

因为我们不是动态sql,所以返回的是 RawSqlSource

sqlSource = new RawSqlSource(configuration, rootSqlNode, parameterType);

step10)解析 resultSets, keyProperty, keyColumn 属性等 (也不用太关心);

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))? new Jdbc3KeyGenerator() : new NoKeyGenerator();}

step11)MapperBuilderAssistant.addMappedStatement() 把解析得到的sql映射对象添加到configuration;

builderAssistant.addMappedStatement(id, sqlSource, statementType, sqlCommandType,fetchSize, timeout, parameterMap, parameterTypeClass, resultMap, resultTypeClass,resultSetTypeEnum, flushCache, useCache, resultOrdered, keyGenerator, keyProperty, keyColumn, databaseId, langDriver, resultSets);

参数列表如下:

  • 1.id,getEmpByLastNameLikeReturnMap;
  • 2.sqlSource,

parameterMappins 明显是 参数映射;

  • 3.statementType , PREPARED;
  • 4.sqlCommandType,SELECT;
  • 5.fetchSize,null;
  • 6.timeout, null;
  • 7.parameterMap,null;
  • 8.parameterTypeClass,null;
  • 9.resultMap,null;
  • 10.resultTypeClass, interface java.util.Map;
  • 11.resultSetTypeEnum,null;
  • 12.flushCache,false;
  • 13.useCache,true;
  • 14.resultOrdered,false;
  • 15.keyGenerator,org.apache.ibatis.executor.keygen.NoKeyGenerator@5ef8df1e;
  • 16.keyProperty,null;
  • 17.keyColumn,null;
  • 18.databaseId,null;
  • 19.langDriver, org.apache.ibatis.scripting.xmltags.XMLLanguageDriver@32057e6;
  • 20.resultSets, null;

MapperBuilderAssistant.addMappedStatement(...)  源码如下:

public MappedStatement addMappedStatement(...) {if (unresolvedCacheRef) {throw new IncompleteElementException("Cache-ref not yet resolved");}id = applyCurrentNamespace(id, false);boolean isSelect = sqlCommandType == SqlCommandType.SELECT;MappedStatement.Builder statementBuilder = new MappedStatement.Builder(configuration, id, sqlSource, sqlCommandType).resource(resource).fetchSize(fetchSize).timeout(timeout).statementType(statementType).keyGenerator(keyGenerator).keyProperty(keyProperty).keyColumn(keyColumn).databaseId(databaseId).lang(lang).resultOrdered(resultOrdered).resultSets(resultSets).resultMaps(getStatementResultMaps(resultMap, resultType, id)).resultSetType(resultSetType).flushCacheRequired(valueOrDefault(flushCache, !isSelect)).useCache(valueOrDefault(useCache, isSelect)).cache(currentCache);ParameterMap statementParameterMap = getStatementParameterMap(parameterMap, parameterType, id);if (statementParameterMap != null) {statementBuilder.parameterMap(statementParameterMap);}MappedStatement statement = statementBuilder.build();configuration.addMappedStatement(statement);return statement;}

id = applyCurrentNamespace(id, false); 这一步,给id添加命名空间前缀,

  • 执行前;id为  getEmpByLastNameLikeReturnMap ;
  • 执行后,id为 com.swjtu.mybatis.dao.EmployeeMapper.getEmpByLastNameLikeReturnMap;

解析每个dml元素时,都要创建一个  MappedStatement.Builder(...) 用于创建 MappedStatement( sql映射信息)

MappedStatement.Builder statementBuilder = new MappedStatement.Builder(configuration, id, sqlSource, sqlCommandType)

MappedStatement statement = statementBuilder.build(); 源码如下:

public MappedStatement build() {assert mappedStatement.configuration != null;assert mappedStatement.id != null;assert mappedStatement.sqlSource != null;assert mappedStatement.lang != null;mappedStatement.resultMaps = Collections.unmodifiableList(mappedStatement.resultMaps);return mappedStatement;}

补充:MappedStatement对象

MappedStatement 实际是封装了 sql映射信息的对象 ,sql映射是在 MapperBuilderAssistant.addMappedStatement(...) 方法里面构建;结构如下:

每条sql都是一个 MappedStatement对象,解析完成后,将其添加到 configuration对象中;

configuration 就是根据mybatis-config.xml 文件解析出的 Configuration 对象;长这个样子:


【2.2.2】 configuration.addLoadedResource(resource);

把已经解析的resource 文件名添加到 configuration,避免二次解析;


【2.2.3】XMLMapperBuilder.bindMapperForNamespace() 创建命名空间

private void bindMapperForNamespace() {String namespace = builderAssistant.getCurrentNamespace();if (namespace != null) {Class<?> boundType = null;try {boundType = Resources.classForName(namespace);} catch (ClassNotFoundException e) {//ignore, bound type is not required}if (boundType != null) {if (!configuration.hasMapper(boundType)) {// Spring may not know the real resource name so we set a flag// to prevent loading again this resource from the mapper interface// look at MapperAnnotationBuilder#loadXmlResourceconfiguration.addLoadedResource("namespace:" + namespace);configuration.addMapper(boundType);}}}}public <T> void addMapper(Class<T> type) {mapperRegistry.addMapper(type);}

很显然,当 有mapperclass的时候,才会设置 boundType;如果没有mapper class也没有关系;因为catch会忽略异常;


【2.2.4】解析其他

 parsePendingResultMaps();parsePendingChacheRefs();parsePendingStatements();

【2.2.5】创建 DefaultSqlSession对象并返回

解析完成后,生成Configuration对象;


【3】sqlSessionFactory.openSession()打开会话

调用的是 DefaultSqlSessionFactory.openSession()

// DefaultSqlSessionFactory
@Overridepublic SqlSession openSession() {return openSessionFromDataSource(configuration.getDefaultExecutorType(), null, false);}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();}}

【3.1】DefaultSqlSessionFactory 中有两类方法包括,

  1. 从数据源获取会话:private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit);
  2. 从连接中获取会话:private SqlSession openSessionFromConnection(ExecutorType execType, Connection connection) ;

两者不同点在于

tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);

final Transaction tx = transactionFactory.newTransaction(connection);

// 从数据源获取会话
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);// 执行器 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();}}// 从数据库连接获取会话 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 transactionsautoCommit = 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();}}

获取会话无论采用 datasource,还是connection方式,其步骤如下:

  • step1)获取环境对象;
  • step2)获取事务工厂;
  • step3)创建事务;
  • step4)创建执行器;
  • step5)创建会话 DefaultSqlSession;

【3.2】事务工厂

// 从环境对象中获取事务工厂private TransactionFactory getTransactionFactoryFromEnvironment(Environment environment) {if (environment == null || environment.getTransactionFactory() == null) {return new ManagedTransactionFactory();}return environment.getTransactionFactory();}

如果配置了环境元素,则返回配置的环境的事务工厂;否则默认返回 ManagedTransactionFactory;

此外,事务工厂有3个,如下:


【3.3】创建事务-ManagedTransaction

// 可管理的事务工厂
public class ManagedTransactionFactory implements TransactionFactory {private boolean closeConnection = true;@Overridepublic void setProperties(Properties props) {if (props != null) {String closeConnectionProperty = props.getProperty("closeConnection");if (closeConnectionProperty != null) {closeConnection = Boolean.valueOf(closeConnectionProperty);}}}@Overridepublic Transaction newTransaction(Connection conn) {return new ManagedTransaction(conn, closeConnection);}
// 创建事务对象@Overridepublic Transaction newTransaction(DataSource ds, TransactionIsolationLevel level, boolean autoCommit) {// Silently ignores autocommit and isolation level, as managed transactions are entirely// controlled by an external manager.  It's silently ignored so that// code remains portable between managed and unmanaged configurations.return new ManagedTransaction(ds, level, closeConnection);}
}

可管理事务: ManagedTransaction;

public class ManagedTransaction implements Transaction {private static final Log log = LogFactory.getLog(ManagedTransaction.class);private DataSource dataSource;private TransactionIsolationLevel level;private Connection connection;private boolean closeConnection;public ManagedTransaction(Connection connection, boolean closeConnection) {this.connection = connection;this.closeConnection = closeConnection;}public ManagedTransaction(DataSource ds, TransactionIsolationLevel level, boolean closeConnection) {this.dataSource = ds;this.level = level;this.closeConnection = closeConnection;}@Overridepublic Connection getConnection() throws SQLException {if (this.connection == null) {openConnection();}return this.connection;}@Overridepublic void commit() throws SQLException {// Does nothing}@Overridepublic void rollback() throws SQLException {// Does nothing}@Overridepublic void close() throws SQLException {if (this.closeConnection && this.connection != null) {if (log.isDebugEnabled()) {log.debug("Closing JDBC Connection [" + this.connection + "]");}this.connection.close();}}protected void openConnection() throws SQLException {if (log.isDebugEnabled()) {log.debug("Opening JDBC Connection");}this.connection = this.dataSource.getConnection();if (this.level != null) {this.connection.setTransactionIsolation(this.level.getLevel());}}}

它的commit, rollback 都是空的;

它的数据库连接的获取有两种方式,

  1. 上游给定,传入 Connection ;
  2. 通过 datasource 数据源获取connection;

【3.4】创建执行器

1)ExecutorType 是枚举类型,默认为SIMPLE ;

final Executor executor = configuration.newExecutor(tx, execType);// Configuration.newExecutor 方法
public Executor newExecutor(Transaction transaction, ExecutorType executorType) {executorType = executorType == null ? defaultExecutorType : executorType;executorType = executorType == null ? ExecutorType.SIMPLE : executorType;Executor executor;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);}if (cacheEnabled) {executor = new CachingExecutor(executor);}executor = (Executor) interceptorChain.pluginAll(executor);return executor;}

如果为SIMPLE,则返回 SimpleExecutor 执行器;  但 如果启用缓存,则封装SimpleExecutor到缓存执行器;

最后把 执行器添加到拦截器列表,拦截器 Interceptor 列表如下,本质上是一个 ArrayList数组;

// 拦截器
public class InterceptorChain {private final List<Interceptor> interceptors = new ArrayList<Interceptor>();public Object pluginAll(Object target) {for (Interceptor interceptor : interceptors) {target = interceptor.plugin(target);}return target;}public void addInterceptor(Interceptor interceptor) {interceptors.add(interceptor);}public List<Interceptor> getInterceptors() {return Collections.unmodifiableList(interceptors);}}

其中SimpleExecutor父类Executor的实现类 包括

2)SimpleExecutor 长这个样子

public class SimpleExecutor extends BaseExecutor {public SimpleExecutor(Configuration configuration, Transaction transaction) {super(configuration, transaction);}@Overridepublic int doUpdate(MappedStatement ms, Object parameter) throws SQLException {Statement stmt = null;try {Configuration configuration = ms.getConfiguration();StatementHandler handler = configuration.newStatementHandler(this, ms, parameter, RowBounds.DEFAULT, null, null);stmt = prepareStatement(handler, ms.getStatementLog());return handler.update(stmt);} finally {closeStatement(stmt);}}@Overridepublic <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {Statement stmt = null;try {Configuration configuration = ms.getConfiguration();StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql);stmt = prepareStatement(handler, ms.getStatementLog());return handler.<E>query(stmt, resultHandler);} finally {closeStatement(stmt);}}@Overrideprotected <E> Cursor<E> doQueryCursor(MappedStatement ms, Object parameter, RowBounds rowBounds, BoundSql boundSql) throws SQLException {Configuration configuration = ms.getConfiguration();StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, null, boundSql);Statement stmt = prepareStatement(handler, ms.getStatementLog());return handler.<E>queryCursor(stmt);}@Overridepublic List<BatchResult> doFlushStatements(boolean isRollback) throws SQLException {return Collections.emptyList();}private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException {Statement stmt;Connection connection = getConnection(statementLog);stmt = handler.prepare(connection, transaction.getTimeout());handler.parameterize(stmt);return stmt;}}

【3.5】 创建会话 DefaultSqlSession

 public DefaultSqlSession(Configuration configuration, Executor executor, boolean autoCommit) {this.configuration = configuration;this.executor = executor;this.dirty = false;this.autoCommit = autoCommit;}

【4】session.insert(...)

【4.1】DefaultSqlSession.insert()

session.insert(...); 它实际调用的是 DefaultSqlSession.insert() 方法;

// DefaultSqlSession
@Overridepublic int insert(String statement, Object parameter) {return update(statement, parameter);}@Overridepublic int update(String statement) {return update(statement, null);}@Overridepublic int update(String statement, Object parameter) {try {dirty = true;MappedStatement ms = configuration.getMappedStatement(statement);return executor.update(ms, wrapCollection(parameter));} catch (Exception e) {throw ExceptionFactory.wrapException("Error updating database.  Cause: " + e, e);} finally {ErrorContext.instance().reset();}}

executor.update(ms, warpCollection(parameter)); 中的  executor是 SimpleExecutor;

我们接着看下 SimpleExecutor-执行器 ;

// 简单执行器
public class SimpleExecutor extends BaseExecutor {public SimpleExecutor(Configuration configuration, Transaction transaction) {super(configuration, transaction);}// 执行器基类
public abstract class BaseExecutor implements Executor {private static final Log log = LogFactory.getLog(BaseExecutor.class);protected Transaction transaction;protected Executor wrapper;protected ConcurrentLinkedQueue<DeferredLoad> deferredLoads;protected PerpetualCache localCache;protected PerpetualCache localOutputParameterCache;protected Configuration configuration;protected int queryStack = 0;private boolean closed;protected BaseExecutor(Configuration configuration, Transaction transaction) {this.transaction = transaction;this.deferredLoads = new ConcurrentLinkedQueue<DeferredLoad>();this.localCache = new PerpetualCache("LocalCache");this.localOutputParameterCache = new PerpetualCache("LocalOutputParameterCache");this.closed = false;this.configuration = configuration;this.wrapper = this;}

BaseExecutor.update(MappedStatement ms, Object parameter) 如下:

@Overridepublic int update(MappedStatement ms, Object parameter) throws SQLException {ErrorContext.instance().resource(ms.getResource()).activity("executing an update").object(ms.getId());if (closed) {throw new ExecutorException("Executor was closed.");}clearLocalCache();return doUpdate(ms, parameter);}

SimpleExecutor.doUpdate(...)

 @Overridepublic int doUpdate(MappedStatement ms, Object parameter) throws SQLException {Statement stmt = null;try {Configuration configuration = ms.getConfiguration();StatementHandler handler = configuration.newStatementHandler(this, ms, parameter, RowBounds.DEFAULT, null, null);stmt = prepareStatement(handler, ms.getStatementLog());return handler.update(stmt);} finally {closeStatement(stmt);}}

上述是执行sql 语句的地方,依赖的对象包括:

  1. configuration:根据mybatis-config.xml 解析得到的配置对象;
  2. StatementHandler:语句处理器;
  3. PreparedStatement:预编译语句;

Configuration.newStatementHandler(...) 方法如下:

  public StatementHandler newStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {StatementHandler statementHandler = new RoutingStatementHandler(executor, mappedStatement, parameterObject, rowBounds, resultHandler, boundSql);statementHandler = (StatementHandler) interceptorChain.pluginAll(statementHandler);return statementHandler;}

参数列表为:

参数1-executor;

参数2-mappedStatement;

参数3-parameterObject;

参数4- parameterObject 为:

{collection=[{RCRD_ID=0908A       0,CUST_NUM=CUST_NUM       0,WARN_TIMES=0,CUST_NAME=CUST_NAME       0},{RCRD_ID=0908A       1,CUST_NUM=CUST_NUM       1,WARN_TIMES=1,CUST_NAME=CUST_NAME       1},{RCRD_ID=0908A       2,CUST_NUM=CUST_NUM       2,WARN_TIMES=2,CUST_NAME=CUST_NAME       2},...],list=[{RCRD_ID=0908A       0,CUST_NUM=CUST_NUM       0,WARN_TIMES=0,CUST_NAME=CUST_NAME       0},{RCRD_ID=0908A       1,CUST_NUM=CUST_NUM       1,WARN_TIMES=1,CUST_NAME=CUST_NAME       1},{RCRD_ID=0908A       2,CUST_NUM=CUST_NUM       2,WARN_TIMES=2,CUST_NAME=CUST_NAME       2},]}

参数5-rowBounds

参数6-resultHandler 为null;

参数7-boundSql 为null ;


SimpleExecutor.newStatementHandler(...) 如下:

delegate = new PreparedStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql);

代码步骤:

step1)获取mybatis 配置对象;

step2)获取 StatementHandler;

StatementHandler handler = configuration.newStatementHandler(this, ms, parameter, RowBounds.DEFAULT, null, null);

StatementHandler handler =
configuration.newStatementHandler(this, ms, parameter, RowBounds.DEFAULT, null, null);// 新建语句处理器public StatementHandler newStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {StatementHandler statementHandler = new RoutingStatementHandler(executor, mappedStatement, parameterObject, rowBounds, resultHandler, boundSql);statementHandler = (StatementHandler) interceptorChain.pluginAll(statementHandler);return statementHandler;}

step3)prepareStatement(StatementHandler handler, Log statementLog) ,把预编译sql 的参数赋值;

handler 为 PreparedStatementHandler ;

  • step3.1)stmt = handler.prepare(connection, transaction.getTimeout()); 获取预编译语句;
// RoutingStatementHandler @Overridepublic Statement prepare(Connection connection, Integer transactionTimeout) throws SQLException {return delegate.prepare(connection, transactionTimeout);}// BaseStatemetnHandler@Overridepublic Statement prepare(Connection connection, Integer transactionTimeout) throws SQLException {ErrorContext.instance().sql(boundSql.getSql());Statement statement = null;try {statement = instantiateStatement(connection);setStatementTimeout(statement, transactionTimeout);setFetchSize(statement);return statement;} catch (SQLException e) {closeStatement(statement);throw e;} catch (Exception e) {closeStatement(statement);throw new ExecutorException("Error preparing statement.  Cause: " + e, e);}}
  • step3.2)handler.parameterize(stmt); 对参数进行赋值
// RoutingStatementHandler@Overridepublic void parameterize(Statement statement) throws SQLException {delegate.parameterize(statement);}// PreparedStatementHandler
@Overridepublic void parameterize(Statement statement) throws SQLException {parameterHandler.setParameters((PreparedStatement) statement);}//

parameterHandler.setParameters(...) ;源码如下:

遍历 sql语句中所有参数,并赋值

public void setParameters(PreparedStatement ps) {ErrorContext.instance().activity("setting parameters").object(mappedStatement.getParameterMap().getId());List<ParameterMapping> 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 paramsvalue = 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);}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);}}}

parameterMappings 是参数信息,如CUST_NUM ;

 boundsql 是预编译sql,带有问号

设置参数;

step4)handler.update() 执行具体 sql;

@Overridepublic int update(Statement statement) throws SQLException {return delegate.update(statement);}
// delegate 代理为 PreparedStatementHandler 

执行具体sql,如下:

@Overridepublic int update(Statement statement) throws SQLException {PreparedStatement ps = (PreparedStatement) statement;ps.execute();  // 执行sql int rows = ps.getUpdateCount();  // 获取更新行数 Object parameterObject = boundSql.getParameterObject();KeyGenerator keyGenerator = mappedStatement.getKeyGenerator();keyGenerator.processAfter(executor, mappedStatement, ps, parameterObject);return rows;}

mybatis-启动源码分析相关推荐

  1. 【Android 启动过程】Activity 启动源码分析 ( ActivityThread 流程分析 二 )

    文章目录 前言 一.ActivityManagerService.attachApplicationLocked 二.ActivityStackSupervisor.attachApplication ...

  2. 【Android 启动过程】Activity 启动源码分析 ( ActivityThread -> Activity、主线程阶段 二 )

    文章目录 前言 一.ActivityThread 类 handleLaunchActivity -> performLaunchActivity 方法 二.Instrumentation.new ...

  3. 【Android 启动过程】Activity 启动源码分析 ( ActivityThread -> Activity、主线程阶段 一 )

    文章目录 前言 一.ClientTransactionHandler.scheduleTransaction 二.ActivityThread.H 处理 EXECUTE_TRANSACTION 消息 ...

  4. 【Android 启动过程】Activity 启动源码分析 ( AMS -> ActivityThread、AMS 线程阶段 二 )

    文章目录 前言 一.热启动与冷启动选择 二.AMS 进程中执行的相关操作 三.通过 Binder 机制转到 ActivityThread 中执行的操作 总结 前言 上一篇博客 [Android 启动过 ...

  5. 【Android 启动过程】Activity 启动源码分析 ( AMS -> ActivityThread、AMS 线程阶段 )

    文章目录 一.Activity 启动源码分析 ( AMS | ActivityManagerService ) 1.Instrumentation 调用 AMS 方法 2.ActivityStarte ...

  6. 【Android 启动过程】Activity 启动源码分析 ( Activity -> AMS、主线程阶段 )

    文章目录 一.Activity 启动源码分析 ( Activity -> AMS 阶段 ) 一.Activity 启动源码分析 ( Activity -> AMS 阶段 ) 调用 star ...

  7. Eureka Server启动源码分析

    本文来分析下Eureka Server启动源码 文章目录 概述 源码解析 服务同步 服务剔除 start包配置 启动源码分析 EurekaServerAutoConfiguration EurekaS ...

  8. 【mybatis源码】 mybatis底层源码分析

    [mybatis源码] mybatis底层源码分析 1.测试用例 2.开撸源码 2.1 SqlSessionFactory对象的创建与获取 2.2 获取SqlSession对象 2.3 获取接口的代理 ...

  9. Android 9(P)之init进程启动源码分析指南之一

         Android 9 之init进程启动源码分析指南之一 Android 9 (P) 系统启动及进程创建源码分析目录: Android 9 (P)之init进程启动源码分析指南之一 Andro ...

  10. springboot启动源码分析3-环境配置

    applyInitializersSpringBoot启动源码分析3--环境配置 springboot启动源码分析1--初步初始化 springboot启动源码分析2--run方法分析 springb ...

最新文章

  1. c语言 百钱买百鸡问题
  2. 关于网狐棋牌6603源码的整理、编译和搭建
  3. 适用于python机器学习与实践的twenty_newsgroups.py文件内容
  4. 神话与谬误:争论C++前你应当知道什么
  5. python中jieba分词快速入门
  6. Spring Boot 2.x 注册 Servlet 三大组件 Servlet、Filter、Listener
  7. .config文件与.xml文件的关系
  8. Predator:比微软Kinect更强的视频追踪算法——来自捷克博士论文
  9. 转载--柯尔莫哥洛夫
  10. 山西省行政村边界数据/乡镇街道边界数据/行政区划边界分布
  11. vs2017安装QT开发插件
  12. 无序列表前小黑点图片替换方法
  13. cfree5.0中文版
  14. 好心情患者故事:节食暴食反复横跳,我确诊了重度抑郁
  15. 数据库事物,隔离级别慢慢深入
  16. 简单明了的普利姆算法
  17. Test failed due to unrecognized service account for this product, please submit initial report for t
  18. 手把手教你如何玩转EasyExcel的导入和导出
  19. 实现strStr()
  20. GoogLeNet: Going deeper with convolutions

热门文章

  1. 2018-2019 ICPC Northwestern European Regional Programming Contest (NWERC 2018)
  2. Acwing 273. 分级
  3. CF1183H Subsequences (hard version)
  4. CF1270H Number of Components(线段树)
  5. 新汉诺塔(洛谷P1242)含第11个数据的解决办法
  6. CF1063F-String Journey【SAM,线段树】
  7. P5287-[HNOI2019]JOJO【KMP】
  8. P3348-[ZJOI2016]大森林【LCT】
  9. CF878E-Numbers on the blackboard【并查集,贪心】
  10. POJ2228-Naptime【环形结构上的dp】