日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

mybatis-启动源码分析

發布時間:2023/12/3 编程问答 20 豆豆
生活随笔 收集整理的這篇文章主要介紹了 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-启动源码分析的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。