MyBatis基礎知識到源碼分析

MyBatis基礎知識到源碼分析

MyBatis是支持定製化SQL、存儲過程以及高級映射的持久層框架。MyBatis使用XML或註解來配置和映射基本體,將接口和Java的POJO映射成數據庫中的記錄。作爲一款輕量級的持久層框架,佔據了很大部分市場,我們公司團隊持久層也是使用mybatis,結合MyBatisCodeHelperPro插件提升開發效率,本文介紹了mybatis的一些基本特性及原理剖析,可以作爲日常開發指南或面試指導而學習。

文章目錄

1、#{}和${}的區別是什麼?

  • $ {} 是Properties文件中的變量佔位符,它可以用於標籤屬性值和sql內部,屬於靜態文本替換,比如${driver}會被靜態替換爲com.mysql.jdbc.Driver。
    #{} 是sql的參數佔位符,Mybatis會將sql中的#{}替換爲?號,在sql執行前會使用PreparedStatement的參數設置方法,按序給sql的?號佔位符設置參數值,比如ps.setInt(0, parameterValue),#{item.name}的取值方式爲使用反射從參數對象中獲取item對象的name屬性值,相當於param.getItem().getName()。

2、Xml映射文件中,除了常見的select|insert|updae|delete標籤之外,還有哪些標籤?

  • 還有很多其他的標籤,< resultMap>、< parameterMap>、< sql>、< include>、< selectKey>,加上動態sql的9個標籤,trim|where|set|foreach|if|choose|when|otherwise|bind等,其中< sql>爲sql片段標籤,通過< include>標籤引入sql片段,< selectKey> 爲不支持自增的主鍵生成策略標籤。

3、最佳實踐中,通常一個Xml映射文件,都會寫一個Dao接口與之對應,請問,這個Dao接口的工作原理是什麼?Dao接口裏的方法,參數不同時,方法能重載嗎?

  • Dao接口(就是Mapper接口),接口的全限名(就是映射文件中的namespace的值),接口的方法名(就是映射文件中MappedStatement的id值),接口方法內的參數(就是傳遞給sql的參數)。
    Mapper接口是沒有實現類的,當調用接口方法時,接口全限名+方法名拼接字符串作爲key值,可唯一定位一個MappedStatement,舉例:com.mybatis3.mappers.StudentDao.findStudentById,可以唯一找到namespace爲com.mybatis3.mappers.StudentDao下面id = findStudentById的MappedStatement。在Mybatis中,每一個< select>、< insert>、< update>、< delete>標籤,都會被解析爲一個MappedStatement對象。
    Dao接口裏的方法,是不能重載的,因爲是全限名+方法名的保存和尋找策略。
  • Dao接口的工作原理是JDK動態代理,Mybatis運行時會使用JDK動態代理爲Dao接口生成代理proxy對象,代理對象proxy會攔截接口方法,轉而執行MappedStatement所代表的sql,然後將sql執行結果返回。

4、Mybatis是如何進行分頁的?分頁插件的原理是什麼?

  • Mybatis使用RowBounds對象進行分頁,它是針對ResultSet結果集執行的內存分頁,而非物理分頁,可以在sql內直接書寫帶有物理分頁的參數來完成物理分頁功能,也可以使用分頁插件來完成物理分頁。
  • 分頁插件的基本原理是使用Mybatis提供的插件接口,實現自定義插件,在插件的攔截方法內攔截待執行的sql,然後重寫sql,根據dialect方言,添加對應的物理分頁語句和物理分頁參數。
    舉例:select * from student,攔截sql後重寫爲:select t.* from (select * from student)t limit 0,10

5、簡述Mybatis的插件運行原理,以及如何編寫一個插件。

  • Mybatis僅可以編寫針對ParameterHandler、ResultSetHandler、StatementHandler、Executor這4種接口的插件,Mybatis使用JDK的動態代理,爲需要攔截的接口生成代理對象以實現接口方法攔截功能,每當執行這4種接口對象的方法時,就會進入攔截方法,具體就是InvocationHandler的invoke()方法,當然,只會攔截那些你指定需要攔截的方法。
  • 實現Mybatis的Interceptor接口並複寫intercept()方法,然後在給插件編寫註解,指定要攔截哪一個接口的哪些方法即可,記住,別忘了在配置文件中配置你編寫的插件。

