1. 程式人生 > >mybatis3.4.6 原始碼分析筆記

mybatis3.4.6 原始碼分析筆記

目錄:從使用開始,一些常用功能,梳理一下程式碼呼叫流程,整理一些功能的實現,值得學習參考的地方,最後對於框架中不理解的地方提出的疑問

資源:https://download.csdn.net/download/u013523089/10781601

歡迎大家在評論中指出錯誤,有什麼問題也可以共同交流共同學習,感謝!

網配置教程:
http://www.mybatis.org/mybatis-3/
原始碼下載:
在這裡插入圖片描述

ORM(來自baidu):
物件關係對映(英語:(Object Relational Mapping,簡稱ORM,或O/RM,或O/R mapping),是一種程式技術,用於實現面向物件程式語言裡不同型別系統的資料之間的轉換

自我梳理了一下mybatis,歡迎大神批評指正,交流學習,感謝!

常用使用說明

  1. 使用mybatis整合pom配置
    使用我的練習專案;第一次自己搭maven
    在這裡插入圖片描述
    可以在一個maven工程裡放子工程,這個方式還挺好的;兩個子專案可以共用父專案的pom配置


	<dependencies>
		<!-- mysql -->
		<dependency>
			<groupId>mysql</groupId>
			<artifactId>mysql-connector-java</artifactId>
			<version>8.0.13</version>
		</dependency>
		
		<!-- mybatis -->
		<dependency>
		  <groupId>org.mybatis</groupId>
		  <artifactId>mybatis</artifactId>
		  <version>3.4.6</version>
		</dependency>
		
		<!-- log4j -->
		<dependency>
            <groupId>org.apache.logging.log4j</groupId>
            <artifactId>log4j-core</artifactId>
            <version>${log4j2.version}</version>
        </dependency>
        <dependency>
            <groupId>org.apache.logging.log4j</groupId>
            <artifactId>log4j-api</artifactId>
            <version>${log4j2.version}</version>
        </dependency>
        
        <!-- mybatis會用到 start -->
        <dependency>
		    <groupId>org.javassist</groupId>
		    <artifactId>javassist</artifactId>
		    <version>3.21.0-GA</version>
		</dependency>
		<!-- https://mvnrepository.com/artifact/ognl/ognl -->
		<dependency>
		    <groupId>ognl</groupId>
		    <artifactId>ognl</artifactId>
		    <version>2.7.3</version>
		</dependency>
		<!-- mybatis會用到 end -->
		
		<!-- mybatis 分頁外掛 -->
		<!-- https://mvnrepository.com/artifact/com.github.pagehelper/pagehelper -->
		<dependency>
		    <groupId>com.github.pagehelper</groupId>
		    <artifactId>pagehelper</artifactId>
		    <version>5.1.7</version>
		</dependency>
		
	</dependencies>

maven工程配置,pom檔案配置;如果需要jar包可以在這裡搜
https://mvnrepository.com/artifact/org.junit.jupiter/junit-jupiter-api/5.3.1

  1. 使用generator外掛使用
    pom配置
  <build>
  	<plugins>
        <plugin>
            <groupId>org.mybatis.generator</groupId>
            <artifactId>mybatis-generator-maven-plugin</artifactId>
            <version>1.3.7</version>
             <configuration>
             	<!--允許移動生成的檔案-->
                <verbose>true</verbose>
                <!--允許覆蓋生成的檔案-->
                <overwrite>true</overwrite>
                <configurationFile>src/main/resources/config/generatorConfig.xml</configurationFile>
            </configuration>
        </plugin>
    </plugins>
  </build>

generator.xml配置

<?xml version="1.0" encoding="UTF-8" ?>

    <!DOCTYPE generatorConfiguration 
      PUBLIC "-//mybatis.org//DTD MyBatis Generator Configuration 1.0//EN"
      "http://mybatis.org/dtd/mybatis-generator-config_1_0.dtd">
     
      <!-- 注意這個外掛是增量向檔案中新增,會重複 -->
     
    <generatorConfiguration>
   
    
        <!-- 引入配置檔案   
    <properties resource="jdbc.properties"/>  -->
    
         <!--資料庫驅動路徑-->
      <classPathEntry location="D:\TSBrowserDownloads\maven\mavenSource\mysql\mysql-connector-java\8.0.13\mysql-connector-java-8.0.13.jar" />
    <context id="DB2Tables" targetRuntime="MyBatis3">
      <commentGenerator>
        <property name="suppressAllComments" value="true"/><!-- 是否取消註釋 -->  
        <property name="suppressDate" value="true" /> <!-- 是否生成註釋代時間戳-->
      </commentGenerator>
      <jdbcConnection driverClass="com.mysql.cj.jdbc.Driver"
            connectionURL="jdbc:mysql://localhost:3306/zhaowd?serverTimezone=GMT"
            userId="root" password="12345">
        </jdbcConnection>
        <!-- 型別轉換 -->
      <javaTypeResolver>
      <!-- 是否使用bigDecimal, false可自動轉化以下型別(Long, Integer, Short, etc.) --> 
         <property name="forceBigDecimals" value="false"/>
      </javaTypeResolver>
      <!-- 生成實體類的包名和位置 注意targetProject的值為實體類放在工程中具體位置的相對路徑,-->  
      <javaModelGenerator targetPackage="zhaowd.source" targetProject="src/main/java">
      <!-- 是否在當前路徑下新加一層schema,eg:fase路徑com.oop.eksp.user.model, true:com.oop.eksp.user.model.[schemaName] -->
          <property name="enableSubPackages" value="true"/>
           <!-- 是否針對string型別的欄位在set的時候進行trim呼叫 -->  
          <property name="trimStrings" value="true"/>
      </javaModelGenerator>
       <!--XML對映檔案,生成的位置(目標包),原始碼資料夾-->
      <sqlMapGenerator targetPackage="sqlmap" targetProject="src/main/resources">
           <property name="enableSubPackages" value="true"/>
      </sqlMapGenerator>
       <!--XML對應的Mapper類-->
      <javaClientGenerator type="XMLMAPPER" targetPackage="zhaowd.mapper" targetProject="src/main/java">
        <property name="enableSubPackages" value="true"/>
      </javaClientGenerator>
      
         <!--下面是資料庫表名和專案中需要生成類的名稱,建議和資料庫保持一致,如果有多個表,新增多個節點即可   enable*ByExample   
                是否生成 example類-->
      <table schema="zhaowd" tableName="user_info" domainObjectName="UserInfo" enableCountByExample="false" enableSelectByExample="true" enableUpdateByExample="false" enableDeleteByExample="false">
       
      </table>
    </context>
      
    </generatorConfiguration>

使用方法:右鍵專案–>run as–>maven Build
自動生成mybatis需要的內容,mapper介面,mapper.xml,表實體
預設直接覆蓋原有的,所以儘量不要在生成的檔案中編寫自己的程式碼,使用單獨的檔案,防止覆蓋

  1. 分頁
    mybatis自帶方法裡的分頁引數是對結果集進行分頁的,就是假分頁
    方法1:就是使用sql語句的拼裝去分頁,直接使用資料庫的sql
    方法2:使用分頁外掛,比如pagehelper
    • pom配置見上面mybatis的配置
    • 使用說明:再查詢之前增加PageHelper.startPage(當前頁,限制數量);即可
    • 注意事項見圖片程式碼註釋
      在這裡插入圖片描述

使用過程中的異常

  1. Caused by: java.lang.IllegalStateException: Cannot enable lazy loading because Javassist is not available. Add Javassist to your classpath.
Caused by: java.lang.IllegalStateException: Cannot enable lazy loading because Javassist is not available. Add Javassist to your classpath.
	at org.apache.ibatis.executor.loader.javassist.JavassistProxyFactory.<init>(JavassistProxyFactory.java:55)
	at org.apache.ibatis.session.Configuration.<init>(Configuration.java:131)
	at org.apache.ibatis.builder.xml.XMLConfigBuilder.<init>(XMLConfigBuilder.java:86)
	at org.apache.ibatis.builder.xml.XMLConfigBuilder.<init>(XMLConfigBuilder.java:82)
	at org.apache.ibatis.session.SqlSessionFactoryBuilder.build(SqlSessionFactoryBuilder.java:77)
	... 25 more
Caused by: java.lang.ClassNotFoundException: Cannot find class: javassist.util.proxy.ProxyFactory

缺少jar依賴

        <dependency>
		    <groupId>org.javassist</groupId>
		    <artifactId>javassist</artifactId>
		    <version>3.21.0-GA</version>
		</dependency>
  1. Caused by: java.lang.RuntimeException: MemberAccess implementation must be provided!
    ognl版本不對,可能太高了
修改成:
		<dependency>
		    <groupId>ognl</groupId>
		    <artifactId>ognl</artifactId>
		    <version>2.7.3</version>
		</dependency>

原始碼分析

程式呼叫鏈條如下圖所示:

1.流程呼叫圖(簡化版)
在這裡插入圖片描述

2.複雜點的(這有點亂,結合簡化版的看)
在這裡插入圖片描述

  1. 目錄結構說明
    annotations:註解定義
    binding:繫結mapper介面與mapper.xml的關係
    builder:註解+配置解析
    cache:快取
    cursor:
    datasource:資料來源
    exceptions:異常
    executor:sql的實際執行者
    io:讀檔案
    jdbc:底層相關
    lang:
    logging:日誌
    mappering:對映到實體
    parsing:解析配置+註解
    plugin:外掛,攔截器
    reflection:反射
    scripting:引數處理
    session:session
    transaction:
    type:型別

原始碼

原始碼的呼叫流程見上圖

學習:

  1. Configuration
    目前mybatis支援兩種方式的實現功能
    • 程式設計式:只是單純的使用mybaits去作為持久化層去使用,mybatis提供xml的形式去簡化配置
    • 整合式:比如整合在spring中使用,可以使用Configuration去設定配置引數
      程式設計式在使用過程中,也會將xml對映到Configuration上
      Configuration與SqlSessionFactory一一對應
  2. 快取
    1. 一級快取:預設開啟,session級別的,第一查詢進行快取,第二次查詢從快取裡取

      • 如果同一個sesssion,在兩次查詢之間有更新資料的操作(update,delete)並提交,session的所有快取都會清
      • 如果有不同的session,在兩次查詢之間有更新資料的操作(update,delete),第二次查詢的資料就是髒資料了,session之間快取都是獨立的
        一級快取是在取二級快取之後(如果二級開關開啟的話)
        在這裡插入圖片描述

      設定一級快取的時候會使用一個佔位符去佔位,作用:比如在聯合查詢中,第二個sql在執行時,可能會命中第二個sql在單獨執行時的一級快取,如果獲取到快取的值是這個佔位符,表示這個快取不能使用;應該就是這個聯合查詢的第二個sql不載入到快取裡,由正在處理的加

      在這裡插入圖片描述

    2. 二級快取:預設關閉,一般使用第三方實現(redies),nameSpace級別的,共享給所有的sqlSession

      • 同一個namespace下的upd,del會清掉該space下的快取同一個namespace下的upd,del會清掉該space下的快取
      • 如果是關聯查詢,快取也只是當前namespace下的;如果另外一個namespace對資料有更新,不會影響其他namespace下的快取,哪怕是關聯查詢的第二條sql
        在這裡插入圖片描述

在這裡插入圖片描述

  1. plugin機制 攔截

    • InterceptorChain.pluginAll方法,攔截器的開始;攔截的內容就是下圖中的類,所有類的建立都會呼叫這個方法
      在這裡插入圖片描述
    • 攔截器的過程
    • 如果有多個plugin,pluginAll裡會迴圈的去做代理;即已經被代理的例項,可以再被代理;代理先走最外層的
      在這裡插入圖片描述

    重複代理示例:

    1. 初始狀態
      在這裡插入圖片描述
    2. 第一次被代理
      在這裡插入圖片描述
    3. 第二次被代理
      在這裡插入圖片描述
  2. ObjectFactory 建立結果物件的例項

    • 拿到結果集,建立結果對映物件,就是實體物件(通過反射呼叫無參構造,如有自定義objectFactory,呼叫create建立)之後;這個時候還沒有對映
    • 入參設定同理
  3. laze Loading機制
    *
    在這裡插入圖片描述

    • 如果lazyLoading開啟,預設是javassist,可選cglib/自定義;會通過config去建立一個proxyFactory(java、cglib)的代理的代理物件
      位置:org.apache.ibatis.executor.resultset.DefaultResultSetHandler.createResultObject(ResultSetWrapper, ResultMap, ResultLoaderMap, String)
    • 在使用這個物件做get的時候,會呼叫invoke、intercept方法懶載入查詢內容
      在這裡插入圖片描述
      擴充套件反射知識:https://blog.csdn.net/u013523089/article/details/83959984
  4. 異常機制
    使用一個ThreadLocal LOCAL去存每個執行緒的異常資訊,如果有異常輸出,這個會打印出每個設定的呼叫步驟的資訊
    在這裡插入圖片描述

  5. log日誌:以log4j2為例

  6. 連線池:自己做連線池的時候可以有個參考
    在這裡插入圖片描述

  7. 事務
    sqlsession–>executor–>transaction{jdbc(使用jdbc的事務,connection),manager(不處理事務,用於整合,由spring管理),自定義}

問題:

  1. 這個連線超時的含義是什麼? sql執行時間過長的限制? 連線超時被斷開?
    後者的可能性好像大些