mybatis-启动源码分析
【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个参数
- mapper文件的输入流(com\swjtu\mybatis\dao\EmployeeMapper.xml文件流);
- mybatis配置对象Configuration;
- resource(sql文件名称com\swjtu\mybatis\dao\EmployeeMapper.xml);
- 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 中有两类方法包括,
- 从数据源获取会话:private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit);
- 从连接中获取会话: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 都是空的;
它的数据库连接的获取有两种方式,
- 上游给定,传入 Connection ;
- 通过 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 语句的地方,依赖的对象包括:
- configuration:根据mybatis-config.xml 解析得到的配置对象;
- StatementHandler:语句处理器;
- 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-启动源码分析相关推荐
- 【Android 启动过程】Activity 启动源码分析 ( ActivityThread 流程分析 二 )
文章目录 前言 一.ActivityManagerService.attachApplicationLocked 二.ActivityStackSupervisor.attachApplication ...
- 【Android 启动过程】Activity 启动源码分析 ( ActivityThread -> Activity、主线程阶段 二 )
文章目录 前言 一.ActivityThread 类 handleLaunchActivity -> performLaunchActivity 方法 二.Instrumentation.new ...
- 【Android 启动过程】Activity 启动源码分析 ( ActivityThread -> Activity、主线程阶段 一 )
文章目录 前言 一.ClientTransactionHandler.scheduleTransaction 二.ActivityThread.H 处理 EXECUTE_TRANSACTION 消息 ...
- 【Android 启动过程】Activity 启动源码分析 ( AMS -> ActivityThread、AMS 线程阶段 二 )
文章目录 前言 一.热启动与冷启动选择 二.AMS 进程中执行的相关操作 三.通过 Binder 机制转到 ActivityThread 中执行的操作 总结 前言 上一篇博客 [Android 启动过 ...
- 【Android 启动过程】Activity 启动源码分析 ( AMS -> ActivityThread、AMS 线程阶段 )
文章目录 一.Activity 启动源码分析 ( AMS | ActivityManagerService ) 1.Instrumentation 调用 AMS 方法 2.ActivityStarte ...
- 【Android 启动过程】Activity 启动源码分析 ( Activity -> AMS、主线程阶段 )
文章目录 一.Activity 启动源码分析 ( Activity -> AMS 阶段 ) 一.Activity 启动源码分析 ( Activity -> AMS 阶段 ) 调用 star ...
- Eureka Server启动源码分析
本文来分析下Eureka Server启动源码 文章目录 概述 源码解析 服务同步 服务剔除 start包配置 启动源码分析 EurekaServerAutoConfiguration EurekaS ...
- 【mybatis源码】 mybatis底层源码分析
[mybatis源码] mybatis底层源码分析 1.测试用例 2.开撸源码 2.1 SqlSessionFactory对象的创建与获取 2.2 获取SqlSession对象 2.3 获取接口的代理 ...
- Android 9(P)之init进程启动源码分析指南之一
Android 9 之init进程启动源码分析指南之一 Android 9 (P) 系统启动及进程创建源码分析目录: Android 9 (P)之init进程启动源码分析指南之一 Andro ...
- springboot启动源码分析3-环境配置
applyInitializersSpringBoot启动源码分析3--环境配置 springboot启动源码分析1--初步初始化 springboot启动源码分析2--run方法分析 springb ...
最新文章
- c语言 百钱买百鸡问题
- 关于网狐棋牌6603源码的整理、编译和搭建
- 适用于python机器学习与实践的twenty_newsgroups.py文件内容
- 神话与谬误:争论C++前你应当知道什么
- python中jieba分词快速入门
- Spring Boot 2.x 注册 Servlet 三大组件 Servlet、Filter、Listener
- .config文件与.xml文件的关系
- Predator:比微软Kinect更强的视频追踪算法——来自捷克博士论文
- 转载--柯尔莫哥洛夫
- 山西省行政村边界数据/乡镇街道边界数据/行政区划边界分布
- vs2017安装QT开发插件
- 无序列表前小黑点图片替换方法
- cfree5.0中文版
- 好心情患者故事:节食暴食反复横跳,我确诊了重度抑郁
- 数据库事物,隔离级别慢慢深入
- 简单明了的普利姆算法
- Test failed due to unrecognized service account for this product, please submit initial report for t
- 手把手教你如何玩转EasyExcel的导入和导出
- 实现strStr()
- GoogLeNet: Going deeper with convolutions
热门文章
- 2018-2019 ICPC Northwestern European Regional Programming Contest (NWERC 2018)
- Acwing 273. 分级
- CF1183H Subsequences (hard version)
- CF1270H Number of Components(线段树)
- 新汉诺塔(洛谷P1242)含第11个数据的解决办法
- CF1063F-String Journey【SAM,线段树】
- P5287-[HNOI2019]JOJO【KMP】
- P3348-[ZJOI2016]大森林【LCT】
- CF878E-Numbers on the blackboard【并查集,贪心】
- POJ2228-Naptime【环形结构上的dp】