6、Mybatis執行批量插入,能返回數據庫主鍵列表嗎?

  • 能,JDBC都能,Mybatis當然也能。

7、Mybatis動態sql是做什麼的?都有哪些動態sql?能簡述一下動態sql的執行原理不?

  • Mybatis動態sql可以讓我們在Xml映射文件內,以標籤的形式編寫動態sql,完成邏輯判斷和動態拼接sql的功能,Mybatis提供了9種動態sql標籤trim|where|set|foreach|if|choose|when|otherwise|bind。
  • 其執行原理爲,使用OGNL從sql參數對象中計算表達式的值,根據表達式的值動態拼接sql,以此來完成動態sql的功能

8、Mybatis是如何將sql執行結果封裝爲目標對象並返回的?都有哪些映射形式?

  • 第一種是使用< resultMap>標籤,逐一定義列名和對象屬性名之間的映射關係。第二種是使用sql列的別名功能,將列別名書寫爲對象屬性名,比如T_NAME AS NAME,對象屬性名一般是name,小寫,但是列名不區分大小寫,Mybatis會忽略列名大小寫,智能找到與之對應對象屬性名,你甚至可以寫成T_NAME AS NaMe,Mybatis一樣可以正常工作。
    有了列名與屬性名的映射關係後,Mybatis通過反射創建對象,同時使用反射給對象的屬性逐一賦值並返回,那些找不到映射關係的屬性,是無法完成賦值的

9、Mybatis能執行一對一、一對多的關聯查詢嗎?都有哪些實現方式,以及它們之間的區別。

  • 能,Mybatis不僅可以執行一對一、一對多的關聯查詢,還可以執行多對一,多對多的關聯查詢,多對一查詢,其實就是一對一查詢,只需要把selectOne()修改爲selectList()即可;多對多查詢,其實就是一對多查詢,只需要把selectOne()修改爲selectList()即可。
  • 關聯對象查詢,有兩種實現方式,一種是單獨發送一個sql去查詢關聯對象,賦給主對象,然後返回主對象 (推薦)。另一種是使用嵌套查詢,嵌套查詢的含義爲使用join查詢,一部分列是A對象的屬性值,另外一部分列是關聯對象B的屬性值,好處是隻發一個sql查詢,就可以把主對象和其關聯對象查出來(不推薦)。
    那麼問題來了,join查詢出來100條記錄,如何確定主對象是5個,而不是100個?其去重複的原理是標籤內的子標籤,指定了唯一確定一條記錄的id列,Mybatis根據列值來完成100條記錄的去重複功能,可以有多個,代表了聯合主鍵的語意。
    同樣主對象的關聯對象,也是根據這個原理去重複的,儘管一般情況下,只有主對象會有重複記錄,關聯對象一般不會重複。
  • 舉例:下面join查詢出來6條記錄,一、二列是Teacher對象列,第三列爲Student對象列,Mybatis去重複處理後,結果爲1個老師6個學生,而不是6個老師6個學生。

10、Mybatis是否支持延遲加載?如果支持,它的實現原理是什麼?

  • Mybatis僅支持association關聯對象和collection關聯集合對象的延遲加載,association指的就是一對一,collection指的就是一對多查詢。在Mybatis配置文件中,可以配置是否啓用延遲加載lazyLoadingEnabled=true|false
  • 它的原理是,使用CGLIB創建目標對象的代理對象,當調用目標方法時,進入攔截器方法,比如調用a.getB().getName(),攔截器invoke()方法發現a.getB()是null值,那麼就會單獨發送事先保存好的查詢關聯B對象的sql,把B查詢上來,然後調用a.setB(b),於是a的對象b屬性就有值了,接着完成a.getB().getName()方法的調用。這就是延遲加載的基本原理。
    當然了,不光是Mybatis,幾乎所有的包括Hibernate,支持延遲加載的原理都是一樣的。

11、Mybatis的Xml映射文件中,不同的Xml映射文件,id是否可以重複?

  • 不同的Xml映射文件,如果配置了namespace,那麼id可以重複;如果沒有配置namespace,那麼id不能重複;畢竟namespace不是必須的,只是最佳實踐而已。
    原因就是namespace+id是作爲Map<String, MappedStatement>的key使用的,如果沒有namespace,就剩下id,那麼,id重複會導致數據互相覆蓋。有了namespace,自然id就可以重複,namespace不同,namespace+id自然也就不同。

