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,歡迎大神批評指正,交流學習,感謝!
常用使用說明
- 使用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
- 使用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,表實體
預設直接覆蓋原有的,所以儘量不要在生成的檔案中編寫自己的程式碼,使用單獨的檔案,防止覆蓋
- 分頁
mybatis自帶方法裡的分頁引數是對結果集進行分頁的,就是假分頁
方法1:就是使用sql語句的拼裝去分頁,直接使用資料庫的sql
方法2:使用分頁外掛,比如pagehelper- pom配置見上面mybatis的配置
- 使用說明:再查詢之前增加PageHelper.startPage(當前頁,限制數量);即可
- 注意事項見圖片程式碼註釋
使用過程中的異常
- 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>
- 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.複雜點的(這有點亂,結合簡化版的看)
- 目錄結構說明
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:型別
原始碼
原始碼的呼叫流程見上圖
學習:
- Configuration
目前mybatis支援兩種方式的實現功能- 程式設計式:只是單純的使用mybaits去作為持久化層去使用,mybatis提供xml的形式去簡化配置
- 整合式:比如整合在spring中使用,可以使用Configuration去設定配置引數
程式設計式在使用過程中,也會將xml對映到Configuration上
Configuration與SqlSessionFactory一一對應
- 快取
-
一級快取:預設開啟,session級別的,第一查詢進行快取,第二次查詢從快取裡取
- 如果同一個sesssion,在兩次查詢之間有更新資料的操作(update,delete)並提交,session的所有快取都會清
- 如果有不同的session,在兩次查詢之間有更新資料的操作(update,delete),第二次查詢的資料就是髒資料了,session之間快取都是獨立的
一級快取是在取二級快取之後(如果二級開關開啟的話)
設定一級快取的時候會使用一個佔位符去佔位,作用:比如在聯合查詢中,第二個sql在執行時,可能會命中第二個sql在單獨執行時的一級快取,如果獲取到快取的值是這個佔位符,表示這個快取不能使用;應該就是這個聯合查詢的第二個sql不載入到快取裡,由正在處理的加
-
二級快取:預設關閉,一般使用第三方實現(redies),nameSpace級別的,共享給所有的sqlSession
- 同一個namespace下的upd,del會清掉該space下的快取同一個namespace下的upd,del會清掉該space下的快取
- 如果是關聯查詢,快取也只是當前namespace下的;如果另外一個namespace對資料有更新,不會影響其他namespace下的快取,哪怕是關聯查詢的第二條sql
-
-
plugin機制 攔截
- InterceptorChain.pluginAll方法,攔截器的開始;攔截的內容就是下圖中的類,所有類的建立都會呼叫這個方法
- 攔截器的過程
- 如果有多個plugin,pluginAll裡會迴圈的去做代理;即已經被代理的例項,可以再被代理;代理先走最外層的
重複代理示例:
- 初始狀態
- 第一次被代理
- 第二次被代理
- InterceptorChain.pluginAll方法,攔截器的開始;攔截的內容就是下圖中的類,所有類的建立都會呼叫這個方法
-
ObjectFactory 建立結果物件的例項
- 拿到結果集,建立結果對映物件,就是實體物件(通過反射呼叫無參構造,如有自定義objectFactory,呼叫create建立)之後;這個時候還沒有對映
- 入參設定同理
-
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
- 如果lazyLoading開啟,預設是javassist,可選cglib/自定義;會通過config去建立一個proxyFactory(java、cglib)的代理的代理物件
-
異常機制
使用一個ThreadLocal LOCAL去存每個執行緒的異常資訊,如果有異常輸出,這個會打印出每個設定的呼叫步驟的資訊
-
log日誌:以log4j2為例
-
連線池:自己做連線池的時候可以有個參考
-
事務
sqlsession–>executor–>transaction{jdbc(使用jdbc的事務,connection),manager(不處理事務,用於整合,由spring管理),自定義}
問題:
- 這個連線超時的含義是什麼? sql執行時間過長的限制? 連線超時被斷開?
後者的可能性好像大些