免费爱碰视频在线观看,九九精品国产屋,欧美亚洲尤物久久精品,1024在线观看视频亚洲

      第02篇:Mybatis配置文件解析

      第02篇:Mybatis配置文件解析

      本篇主要內(nèi)容如下,由于頭條頁面對Markdown文檔的展示問題,所以排版可能有問題。

      一、配置文件分析

      文件分析

      在上一篇的代碼中,我們看到了一個非常重要文件,這里我們先來人肉分析看,然后看下代碼是如何解析的,畢竟代碼也是人寫的。 思路決定出路,我們?nèi)绻兴悸?然后在看源碼會更加的具有分析的能動性。

      @Test public void mapper() { // 讀取配置信息(為什么路徑前不用加/,因為是相對路徑。maven編譯后的資源文件和class文件都是在一個包下,所以不用加/就是當(dāng)前包目錄) InputStream mapperInputStream = Thread.currentThread().getContextClassLoader().getResourceAsStream(“mybatisConfig.xml”); // 生成SqlSession工廠,SqlSession從名字上看就是,跟數(shù)據(jù)庫交互的會話信息,負責(zé)將sql提交到數(shù)據(jù)庫進行執(zhí)行 SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(mapperInputStream, “development”); // 獲取Mybatis配置信息 Configuration configuration = sqlSessionFactory.getConfiguration(); // 參數(shù): autoCommit,從名字上看就是是否自動提交事務(wù) SqlSession sqlSession = sqlSessionFactory.openSession(false); // 獲取Mapper TUserMapper mapper = configuration.getMapperRegistry().getMapper(TUserMapper.class, sqlSession); TUser tUser = new TUser(); tUser.setName(“testUser1”); tUser.setTokenId(“testTokenId1”); mapper.insert(tUser); // 獲取插入的數(shù)據(jù) System.out.println(mapper.selectAll()); // 數(shù)據(jù)插入后,執(zhí)行查詢,然后回滾數(shù)據(jù) sqlSession.rollback(); }

      1.1 mybatisConfig.xml

      tip 注意看高亮行

    1. line(4) dtd文件是xml的約束文件,用于約束 xml 標(biāo)簽屬性
    2. line(8) properties標(biāo)簽,指定了配置信息文件是 application.properties
    3. line(11-13) mybatis的配置信息
    4. line(15-27) mybatis支持多環(huán)境配置
    5. line(30-32) 映射文件 :::
    6. 基于上面的行,我們來講解。

      二、知識點講解

      2.1 xml約束文件dtd

      為什么要學(xué)習(xí)dtd約束文件呢? 當(dāng)你學(xué)會dtd約束文件后,你就知道這個標(biāo)簽有那些屬性,知道標(biāo)簽及子標(biāo)簽信息。 當(dāng)有一天你要寫開源框架的時候,你也可以來定義你自己的配置文件規(guī)則。這部分知識了解就行。不需要死記硬背。 因為記住也基本沒啥用,只要做到看到了認識,需要用了知道去哪里抄代碼學(xué)習(xí)就夠了。

      2.1.1 元素 & 屬性 & 屬性值

      dtd文件

      域 示例 語法 例子 元素 聲明根元素標(biāo)簽 ,元素students有一個student 元素 空元素 元素 元素只出現(xiàn)一次 ,元素students至少有一個student 元素 元素最少出現(xiàn)一次 ,元素students最少有一個student 元素 聲明出現(xiàn)零次或多次的元素 ,元素students可以有多個student,也可以一個沒有 元素 聲明“非…/既…”類型的內(nèi)容 “ 元素 聲明混合型的內(nèi)容 `<!ELEMENT note (#PCDATA to 屬性 屬性聲明 ,payment有一個屬性type,類型為字符類型,默認值check

      值類型

      類型 描述 CDATA 值為字符數(shù)據(jù) (character data) (en1 en2 ID 值為唯一的 id IDREF 值為另外一個元素的 id IDREFS 值為其他 id 的列表 NMTOKEN 值為合法的 XML 名稱 NMTOKENS 值是一個實體 ENTITIES 值是一個實體列表 NOTATION 此值是符號的名稱 xml: 值是一個預(yù)定義的 XML 值

      默認值參數(shù)可使用下列值

      類型 描述 值 屬性的默認值 #REQUIRED 屬性值是必需的 #IMPLIED 屬性不是必需的 #FIXED value 屬性值是固定的

      2.2 configuration標(biāo)簽分析

      前面我們知道了dtd約束文件,我們就可以看下,configuration標(biāo)簽一共有那些子標(biāo)簽及屬性信息了。

      mybatis-3-config.dtd

      通過分析dtd文件,我們知道有那些子標(biāo)簽及屬性信息。內(nèi)容比較長。但是不是很重要。這里只要知道就行。

      后面我們看如何使用代碼來解析這些標(biāo)簽。

      2.3 Mybatis配置解析核心邏輯

      :::tip 思路決定出路

      • line(6) sqlSessionFactory.getConfiguration()

      由此來看所有的解析都是在SqlSessionFactoryBuilder進行完成的. 具體的解析xml代碼我們不研究,這里我們只要搞清楚它的調(diào)用關(guān)系,及實現(xiàn)的代碼在哪里即可。如果這里 看懂,其實都會得到一個結(jié)論。就是mybaits的源碼是比較簡單的,因為他的配置是比較集中的,無論是xml方式或者是注解方式。 最終所有的配置信息都在 Configuration 類中。 :::

      @Test public void configuration() { // 讀取配置信息(為什么路徑前不用加/,因為是相對路徑。maven編譯后的資源文件和class文件都是在一個包下,所以不用加/就是當(dāng)前包目錄) InputStream mapperInputStream = Thread.currentThread().getContextClassLoader().getResourceAsStream(“mybatisConfig.xml”); // 生成SqlSession工廠,SqlSession從名字上看就是,跟數(shù)據(jù)庫交互的會話信息,負責(zé)將sql提交到數(shù)據(jù)庫進行執(zhí)行 SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(mapperInputStream, “development”); // 獲取Mybatis配置信息,由此來看所有的解析都是在SqlSessionFactoryBuilder進行完成的. Configuration configuration = sqlSessionFactory.getConfiguration(); }

      2.3.1 new SqlSessionFactoryBuilder().build

      這里可以看到就是核心類就是使用 XMLConfigBuilder 進行解析。下面我們就主要分析 XMLConfigBuilder

      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. } } }

      2.3.2 核心配置類解析(XMLConfigBuilder)

      :::note 重點關(guān)注

    7. line(8), 我們看到核心解析類是 XPathParser parser = new XPathParser()
    8. line(17), 標(biāo)簽的解析都在 parseConfiguration
    9. line(17), 思考下為什么先解析 propertiesElement(root.evalNode(“properties”)) :::
    10. public class XMLConfigBuilder extends BaseBuilder { private boolean parsed; private final XPathParser parser; private String environment; private final ReflectorFactory localReflectorFactory = new DefaultReflectorFactory(); public Configuration parse() { if (parsed) { throw new BuilderException(“Each XMLConfigBuilder can only be used once.”); } parsed = true; parseConfiguration(parser.evalNode(“/configuration”)); return configuration; } private void parseConfiguration(XNode root) { try { // issue #117 read properties first propertiesElement(root.evalNode(“properties”)); Properties settings = settingsAsProperties(root.evalNode(“settings”)); loadCustomVfs(settings); loadCustomLogImpl(settings); typeAliasesElement(root.evalNode(“typeAliases”)); pluginElement(root.evalNode(“plugins”)); objectFactoryElement(root.evalNode(“objectFactory”)); objectWrapperFactoryElement(root.evalNode(“objectWrapperFactory”)); reflectorFactoryElement(root.evalNode(“reflectorFactory”)); settingsElement(settings); // read it after objectFactory and objectWrapperFactory issue #631 environmentsElement(root.evalNode(“environments”)); databaseIdProviderElement(root.evalNode(“databaseIdProvider”)); typeHandlerElement(root.evalNode(“typeHandlers”)); mapperElement(root.evalNode(“mappers”)); } catch (Exception e) { throw new BuilderException(“Error parsing SQL Mapper Configuration. Cause: ” + e, e); } }}

      看到上面代碼是不是就恍然大悟了,原來配置文件的標(biāo)簽都是在這里解析呀。這里的主要思路就是將xml解析成Java對象然后放到 Configuration中。具體任何實現(xiàn)呢? 感興趣可以自己研究下。

      2.3.3 Configuration屬性介紹

      那么這些數(shù)據(jù)最終哪里會使用呢,我們專門留一片文章, 詳細分析。這里先看看Configuration內(nèi)部都有那些關(guān)鍵的配置類把。

      屬性 解釋 TypeAliasRegistry key是一個別名,value是一個class對象 Properties variables 配置文件中占位符的變量配置 InterceptorChain interceptorChain 攔截鏈,用于攔截方法,實現(xiàn)插件 ObjectFactory objectFactory 對象實例化統(tǒng)一的工廠方法,我們不用都反射來實例化了 ObjectWrapperFactory objectWrapperFactory 包裝對象后為其提供統(tǒng)一的屬性操作方法。我們不用通過反射來修改對象屬性值了 ReflectorFactory reflectorFactory 反射工廠,用于生成一個反射信息對象,具有緩存的作用 Environment environment 環(huán)境信息包含(事務(wù)管理器和數(shù)據(jù)源) TypeHandlerRegistry typeHandlerRegistry 主要處理jdbc的返回數(shù)據(jù),轉(zhuǎn)換成Java對象 MapperRegistry mapperRegistry Mapper生成的處理類,包含代理的邏輯

      2.3.4 Mapper.xml 解析

      XMLMapperBuilder

      解析Mapper對應(yīng)的xml配置文件,這里面包含了sql的信息。

      mapper的dtd約束文件更多,可以參考: https://mybatis.org/mybatis-3/zh/sqlmap-xml.html#

      這里就要介紹一個重要的類的,MapperBuilderAssistant Mapper構(gòu)建輔助工具類。

      屬性 解釋 MapperBuilderAssistant Mapper構(gòu)建輔助工具類(緩存配置) CacheRefResolver 決定如何使用緩存 ParameterMapping 當(dāng)sql中使用到了#{}占位符時候,負責(zé)填充sql參數(shù) ResultMapResolver 返回值映射 Map sqlFragments sql片段 MappedStatement Mapper方法的所有信息(出參,入?yún)?,及sql信息等)

      2.4 Mybatis可以借鑒的知識點

      2.4.1 占位符解析邏輯

      在第一篇的時候我們說過,從配置文件解析中我們能學(xué)會,如果解析占位符。并將占位符填充真實數(shù)據(jù)。這里我們就具體說下是如何解析。 還記得前面讓思考下為什么先解析 propertiesElement(root.evalNode(“properties”))。

      答案就是為了先讀取變量信息,方便后面給依賴的信息,給填充值。

      我們直接說答案: 具體誰來做了這個事情,從職責(zé)劃分上來看,這個其實還是屬于xml文件解析。所以是 XPathParser parser XPathParser中填充上變量信息,這樣XPathParser在解析的時候會自動將 ${} 填充上真實的數(shù)據(jù)。

      // 執(zhí)行后,會解析properties標(biāo)簽,并且將屬性賦值給XPathParser propertiesElement(root.evalNode(“properties”)); parser.setVariables(defaults); configuration.setVariables(defaults); // XPathParser 生成節(jié)點時候,屬性信息會提前處理。 public XNode(XPathParser xpathParser, Node node, Properties variables) { this.xpathParser = xpathParser; this.node = node; this.name = node.getNodeName(); this.variables = variables; this.attributes = parseAttributes(node); this.body = parseBody(node); } // 發(fā)現(xiàn)是占位符,就從變量中讀取。 // ${datasource.driver-class-name} 替換成變量值里面的數(shù)據(jù)。 public static String parse(String string, Properties variables) { VariableTokenHandler handler = new VariableTokenHandler(variables); GenericTokenParser parser = new GenericTokenParser(“${“, “}”, handler); return parser.parse(string); }

      2.4.2 Mybatis Resources 工具

      可以從配置文件中或者網(wǎng)絡(luò)中解析配置,生成 Resources 對象

      String resource = context.getStringAttribute(“resource”); if (resource != null) { defaults.putAll(Resources.getResourceAsProperties(resource)); } else if (url != null) { defaults.putAll(Resources.getUrlAsProperties(url)); } parser.setVariables(defaults); configuration.setVariables(defaults); // 從資源中獲取流 InputStream inputStream = Resources.getResourceAsStream(resource) // 從url中獲取流 InputStream inputStream = Resources.getUrlAsStream(url)

      2.4.3 Mybatis PropertyParser 占位符解析

      @Test public void propertyParser() { Properties variables = new Properties(); variables.put(“datasource.driver-class-name”, “com.mysql.cj.jdbc.Driver”); // 變量中有就從變量中獲取 參數(shù)信息: com.mysql.cj.jdbc.Driver System.out.println(PropertyParser.parse(“參數(shù)信息: ${datasource.driver-class-name}”, variables)); // 變量中沒有就直接返回key datasource.url System.out.println(PropertyParser.parse(“datasource.url”, variables)); }

      2.4.4 反射工廠 ReflectorFactory

      在Mybatis中使用到的反射地方蠻多的,那么都知道反射是相對比較耗時間,那么我們來看Mybatis是如何利用反射工廠來提高反射的性能的?

      緩存,對要使用的Class類,做反射并保存起來, 生成的對象是 Reflector。

      ReflectorFactory reflectorFactory = new DefaultReflectorFactory();

      public interface ReflectorFactory { boolean isClassCacheEnabled(); void setClassCacheEnabled(boolean classCacheEnabled); Reflector findForClass(Class type);}public class Reflector { private final Class type; private final String[] readablePropertyNames; private final String[] writablePropertyNames; private final Map setMethods = new HashMap(); private final Map getMethods = new HashMap(); private final Map setTypes = new HashMap(); private final Map getTypes = new HashMap(); private Constructor defaultConstructor; private Map caseInsensitivePropertyMap = new HashMap();} @Test public void reflector() throws Exception { ReflectorFactory reflectorFactory = new DefaultReflectorFactory(); Reflector forClass = reflectorFactory.findForClass(TUser.class); TUser user = (TUser) forClass.getDefaultConstructor().newInstance(); forClass.getSetInvoker(“uid”).invoke(user, new Object[]{1}); forClass.getSetInvoker(“name”).invoke(user, new Object[]{“孫悟空”}); forClass.getSetInvoker(“tokenId”).invoke(user, new Object[]{“tokenId”}); // 1 System.out.println(forClass.getGetInvoker(“uid”).invoke(user, new Object[]{})); // 孫悟空 System.out.println(forClass.getGetInvoker(“name”).invoke(user, new Object[]{})); }

      2.4.5 異常上下文設(shè)計 ErrorContext

    11. 在代碼執(zhí)行的過程中,將關(guān)鍵信息通過 ErrorContext.instance().message() 保存進去。利用到了線程隔離的知識。
    12. ErrorContext.instance() 是利用 ThreadLocal 進行線程隔離。
    13. 異常打印后,進行 reset 重置。
    14. public int update(String statement, Object parameter) { try { dirty = true; MappedStatement ms = configuration.getMappedStatement(statement); return executor.update(ms, wrapCollection(parameter)); } catch (Exception e) { throw wrapException(“Error updating database. Cause: ” + e, e); } finally { // 完成之后異常上下文進行重置 ErrorContext.instance().reset(); } } // 將異常上線文中報錯的錯誤都打印出來。 public static RuntimeException wrapException(String message, Exception e) { return new PersistenceException(ErrorContext.instance().message(message).cause(e).toString(), e); }

      鄭重聲明:本文內(nèi)容及圖片均整理自互聯(lián)網(wǎng),不代表本站立場,版權(quán)歸原作者所有,如有侵權(quán)請聯(lián)系管理員(admin#wlmqw.com)刪除。
      用戶投稿
      上一篇 2022年7月11日 09:35
      下一篇 2022年7月11日 12:05

      相關(guān)推薦

      聯(lián)系我們

      聯(lián)系郵箱:admin#wlmqw.com
      工作時間:周一至周五,10:30-18:30,節(jié)假日休息