12、Mybatis中如何執行批處理?

  • 使用BatchExecutor完成批處理。

13、Mybatis都有哪些Executor執行器?它們之間的區別是什麼?

  • Mybatis有三種基本的Executor執行器,SimpleExecutor(mybatis
    默認開啓該配置
    )、ReuseExecutor、BatchExecutor。
執行器 邏輯
SimpleExecutor 每執行一次update或select,就開啓一個Statement對象,用完立刻關閉Statement對象。(默認)
ReuseExecutor 執行update或select,以sql作爲key查找Statement對象,存在就使用,不存在就創建,用完後,不關閉Statement對象,而是放置於Map<String, Statement>內,供下一次使用。簡言之,就是重複使用Statement對象
BatchExecutor 執行update(沒有select,JDBC批處理不支持select),將所有sql都添加到批處理中(addBatch()),等待統一執行(executeBatch()),它緩存了多個Statement對象,每個Statement對象都是addBatch()完畢後,等待逐一執行executeBatch()批處理。與JDBC批處理相同。

作用範圍:Executor的這些特點,都嚴格限制在SqlSession生命週期範圍內。
Mybatis配置文件放在那兒 我們公司將配置文件放置在apollo中
配置信息解讀:mybatis.plugin.autoAddTraceid = true


14、Mybatis中如何指定使用哪一種Executor執行器?

  • 在Mybatis配置文件中,可以指定默認的ExecutorType執行器類型,也可以手動給DefaultSqlSessionFactory的創建SqlSession的方法傳遞ExecutorType類型參數。

15、Mybatis是否可以映射Enum枚舉類?

  • Mybatis可以映射枚舉類,不單可以映射枚舉類,Mybatis可以映射任何對象到表的一列上。映射方式爲自定義一個TypeHandler,實現TypeHandler的setParameter()和getResult()接口方法。
    TypeHandler有兩個作用,一是完成從javaType至jdbcType的轉換,二是完成jdbcType至javaType的轉換,體現爲setParameter()和getResult()兩個方法,分別代表設置sql問號佔位符參數和獲取列查詢結果。

16、Mybatis映射文件中,如果A標籤通過include引用了B標籤的內容,請問,B標籤能否定義在A標籤的後面,還是說必須定義在A標籤的前面?

  • 雖然Mybatis解析Xml映射文件是按照順序解析的,但是,被引用的B標籤依然可以定義在任何地方,Mybatis都可以正確識別。
  • 原理是,Mybatis解析A標籤,發現A標籤引用了B標籤,但是B標籤尚未解析到,尚不存在,此時,Mybatis會將A標籤標記爲未解析狀態,然後繼續解析餘下的標籤,包含B標籤,待所有標籤解析完畢,Mybatis會重新解析那些被標記爲未解析的標籤,此時再解析A標籤時,B標籤已經存在,A標籤也就可以正常解析完成了。

17、簡述Mybatis的Xml映射文件和Mybatis內部數據結構之間的映射關係?

  • Mybatis將所有Xml配置信息都封裝到All-In-One重量級對象Configuration內部。在Xml映射文件中,< parameterMap>標籤會被解析爲ParameterMap對象,其每個子元素會被解析爲ParameterMapping對象。
    < resultMap>標籤會被解析爲ResultMap對象,其每個子元素會被解析爲ResultMapping對象。每一個< select>、< insert>、< update>、標籤均會被解析爲MappedStatement對象,標籤內的sql會被解析爲BoundSql對象。

18、爲什麼說Mybatis是半自動ORM映射工具?它與全自動的區別在哪裏?

  • Hibernate屬於全自動ORM映射工具,使用Hibernate查詢關聯對象或者關聯集合對象時,可以根據對象關係模型直接獲取,所以它是全自動的。而Mybatis在查詢關聯對象或關聯集合對象時,需要手動編寫sql來完成,所以,稱之爲半自動ORM映射工具。

19、對Mybatis的理解?

  • 什麼是mybatis?(版本mybatis-3.4.1)
    MyBatis是支持定製化SQL、存儲過程以及高級映射的持久層框架。MyBatis使用XML或註解來配置和映射基本體,將接口和Java的POJO(PlainOldJava Objects,普通的Java對象)映射成數據庫中的記錄。
  • MyBatis-plus2.1.5:非常強大的MyBatis增強工具包,只做增強不做改變,在不用編寫任何SQL語句的情況下即可以極其方便的實現單一、批量、分頁等操作;
  • 集成Mybatis-Plus、通用CRUD、EntityWrapper條件構造器、ActiveRecord等基本操作,更有代碼生成器、插件擴展、自定義全局操作、公共字段填充、Oracle主鍵Sequence處理、Idea快速開發插件等高階技術

