<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          一周學完MyBatis源碼,萬字總結(jié)

          共 9528字,需瀏覽 20分鐘

           ·

          2021-11-18 08:18

          點擊下方“IT牧場”,選擇“設為星標”

          之前,我給大家分享給很多MyBatis源碼分析的一系列文章。今天,就自己的感受來做一個整體的總結(jié)。?

          眾所周知,MyBatis是對JDBC進行封裝而成的產(chǎn)品,所以,聊MyBatis源碼之前我們得先了解JDBC

          JDBC

          JDBC案例:

          public?class?JdbcDemo?{
          ????public?static?final?String?URL?=?"jdbc:mysql://localhost:3306/mblog";
          ????public?static?final?String?USER?=?"root";
          ????public?static?final?String?PASSWORD?=?"123456";

          ????public?static?void?main(String[]?args)?throws?Exception?{?
          ????????Class.forName("com.mysql.jdbc.Driver");?
          ????????Connection?conn?=?DriverManager.getConnection(URL,?USER,?PASSWORD);?
          ????????Statement?stmt?=?conn.createStatement();?
          ????????ResultSet?rs?=?stmt.executeQuery("SELECT?id,?name,?age?FROM?m_user?where?id?=1");?
          ????????while(rs.next()){
          ????????????System.out.println("name:?"+rs.getString("name")+"?年齡:"+rs.getInt("age"));
          ????????}
          ????}
          }

          說明:

          數(shù)據(jù)庫驅(qū)動:

          Class.forName("com.mysql.jdbc.Driver");?

          獲取連接:

          Connection?conn?=?DriverManager.getConnection(URL,?USER,?PASSWORD);?

          創(chuàng)建Statement或者PreparedStatement對象:

          Statement?stmt?=?conn.createStatement();?

          執(zhí)行sql數(shù)據(jù)庫查詢:

          ResultSet?rs?=?stmt.executeQuery("SELECT?id,?name,?age?FROM?m_user?where?id?=1");?

          解析結(jié)果集:

          System.out.println("name:?"+rs.getString("name")+"?年齡:"+rs.getInt("age"));

          在使用的時候,業(yè)務處理完成后記得關(guān)閉相關(guān)資源

          使用過JDCB的朋友都知道,JDBC如果用到我們項目中基本上都會存在以下幾個問題:

          傳統(tǒng)JDBC的問題

          • 創(chuàng)建數(shù)據(jù)庫的連接存在大量的硬編碼,
          • 執(zhí)行statement時存在硬編碼.
          • 頻繁的開啟和關(guān)閉數(shù)據(jù)庫連接,會嚴重影響數(shù)據(jù)庫的性能,浪費數(shù)據(jù)庫的資源.
          • 存在大量的重復性編碼

          針對上面這些問題,于是一大堆持久化框架應運而生。

          持久化框

          做持久層的框架有很多,有orm系和utils系列。

          • orm系列的代表有:hibernateeclipseLinktopLink
          • utils系列的代表有:MyBatisdbUtilsjdbcTemplate等;

          下面對于這些框架做個簡單概述:

          至于 jpa,它只是一個規(guī)范標準,并非具體框架,不等同于 spring-data-jpa;同時 spring-data-jpa 也不是 jpa 的具體實現(xiàn),它只是 jpa 規(guī)范標準的進一步封裝。hibernate 是 jpa 最為常見的實現(xiàn)框架,當然其他還有 eclipseLink,topLink。

          MyBatis 的特點是在對 SQL 優(yōu)化時,復雜 SQL 的優(yōu)化可控性高,框架內(nèi)部調(diào)用層次簡單,除了部分可以自動生成代碼,還會有很多 SQL 需要自行編碼。spring-data-jpa(hibernate) 的特點是在開發(fā)過程中,脫離 SQL 編碼開發(fā),當然也支持本地SQL來查詢,框架內(nèi)部調(diào)用層次復雜。以上就可以根據(jù)實際的業(yè)務進度和業(yè)務支撐情況做出選擇了。

          其實可以在一個項目在同時支持 MyBatis 和 spring-data-jpa,復雜SQL走 mybatis,常用SQL走 spring-data-jpa。

          鑒于前實際開發(fā)中使用數(shù)量,我們選擇MyBatis 進行分析。

          mybatis

          新加入開發(fā)的朋友,估計不知道MyBatis 的前身,在2010年之前,不交MyBatis ,叫ibatis

          MyBatis 是一款優(yōu)秀的持久層框架,它支持定制化 SQL、存儲過程以及高級映射。MyBatis 避免了幾乎所有的 JDBC 代碼和手動設置參數(shù)以及獲取結(jié)果集。MyBatis 可以使用簡單的 XML 或注解來配置和映射原生信息,將接口和 Java 的 POJOs(Plain Ordinary Java Object,普通的 Java對象)映射成數(shù)據(jù)庫中的記錄 。

          特點

          • 簡單易學:本身就很小且簡單。沒有任何第三方依賴,最簡單安裝只要兩個jar文件+配置幾個sql映射文件易于學習,易于使用,通過文檔和源代碼,可以比較完全的掌握它的設計思路和實現(xiàn)。
          • 靈活:MyBatis 不會對應用程序或者數(shù)據(jù)庫的現(xiàn)有設計強加任何影響。sql寫在xml里,便于統(tǒng)一管理和優(yōu)化。通過sql語句可以滿足操作數(shù)據(jù)庫的所有需求。
          • 解除sql與程序代碼的耦合:通過提供DAO層,將業(yè)務邏輯和數(shù)據(jù)訪問邏輯分離,使系統(tǒng)的設計更清晰,更易維護,更易單元測試。sql和代碼的分離,提高了可維護性。
          • 提供映射標簽,支持對象與數(shù)據(jù)庫的orm字段關(guān)系映射
          • 提供對象關(guān)系映射標簽,支持對象關(guān)系組建維護
          • 提供xml標簽,支持編寫動態(tài)sql。

          案例

          需要來源兩個jar包:MyBatis的jar包和MySQL數(shù)據(jù)庫連接jar包。

          <dependency>
          ??<groupId>org.mybatisgroupId>
          ??<artifactId>mybatisartifactId>
          ??<version>x.x.xversion>
          dependency>
          <dependency>
          ???<groupId>mysqlgroupId>
          ???<artifactId>mysql-connector-javaartifactId>
          ???<version>8.0.16version>
          dependency>

          創(chuàng)建一個表t_user(數(shù)據(jù)庫也是肯定要自己創(chuàng)建的哈)

          ?CREATE?TABLE?`t_user`?(
          ??????`id`?int(11)?NOT?NULL?AUTO_INCREMENT,
          ??????`name`?varchar(255)?DEFAULT?NULL,
          ??????`age`?int(11)?DEFAULT?NULL,
          ??????PRIMARY?KEY?(`id`)
          )?ENGINE=InnoDB?DEFAULT?CHARSET=utf8mb4;

          插入一條數(shù)據(jù):

          INSERT?INTO?`t_user`?VALUES?('1',?'tian',?'19',?'1');

          創(chuàng)建該數(shù)據(jù)庫表的實體類:

          public?class?User?{
          ????private?Integer?id;
          ????private?String?name;
          ????private?Integer?age;
          ????//set?get
          }

          創(chuàng)建mapper配置文件:UserMapper.xml


          mapper
          ????????PUBLIC?"-//mybatis.org//DTD?Mapper?3.0//EN"
          ????????"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
          <mapper?namespace="com.tian.mapper.UserMapper">
          ????<select?id="selectUserById"?resultType="com.tian.domain.User">
          ????????select?*?from?t_user?where?id?=?#{id}
          ????select>
          mapper>

          創(chuàng)建mapper接口:UserMapper.java

          import?com.tian.domain.User;

          public?interface?UserMapper?{
          ????User?selectUserById(Integer?id);
          }

          MyBatis 整體配置文件:


          configuration
          ????????PUBLIC?"-//mybatis.org//DTD?Config?3.0//EN"
          ????????"http://mybatis.org/dtd/mybatis-3-config.dtd">
          <configuration>
          ????<environments?default="development">
          ????????<environment?id="development">
          ????????????<transactionManager?type="JDBC"/>
          ????????????<dataSource?type="POOLED">
          ????????????????<property?name="driver"?value="com.mysql.cj.jdbc.Driver"/>
          ????????????????<property?name="url"?value="jdbc:mysql://localhost:3306/mblog?useUnicode=true&characterEncoding=utf8&autoReconnect=true&useSSL=false&serverTimezone=UTC"/>
          ????????????????<property?name="username"?value="root"/>
          ????????????????<property?name="password"?value="123456"/>
          ????????????dataSource>
          ????????environment>
          ????environments>
          ????<mappers>
          ????????<mapper?resource="mappers/UserMapper.xml"/>
          ????mappers>
          configuration>

          上面這些就是我們使用MyBatis基本開發(fā)代碼。

          下面我們來寫一個測試類:

          import?com.tian.domain.User;
          import?com.tian.mapper.UserMapper;
          import?org.apache.ibatis.io.Resources;
          import?org.apache.ibatis.session.SqlSession;
          import?org.apache.ibatis.session.SqlSessionFactory;
          import?org.apache.ibatis.session.SqlSessionFactoryBuilder;

          import?java.io.IOException;
          import?java.io.InputStream;

          public?class?MybatisApplication?{

          ????public?static?void?main(String[]?args)?{
          ????????String?resource?=?"mybatis-config.xml";
          ????????InputStream?inputStream?=?null;
          ????????SqlSession?sqlSession?=null;
          ????????try?{
          ????????????//讀取配置文件
          ????????????inputStream?=?Resources.getResourceAsStream(resource);
          ????????????//創(chuàng)建SqlSession工廠
          ????????????SqlSessionFactory?sqlSessionFactory?=?new?SqlSessionFactoryBuilder().build(inputStream);
          ????????????//創(chuàng)建sql操作會話
          ????????????sqlSession?=?sqlSessionFactory.openSession();
          ????????????UserMapper?userMapper=sqlSession.getMapper(UserMapper.class);
          ????????????//獲取數(shù)據(jù)并解析成User對象
          ????????????User?user?=?userMapper.selectUserById(1);
          ????????????//輸出
          ????????????System.out.println(user);
          ????????}?catch?(Exception?e)?{
          ????????????e.printStackTrace();
          ????????}finally?{
          ????????????//關(guān)閉相關(guān)資源
          ????????????try?{
          ????????????????inputStream.close();
          ????????????}?catch?(IOException?e)?{
          ????????????????e.printStackTrace();
          ????????????}
          ????????????sqlSession.close();
          ????????}
          ????}

          }

          測試結(jié)果:

          User{id=1, name='tian', age=19}

          如上面的代碼所示,SqlSession是MyBatis中提供的與數(shù)據(jù)庫交互的接口,SqlSession實例通過工廠模式創(chuàng)建。

          為了創(chuàng)建SqlSession對象,首先需要創(chuàng)建SqlSessionFactory對象,而SqlSessionFactory對象的創(chuàng)建依賴于SqlSessionFactoryBuilder類,該類提供了一系列重載的build()方法,我們需要以主配置文件的輸入流作為參數(shù)調(diào)用SqlSessionFactoryBuilder對象的bulid()方法,該方法返回一個SqlSessionFactory對象。

          有了SqlSessionFactory對象之后,調(diào)用SqlSessionFactory對象的openSession()方法即可獲取一個與數(shù)據(jù)庫建立連接的SqlSession實例。

          前面我們定義了UserMapper接口,這里需要調(diào)用SqlSessiongetMapper()方法創(chuàng)建一個動態(tài)代理對象,然后調(diào)用UserMapper代理實例的方法即可完成與數(shù)據(jù)庫的交互。

          針對上面這個案例,我們來梳理一下MyBatis的整體執(zhí)行流程和核心組件。

          MyBatis 核心組件

          MyBatis 執(zhí)行流程


          Configuration

          用于描述MyBatis的主配置信息,其他組件需要獲取配置信息時,直接通過Configuration對象獲取。除此之外,MyBatis在應用啟動時,將Mapper配置信息、類型別名、TypeHandler等注冊到Configuration組件中,其他組件需要這些信息時,也可以從Configuration對象中獲取。

          MappedStatement

          MappedStatement用于描述Mapper中的SQL配置信息,是對Mapper XML配置文件中等標簽或者@Select/@Update等注解配置信息的封裝。

          SqlSession

          SqlSession是MyBatis提供的面向用戶的API,表示和數(shù)據(jù)庫交互時的會話對象,用于完成數(shù)據(jù)庫的增刪改查功能。SqlSession是Executor組件的外觀,目的是對外提供易于理解和使用的數(shù)據(jù)庫操作接口。

          Executor

          Executor是MyBatis的SQL執(zhí)行器,MyBatis中對數(shù)據(jù)庫所有的增刪改查操作都是由Executor組件完成的。

          StatementHandler

          StatementHandler封裝了對JDBC Statement對象的操作,比如為Statement對象設置參數(shù),調(diào)用Statement接口提供的方法與數(shù)據(jù)庫交互,等等。

          ParameterHandler

          MyBatis框架使用的Statement類型為CallableStatementPreparedStatement時,ParameterHandler用于為Statement對象參數(shù)占位符設置值。

          ResultSetHandler

          ResultSetHandler封裝了對JDBC中的ResultSet對象操作,當執(zhí)行SQL類型為SELECT語句時,ResultSetHandler用于將查詢結(jié)果轉(zhuǎn)換成Java對象。

          TypeHandler

          TypeHandler是MyBatis中的類型處理器,用于處理Java類型與JDBC類型之間的映射。它的作用主要體現(xiàn)在能夠根據(jù)Java類型調(diào)用PreparedStatementCallableStatement對象對應的setXXX()方法為Statement對象設置值,而且能夠根據(jù)Java類型調(diào)用ResultSet對象對應的getXXX()獲取SQL執(zhí)行結(jié)果。

          使用JDBC API開發(fā)應用程序,其中一個比較煩瑣的環(huán)節(jié)是處理JDBC類型與Java類型之間的轉(zhuǎn)換。涉及Java類型和JDBC類型轉(zhuǎn)換的兩種情況如下:

          • PreparedStatement對象為參數(shù)占位符設置值時,需要調(diào)用PreparedStatement接口中提供的一系列的setXXX()方法,將Java類型轉(zhuǎn)換為對應的JDBC類型并為參數(shù)占位符賦值。
          • 執(zhí)行SQL語句獲取ResultSet對象后,需要調(diào)用ResultSet對象的getXXX()方法獲取字段值,此時會將JDBC類型轉(zhuǎn)換為Java類型。

          MyBatis提供的TypeHandler及與Java類型和JDBC類型之間的對應關(guān)系:

          小結(jié)

          我們使用到了SqlSession組件,它是用戶層面的API。實際上SqlSession是Executor組件的外觀,目的是為用戶提供更友好的數(shù)據(jù)庫操作接口,這是設計模式中外觀模式的典型應用。

          真正執(zhí)行SQL操作的是Executor組件,Executor可以理解為SQL執(zhí)行器,它會使用StatementHandler組件對JDBC的Statement對象進行操作。

          當Statement類型為CallableStatementPreparedStatement時,會通過ParameterHandler組件為參數(shù)占位符賦值。ParameterHandler組件中會根據(jù)Java類型找到對應的TypeHandler對象,TypeHandler中會通過Statement對象提供的setXXX()方法(例如setString()方法)為Statement對象中的參數(shù)占位符設置值。

          StatementHandler組件使用JDBC中的Statement對象與數(shù)據(jù)庫完成交互后,當SQL語句類型為SELECT時,MyBatis通過ResultSetHandler組件從Statement對象中獲取ResultSet對象,然后將ResultSet對象轉(zhuǎn)換為Java對象。

          推薦:教小師妹快速入門Mybatis,看這篇就夠了

          高級技能

          設計模式

          在MyBatis 中大量的使用了設計模式,在MyBatis 我們可以學到一下幾種設計模式:

          • 工廠模式
          • 模板方法模式
          • 代理模式
          • 建造者模式
          • 單例模式
          • 適配模式
          • 裝飾器模式
          • 責任鏈模式
          • ....

          推薦:面試官:Mybatis里的設計模式有哪些?

          緩存

          在 Web 應用中,緩存是必不可少的組件。通常我們都會用 Redis 或 memcached 等緩存中間件,攔截大量奔向數(shù)據(jù)庫的請求,減輕數(shù)據(jù)庫壓力。作為一個重要的組件,MyBatis 自然也在內(nèi)部提供了相應的支持。通過在框架層面增加緩存功能,可減輕數(shù)據(jù)庫的壓力,同時又可以提升查詢速度,可謂一舉兩得。

          MyBatis 緩存結(jié)構(gòu)由一級緩存二級緩存構(gòu)成,這兩級緩存均是使用 Cache 接口的實現(xiàn)類。因此,在接下里的章節(jié)中,我將首先會向大家介紹 Cache 幾種實現(xiàn)類的源碼,然后再分析一級和二級緩存的實現(xiàn)。

          MyBatis一級緩存和二級緩存的使用:MyBatis一級緩存是SqlSession級別的緩存,默認就是開啟的,而且無法關(guān)閉;二級緩存需要在MyBatis主配置文件中通過設置cacheEnabled參數(shù)值來開啟。

          一級緩存是在Executor中實現(xiàn)的。MyBatis的Executor組件有3種不同的實現(xiàn),分別為SimpleExecutorReuseExecutorBatchExecutor。這些類都繼承自BaseExecutor,在BaseExecutor類的query()方法中,首先從緩存中獲取查詢結(jié)果,如果獲取不到,則從數(shù)據(jù)庫中查詢結(jié)果,然后將查詢結(jié)果緩存起來。而MyBatis的二級緩存則是通過裝飾器模式實現(xiàn)的,當通過cacheEnabled參數(shù)開啟了二級緩存,MyBatis框架會使用CachingExecutor對SimpleExecutorReuseExecutor或者BatchExecutor進行裝飾,當執(zhí)行查詢操作時,對查詢結(jié)果進行緩存,執(zhí)行更新操作時則更新二級緩存。本章最后介紹了MyBatis如何整合Redis作為二級緩存。

          除此之外,MyBatis還支持EhcacheOSCache等,這種特性并不常用。

          推薦:@萬字長文 | MyBatis 緩存到底

          插件

          大多數(shù)框架,都支持插件,用戶可通過編寫插件來自行擴展功能,Mybatis也不例外。

          MyBatis提供了擴展機制,能夠在執(zhí)行Mapper時改變SQL的執(zhí)行行為。這種擴展機制是通過攔截器來實現(xiàn)的,用戶自定義的攔截器也被稱為MyBatis插件。

          MyBatis框架支持對ExecutorParameterHandlerResultSetHandlerStatementHandler四種組件的方法進行攔截。掌握了MyBatis插件的實現(xiàn)原理,然后自己實現(xiàn)一個分頁查詢插件和慢SQL統(tǒng)計插件,當我們需要的功能MyBatis框架無法滿足時,可以考慮通過自定義插件來實現(xiàn)。

          經(jīng)典實現(xiàn):PageHelper 。

          推薦:建議收藏,mybatis插件原理詳解

          日志

          MyBatis針對不同的日志框架提供對Log接口對應的實現(xiàn),Log接口的實現(xiàn)類下圖所示。從實現(xiàn)類可以看出,MyBatis支持7種不同的日志實現(xiàn),具體如下。

          下面對常用幾個做一個簡單說明:

          • Apache Commons Logging:使用JCL輸出日志。
          • Log4j 2:使用Log4j 2框架輸入日志。
          • Java Util Logging:使用JDK內(nèi)置的日志模塊輸出日志。
          • Log4j:使用Log4j框架輸出日志。
          • No Logging:不輸出任何日志。
          • SLF4J:使用SLF4J日志門面輸出日志。
          • Stdout:將日志輸出到標準輸出設備(例如控制臺)。

          MyBatis查找日志框架的順序為

          SLF4J→JCL→Log4j2→Log4j→JUL→No Logging。

          如果Classpath下不存在任何日志框架,則使用NoLoggingImpl日志實現(xiàn)類,即不輸出任何日志。

          動態(tài)SQL綁定

          動態(tài)SQL指的是事先無法預知具體的條件,需要在運行時根據(jù)具體的情況動態(tài)地生成SQL語句。

          在MyBatis中有著豐富的動態(tài)SQL標簽,比如:等。

          在MyBatis源碼中有個SqlSource對象會作為MappedStatement對象的屬性保存在MappedStatement對象中。執(zhí)行Mapper時,會根據(jù)傳入的參數(shù)信息調(diào)用SqlSource對象的getBoundSql()方法獲取BoundSql對象,這個過程就完成了將SqlNode對象轉(zhuǎn)換為SQL語句的過程。

          比如:,#{}占位符會被替換為“?”,然后調(diào)用JDBC中PreparedStatement對象的setXXX()方法為參數(shù)占位符設置值,而${}占位符則會直接替換為傳入的參數(shù)文本內(nèi)容。

          推薦:掌握Mybatis動態(tài)映射,我可是下了功夫的

          總結(jié)

          本文是對MyBatis源碼分析的整體感受,希望對你有點點幫助。

          咱們不是為了看源碼而看源碼,更不是為了裝B而看源碼,更多是我們從這個源碼中學到了什么。比如學到了設計模式是怎么使用的,學會了插件的思想,學會了緩存是怎么使用的等等。

          個人覺得學習MyBatis源碼有幾個好處:

          • 掌握Mybatis整體思想
          • 學到一些核心技能
          • 對于實際工作中排查問題有所幫助
          • 面試還可以吹吹牛

          干貨分享

          最近將個人學習筆記整理成冊,使用PDF分享。關(guān)注我,回復如下代碼,即可獲得百度盤地址,無套路領取!

          ?001:《Java并發(fā)與高并發(fā)解決方案》學習筆記;?002:《深入JVM內(nèi)核——原理、診斷與優(yōu)化》學習筆記;?003:《Java面試寶典》?004:《Docker開源書》?005:《Kubernetes開源書》?006:《DDD速成(領域驅(qū)動設計速成)》?007:全部?008:加技術(shù)群討論

          加個關(guān)注不迷路

          喜歡就點個"在看"唄^_^

          瀏覽 31
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  超碰97国产福利 | 五月激情五月婷婷 | 欧美A视频在线观看 | 国产精品福利高清在线观看 | 黄色照片视频 |