20、mybatis的緩存機制? (招銀網絡科技)

分爲兩級緩存

  • 1、一級緩存是session會話級別的緩存,位於表示一次數據庫會話的sqlsession對象之中,又被稱爲本地緩存。一級緩存是mybatis內部實現的一個特性,用戶不需要配置,默認情況下是自動支持的緩存,用戶沒有定製它的權利。當 openSession()後,如果執行相同的SQL(相同語句和參數),Mybatis不進行執行SQL,而是從緩存中命中返回。其儲存作用域爲session(一次會話),當session flush或close之後,該session中所有cache被清空
  • 2、二級緩存是application應用級別的緩存,它的生命週期很長,跟application同,二級緩存可以跨session,二級緩存與一級緩存機制相同,默認也是採用PerpetualCache,使用HashMap存儲。
  • 3、不同之處是,其存儲作用域爲Mapper(namespace),並且可以自定義存儲源,如Ehcache;作用域namespace是指該namespace對應的配置文件中所有的select操作結果都緩存,這樣不同線程之間就可以共用二級緩存。啓動二級緩存:在Mapper配置文件中: < cache/>
  • 4、緩存數據更新機制當某一個作用域(一級緩存Session/二級緩存Namespaces)進行了C/U/D 操作後,默認該作用域下所有select中的緩存將被clear.

21、mybatis和jdbc的區別?(浙商銀行)

也可以換種提問的方式:JDBC編程有哪些不足之處,MyBatis是如何解決這些問題的?

  • 1、數據庫連接創建,釋放頻繁會造成系統浪費從而影響系統性能,如果使用數據庫連接池可以解決此問題。在sqlMapConfig.xml中配置數據連接池,使用連接池管理數據庫連接
  • 2、sql語句與java代碼耦合,造成代碼不易維護,實際需求sql的變更可能較大,需要改變java代碼。將sql語句配置在xxxmapper.xml文件中,與java代碼分離。
  • 3、向sql語句傳參數麻煩,因爲sql語句的where條件不一定能夠用到,可能多可能少,佔位符需要和參數一一對應。mybatis自動將java對象映射至sql語句。
  • 4、對結果集解析麻煩,sql變化導致解析代碼變化,且解析前需要遍歷,如果能將數據庫記錄封裝成pojo對象,解析比較方便。mybatis自動將sql執行結果映射至java對象。

22、jdbc流程?分爲6個步驟

  • 1、加載driver類,通過反射註冊數據庫驅動
  • 2、通過driverManager,使用url,用戶名,密碼建立連接
  • 3、通過connection,使用sql語句打開Statement對象
  • 4、執行語句,將結果返回resultSet
  • 5、對結果進行處理 這步很麻煩,使用框架可以簡化
  • 6、倒序釋放資源resultset-》preparedStatement-》connection
    preparedStatement好處是可以對sql語句預編譯存儲,多次高效的執行sql

23、MyBatis批量操作(可以通過通用Mapper或逆向工程來創建)

  • 1、批量查詢 list
select  *
from m_acc_keys where accid in
<foreach item="item" index="index" collection="paramIds" open="(" separator="," close=")">
   #{item}
</foreach>
  • 2、批量保存 list
<insert id="insertList" parameterType="list" >
    replace into xx (id, c1,c2) values
    <foreach collection="list" item="it" separator=",">
        (#{it.id},#{it.c1},#{it.c2})
    </foreach>
</insert>

replace into 跟 insert 功能類似,不同點在於:replace into 首先嚐試插入數據到表中, 1. 如果發現表中已經有此行數據(根據主鍵或者唯一索引判斷)則先刪除此行數據,然後插入新的數據。 2. 否則,直接插入新數據
要注意的是:插入數據的表必須有主鍵或者是唯一索引!否則的話,replace into 會直接插入數據,這將導致表中出現重複的數據。

  • 3、批量查詢 數組
public List<Column> selectByIds(String[] ids);
<select id="selectByIds" parameterType="java.lang.String" 
    resultType="columnMap">
    <include refid="select_clause"></include>
    where c.id in 
    <foreach collection="array" item="id" open="(" separator="," close=")">
           #{id}
       </foreach>
</select>
  • 4、多重循環刪除
<delete id="move" parameterType="java.util.Map">
    insert into b_column_programme(columnId, programmeId) 
    values 
        <foreach collection="colids" item="colid" separator="," >
           <foreach collection="proids" item="proid" separator="," >
            (#{colid}, #{proid})
           </foreach>
       </foreach>
</delete>
  • 批量插入(主鍵自增長)
<insert id="insertList" parameterType="list" useGeneratedKeys="true" keyProperty="id" >
    insert into xx
    (c1,c2)
    values
    <foreach collection="list" item="it" separator=",">
        (#{it.c1},#{it.c2})
    </foreach>
</insert>

24、MYbatis源碼? 待補充

    1、mybatis源碼結構分析
    
    2、mybatis1.0手寫實現
    
    3、mybatis2.0升級手寫
    
    4、mapper文件解讀

25、Hibernate與mybatis相比較?/描述一下Hibernate的三個狀態? 招銀網絡,浙商銀行

  • 1、Hibernate與mybatis相比較:
1 2
相同點1 Hibernate與MyBatis都可以是通過SessionFactoryBuider由XML配置文件生成SessionFactory,然後由SessionFactory生成Session,最後由Session來開啓執行事務和SQL語句;
相同點2 Hibernate和MyBatis都支持JDBC事務處理。
不同點1 mybatis更爲輕便、靈活,容易掌握。mybatis可以把sql語句從java代碼中分離了出來,放在了配置文件中(xml中)書寫,大大降低裏java代碼與SQL語句的耦合度
不同點2 更容易對sql語句操作,重要的是mybatis還可以書寫動態的sql語句(mybatis本身的緩存機制沒有hibernate那麼完善);
不同點3 hibernate除了本身有良好的緩存機制,還可以使用第三方緩存。Hibernate較完整的封裝了JDBC,但學起來要比mybatis更困難一些。
不同點 4 Hibernate的DAO層開發比MyBatis簡單,對對象的維護和緩存要比MyBatis好。
不同點5 Hibernate是ORM 框架,它能建立面向對象的域模型和關係數據模型之間的映射,它大大簡化dao層的編碼工作。
從學習的角度 Hibernate應用非常廣泛,但是過度強調持久化和隔離數據庫底層細節,也導致了很多弊端,例如HQL需要額外的學習,減弱程序員對SQL的直接控制;MyBatis雖然仍然提供了一些映射的功能,但更加以SQL爲中心,開發者可以側重於SQL和存儲過程,非常簡單、直接;Spring JDBC Template也是更加接近於SQL層面,Spring本身也可以集成Hibernate等O/R Mapping框架
  • 2、hibernate三種狀態?
    瞬時態(臨時態、自由態):不存在持久態標識OID,尚未與session關聯對象,被認爲處於瞬時態,失去引用將被JVM回收;
    持久態:存在持久化標識OID,與當前session有關聯,並且相關聯的session沒有關閉,並且事務未提交
    託管態(離線態,遊離態):存在持久化標識OID,但沒有與當前session關聯,脫管狀態,改變hibernate不能檢測到

26、Springboot整合mybatis? 待補充


27、從架構設計的角度,可以將MyBatis分爲哪幾層?每層都有哪些主要模塊?

  • mybatis架構自下而上分爲基礎支撐層、數據處理層、API接口層這三層。
    基礎支撐層:主要是用來做連接管理、事務管理、配置加載、緩存管理等最基礎組件,爲上層提供最基礎的支撐。(xml文件)
    數據處理層:主要是用來做參數映射、sql解析、sql執行、結果映射等處理,可以理解爲請求到達,完成一次數據庫操作的流程。
    API接口層:主要對外提供API,提供諸如數據的增刪改查、獲取配置等接口。

參考文獻:
1、https://mp.weixin.qq.com/s/PEaxcHdGP2nBCbITRLSFjQ
2、劉增福 《mybatis必知必會》
3、https://blog.csdn.net/daijiguo/article/details/51840188
4、MyBatisCodeHelperPro插件地址及教學視頻:https://www.bilibili.com/video/av23458308?from=search&seid=11858208907274781658

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章