1. 程式人生 > >Java面試題2018---J2EE後端---MyBatis 框架

Java面試題2018---J2EE後端---MyBatis 框架

1、MyBatis 的核心原理,及執行流程

現在開源專案中持久層框架用到最多的基本就是 iBatis、myBatis 和 Hibernate 了。

原理詳解:

        MyBatis應用程式根據XML配置檔案建立SqlSessionFactory,SqlSessionFactory在根據配置,配置來源於兩個地方,一處是配置檔案,一處是Java程式碼的註解,獲取一個SqlSession。SqlSession包含了執行sql所需要的所有方法,可以通過SqlSession例項直接執行對映的sql語句,完成對資料的增刪改查和事務提交等,用完之後關閉SqlSession。

執行流程:

*MyBatis在初始化的時候,會將MyBatis的配置資訊全部載入到記憶體中,使用org.apache.ibatis.session.Configuration例項來維護。使用者可以使用sqlSession.getConfiguration()方法來獲取。MyBatis的配置檔案中配置資訊的組織格式和記憶體中物件的組織格式幾乎完全對應的。

*載入到記憶體中會生成一個對應的MappedStatement物件,然後會以key="com.louis.mybatis.dao.EmployeesMapper.selectByMinSalary" ,value為MappedStatement物件的形式維護到Configuration的一個Map中。當以後需要使用的時候,只需要通過Id值來獲取就可以了。

我們可以看到SqlSession的職能是:

SqlSession根據Statement ID, 在mybatis配置物件Configuration中獲取到對應的MappedStatement物件,然後呼叫mybatis執行器來執行具體的操作。

*MyBatis執行器Executor根據SqlSession傳遞的引數執行query()方法

Executor.query()方法幾經轉折,最後會建立一個StatementHandler物件,然後將必要的引數傳遞給StatementHandler,使用StatementHandler來完成對資料庫的查詢,最終返回List結果集。

Executor的功能和作用是:

(1、根據傳遞的引數,完成SQL語句的動態解析,生成BoundSql物件,供StatementHandler使用;

(2、為查詢建立快取,以提高效能

(3、建立JDBC的Statement連線物件,傳遞給StatementHandler物件,返回List查詢結果。

*StatementHandler物件負責設定Statement物件中的查詢引數、處理JDBC返回的resultSet,將resultSet加工為List 集合返回

StatementHandler物件主要完成兩個工作:

         (1. 對於JDBC的PreparedStatement型別的物件,建立的過程中,我們使用的是SQL語句字串會包含 若干個? 佔位符,我們其後再對佔位符進行設值。StatementHandler通過parameterize(statement)方法對Statement進行設值;       

         (2.StatementHandler通過List<E> query(Statement statement, ResultHandler resultHandler)方法來完成執行Statement,和將Statement物件返回的resultSet封裝成List;

         StatementHandler 的parameterize(Statement) 方法呼叫了 ParameterHandler的setParameters(statement) 方法,

ParameterHandler的setParameters(Statement)方法負責 根據我們輸入的引數,對statement物件的 ? 佔位符處進行賦值。

         StatementHandler的List<E> query(Statement statement, ResultHandler resultHandler)方法的實現,是呼叫了ResultSetHandler的handleResultSets(Statement)方法。ResultSetHandler的handleResultSets(Statement) 方法會將Statement語句執行後生成的resultSet結果集轉換成List<E> 結果集

從MyBatis程式碼實現的角度來看,MyBatis的主要的核心部件有以下幾個:

MyBatis封裝了對資料庫的訪問,把對資料庫的會話和事務控制放到了SqlSession物件中。

  • SqlSession           作為MyBatis工作的主要頂層API,表示和資料庫互動的會話,完成必要資料庫增刪改查功能
  • Executor             MyBatis執行器,是MyBatis 排程的核心,負責SQL語句的生成和查詢快取的維護
  • StatementHandler  封裝了JDBC Statement操作,負責對JDBC statement 的操作,如設定引數、將Statement結果集轉換成List集合。
  • ParameterHandler  負責對使用者傳遞的引數轉換成JDBC Statement 所需要的引數,
  • ResultSetHandler   負責將JDBC返回的ResultSet結果集物件轉換成List型別的集合;
  • TypeHandler         負責java資料型別和jdbc資料型別之間的對映和轉換
  • MappedStatement  MappedStatement維護了一條<select|update|delete|insert>節點的封裝,
  • SqlSource           負責根據使用者傳遞的parameterObject,動態地生成SQL語句,將資訊封裝到BoundSql物件中,並返回
  • BoundSql            表示動態生成的SQL語句以及相應的引數資訊
  • Configuration       MyBatis所有的配置資訊都維持在Configuration物件之中。

本題參考部落格:https://blog.csdn.net/d12345678a/article/details/53956485

2、MyBatis 與 Hibernate 有什麼異同? 

hibernate:

1、Hibernate則相對來說較為複雜,學習門檻不低,要精通門檻更高,而且怎麼設計O/R對映,在效能和物件模型之間如何權衡取得平衡,怎樣用好 Hibernate 方面需要經驗和能力都很強才行。 

2、Hibernate 會自動生成sql 語句,能夠在程式執行時自動生成,能夠自動建表,無論到什麼機器上,都不需要資料庫,都能自動完成遷移;

3、Hibernate 功能強大,資料庫無關性好,O/R(物件/關係)對映能力強,Hibernate 對資料庫結構提供了較為完整的封裝,Hibernate 的O/R Mapping實現了POJO(實體類) 和資料庫表之間的對映,以及SQL 的自動生成和執行。程式設計師往往只需定義好了POJO 到資料庫表的對映關係,即可通過 Hibernate 提供的方法完成持久層操作。程式設計師甚至不需要對SQL 的熟練掌握, Hibernate/OJB 會根據制定的儲存邏輯,自動生成對應的SQL 並呼叫JDBC 介面加以執行;

5、hibernate可移植性好,如一個專案開始使用的是mysql資料庫,但是隨著業務的發展,現mysql資料庫已經無法滿足當前的繡球了,現在決定使用Oracle資料庫,雖然sql標準定義的資料庫間的sql語句差距不大,但是不同的資料庫sql標準還是有差距的,那麼我們手動修改起來會存在很大的困難,使用hibernate只需改變一下資料庫方言即可搞定。用hibernate框架,資料庫的移植變的非常方便。

6、當系統屬於二次開發,無法對資料庫結構做到控制和修改,那myBatis 的靈活性將比 Hibernate 更適合。系統資料處理量巨大,效能要求極為苛刻,這往往意味著我們必須通過經過高度優化的SQL語句(或儲存過程)才能達到系統性能設計指標。在這種情況下 myBatis 會有更好的可控性和表現。 

7、涉及到大資料的系統使用Mybatis比較好,因為優化較方便。涉及的資料量不是很大且對優化沒有那麼高,可以使用hibernate。

mybatis:

MyBatis 是支援定製化 SQL、儲存過程以及高階對映的優秀的持久層框架。MyBatis 避免了幾乎所有的 JDBC 程式碼和手動設定引數以及獲取結果集。MyBatis 可以對配置和原生Map使用簡單的 XML 或註解,將介面和 Java 的 POJOs(Plain Old Java Objects,普通的 Java物件)對映成資料庫中的記錄。

    mybatis的優點:

  • 1. 與JDBC相比,減少了50%以上的程式碼量。
  • 2. MyBatis是最簡單的持久化框架,小巧並且簡單易學。
  • 3. MyBatis相當靈活,不會對應用程式或者資料庫的現有設計強加任何影響,SQL寫在XML裡,從程式程式碼中徹底分離,降低耦合度,便於統一管理和優化,並可重用。
  • 4. 提供XML標籤,支援編寫動態SQL語句。
  • 5. 提供對映標籤,支援物件與資料庫的ORM欄位關係對映。

   MyBatis框架的缺點:

  • 1. SQL語句的編寫工作量較大,尤其是欄位多、關聯表多時,更是如此,對開發人員編寫SQL語句的功底有一定要求。
  • 2. SQL語句依賴於資料庫,導致資料庫移植性差,不能隨意更換資料庫。

 iBatis與myBatis的關係:
  iBatis是2002年發起的開放原始碼專案,之前一直託管在apache下,於2010年6月16號被谷歌託管,改名為MyBatis,myBatis 與 iBatis 一樣,也是一種“半自動化”的ORM實現,共同的優點:

  • 是一個基於Java的持久層框架 
  • 提供的持久層框架包括SQL Maps和Data Access Objects(DAO) 
  • 支援普通 SQL查詢,儲存過程和高階對映的優秀持久層框架 
  • 消除了幾乎所有的JDBC程式碼和引數的手工設定以及結果集的檢索 
  • 使用簡單的 XML或註解用於配置和原始對映,將介面和 Java 的POJOs(Plain Old Java Objects,普通的 Java物件)對映成資料庫中的記錄 
  • myBatis 採用功能強大的基於OGNL的表示式來消除其他元素。 

功能架構: 


  • 1.API介面層:提供給外部使用的介面API,開發人員通過這些本地API來操縱資料庫。介面層一接收到呼叫請求就會呼叫資料處理層來完成具體的資料處理。 
    2.資料處理層:負責具體的SQL查詢、SQL解析、SQL執行和執行結果對映處理等。它主要的目的是根據呼叫的請求完成一次資料庫操作。 
    3.基礎支撐層:負責最基礎的功能支撐,包括連線管理、事務管理、配置載入和快取處理,這些都是共用的東西,將他們抽取出來作為最基礎的元件。為上層的資料處理層提供最基礎的支撐。 

本題參考部落格:https://blog.csdn.net/weixin_42131983/article/details/81322029

3、什麼是 MyBatis 名稱空間? 

在 mybatis 中,可以為每個對映檔案起一個唯一的名稱空間,這樣,定義在這個對映檔案中的每個 SQL 語句就成了定義在這個名稱空間中的一個 id。只要我們能夠保證每個名稱空間是唯一的,即使在不同對映檔案中的語句的 id 相同,也就不會衝突了。

在mybatis中,對映檔案中的namespace是用於繫結Dao介面的,即面向介面程式設計
當你的namespace繫結介面後,你可以不用寫介面實現類,mybatis會通過該繫結自動
幫你找到對應要執行的SQL語句,如下:
假設定義了IArticeDAO介面

public interface IArticleDAO
{
   List<Article> selectAllArticle();
}

對於對映檔案如下:

<mapper namespace="IArticleDAO">
  <select id="selectAllArticle" resultType="article">
      SELECT t.* FROM T_article t WHERE t.flag = '1' ORDER BY t.createtime DESC
  </select>
</mapper>

請注意介面中的方法與對映檔案中的SQL語句的ID一一對應 。
則在程式碼中可以直接使用IArticeDAO面向介面程式設計而不需要再編寫實現類。

本題參考部落格:https://blog.csdn.net/suyu_yuan/article/details/51329144

4、MyBatis 中如何進行 Mapper 的動態代理? 

UserMapper是一個介面 它並沒有實現類,為什麼介面可以直接使用呢? 那是因為MyBbatis使用了JDK動態代理機制動態生成了代理類

採用Mapper動態代理方法只需要編寫相應的Mapper介面(相當於Dao介面),那麼Mybatis框架根據介面定義建立介面的動態代理物件,代理物件的方法體同Dao介面實現類方法。
Mapper介面開發需要遵循以下規範:
1、Mapper.xml檔案中的namespace與mapper介面的全類名相同。
2、Mapper介面方法名和Mapper.xml中定義的每個statement的id相同。
3、Mapper介面方法的輸入引數型別和mapper.xml中定義的每個sql 的parameterType的型別相同。
4、Mapper介面方法的輸出引數型別和mapper.xml中定義的每個sql的resultType的型別相同。

此處參考部落格:https://blog.csdn.net/xiaokang123456kao/article/details/66476828

原始碼分析:

*Mybatis關於包裝Mapper的程式碼都在org.apache.ibatis.binding 這個包下面。其中有4個類: 
MapperRegistry 類是註冊Mapper介面與獲取代理類例項的工具類。

*類的getMapper方法裡面最後會去呼叫MapperProxyFactory類的newInstance方法。在呼叫getMapper方法前會初始化MapperProxyFactory,它是建立Mapper代理物件的工廠

*在這裡建立了MapperProxy物件 這個類實現了JDK的動態代理介面 InvocationHandler

*從快取中獲得執行方法對應的MapperMethod類例項。如果MapperMethod類例項不存在的情況,建立加入快取並返回相關的例項。最後呼叫MapperMethod類的execute方法。 
到這裡我們可以看到,getMapper方法就是用來獲得相關的資料操作類介面。而事實資料操作類邦定了動態代理。所以操據操作類執行方法的時候,會觸動每個方法相應的MapperProxy類的invoke方法。所以invoke方法返回的結果就是根據操作類執行方法的結果。這樣子我們就知道最後的任務交給了MapperMethod類例項。
*MapperMethod類裡面有倆個成員:SqlCommand類和MethodSignature類。從名字上我們大概的能想到一個可能跟SQL語句有關係,一個可能跟要執行的方法有關係。事實也是如此。 
上面使用一個內部類SqlCommand來封裝底層的增刪改查操作,確切來講這一部分的內容跟XxxMapper的XML配置檔案裡面的select節點、delete節點等有關。我們都會知道節點上有id屬性值。那麼MyBatis框架會把每一個節點(如:select節點、delete節點)生成一個MappedStatement類。要找到MappedStatement類就必須通過id來獲得。有一個細節要注意:程式碼用到的id = 當前介面類 + XML檔案的節點的ID屬性。
*我們可以回頭去找一找在什麼時候增加了MappedStatement類。上面之所以可以執行是建立在XML配置資訊都被載入進來了。所以MappedStatement類也一定是在載入配置的時候就進行的。請讀者們自行檢視一下MapperBuilderAssistant類的addMappedStatement方法——加深理解。SqlCommand類的name成員和type成員我們還是關注一下。name成員就是節點的ID,type成員表示查尋還是更新或是刪除。至於MethodSignature類呢。他用於說明方法的一些資訊,主要有返回資訊。

小知識:反射呼叫的最大好處就是配置性大大提高,就如同Spring IOC容器一樣,我們可以給很多配置引數,使得java應用程式能夠順利執行起來,大大提高java的靈活性和可配置性,降低模組之間的耦合。動態代理就是基於反射實現的。JAVA自帶的動態代理是基於java.lang.reflect.Proxy、java.lang.reflect.InvocationHandler兩個類來完成的,使用了JAVA反射機制,通常使用下面方法建立代理物件: Object proxy = Proxy.newProxyInstance(定義代理物件的類載入器,要代理的目標物件的歸屬介面陣列,回撥介面InvocationHandler)
本題參考部落格:https://blog.csdn.net/xiaokang123456kao/article/details/76228684

 

也可檢視我的另一篇部落格https://blog.csdn.net/jinhaijing/article/details/84314576,檢視具體完整的原始碼流程

建議可以看看https://blog.csdn.net/xubaifu/article/details/78831147這篇博文,看看自己怎麼實現jdk和cglib的動態代理。對於理解mybatis中的動態代理有好處。

 

5、MyBatis 中如何定義別名查詢? 

非自定義別名,所有javaJDK中的類都定義了別名,別名是類名不區分大小寫: map 替換java.util.Map,如果是包裝類,還可以使用基本資料型別:int 替換java.lang.Integer

自定義別名:

<configuration>
    <typeAliases>
      <typeAlias  alias="Person" type="com.stone.model.Person"/> 
    </typeAliases>
</configuration>

mapper.xml中可以使用:

<select id="getAllUser" resultType="Person">
    select * from users
</select>

6、MyBatis 的結果集 resultMap 可以定義哪些型別? 

resultmap是mybatis中最複雜的元素之一,它描述如何從結果集中載入物件,主要作用是定義對映規則、級聯的更新、定製型別轉化器。

比如查詢物件中有集合,或者物件時,此時就需要用到resultMap來適配結果。

association 一對一級聯

 

collection 一對多級聯

 

注:此處,用到了分步查詢,如果還需要配置延遲載入(也叫懶載入和按需載入),在全域性檔案中還需要配置

<settings>
	<!--顯示的指定每個我們需要更改的配置的值,即使他是預設的。防止版本更新帶來的問題  -->
	<setting name="lazyLoadingEnabled" value="true"/>
	<setting name="aggressiveLazyLoading" value="false"/>
</settings>

關於分步查詢和延遲載入可參考部落格:https://blog.csdn.net/jinhaijing/article/details/84173959

7、MyBatis 是如何進行分頁的?分頁外掛的原理是什麼? 

mybatis原生也是支援分頁的,但為了與資料庫語法解耦,實現的是邏輯分頁,首先將所有結果查詢出來,然後通過計算offset和limit,只返回部分結果,操作在記憶體中進行,所以也叫記憶體分頁,當資料量比較大的時候,肯定是不行的,核心類叫Rowbounds

使用原生分頁方法:

給介面中傳入RowBounds rowBounds = new RowBounds(0, 5);

listPo = userPoMapper.selectByExample(type,rowBounds);

 List<DictionaryPo> selectByExample(Integer type, RowBounds rowBounds) throws Exception;

 

注:PageHelper的使用

  • PageHelper的出現使得mybatis實現了物理上的分頁(之前一直聽說mybatis分頁很弱,而且實現的是邏輯分頁,非常影響效能,而且大大增加了OOM的可能),實際原理就是修改最後的執行sql,增加xi相應的分頁內容,是基於攔截器實現的。
  • 實現原理: 就是在StatementHandler之前進行攔截,對MappedStatement進行一系列的操作(大致就是拼上分頁sql)
<?xmlversion="1.0"encoding="UTF-8"?>
<!DOCTYPEconfiguration
            PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
            "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
      <!-- 配置分頁外掛 -->
      <plugins>
            <plugininterceptor="com.github.pagehelper.PageHelper">
                  <!-- 設定資料庫型別 Oracle,Mysql,MariaDB,SQLite,Hsqldb,PostgreSQL六種資料庫-->       
           <propertyname="dialect"value="mysql"/>
            </plugin>
      </plugins>
</configuration>

 

運用分頁外掛後mybatis的執行流程:

1、sqlsession: sqlsession是一次與資料庫的會話。在你每次訪問資料庫時都需要建立它(當然並不是說在sqlsession裡只能執行一次sql,你可以執行多次,當一旦關閉了Sqlsession就需要重新建立它)。sqlsession只能由SqlsessionFactory的openSession方法來完成建立
2、executor: 通過原始碼我們可以看到:executor其實只是DefaultSqlSession的一個屬性,而executor的每個操作都需要一個物件,那就是MappedStatement

3、MappedStatement: 這個東西個人看來就是用來封裝sql和返回結果的,我們很熟悉的可以看到id(個人猜想可能是mapper.xml中的那個id,即具體執行的是哪個sql)、parameterMap(應該就是mapper.xml中的那個parameterMap,即sql中變數的型別)、 resultMaps(大概就是你需要的返回物件型別)
可參考部落格:https://blog.csdn.net/leozhou13/article/details/50394242看怎麼自己製作一個分頁外掛

8、MyBatis 中什麼是邏輯分頁,什麼是物理分頁,分別有什麼優缺點? 

邏輯分頁:將所有結果查詢出來,然後通過計算offset和limit,只返回部分結果,操作在記憶體中進行,所以也叫記憶體分頁,當資料量比較大的時候,肯定是不行的,核心類叫Rowbounds

物理分頁:修改最後的執行sql,增加xi相應的分頁內容,是基於攔截器實現的

具體可看上一題

9、MyBatis 怎樣進行事務管理? 

mybatis支援兩種事務型別,分別為JdbcTransaction和ManagedTransaction。

Mybatis定義了一個事務型別介面Transaction,JdbcTransaction和ManagedTransaction兩種事務型別都實現了Transaction介面。

具體可參考部落格:https://blog.csdn.net/majinggogogo/article/details/72026693

10、比較 MyBatis 和 Hibernate 事務管理的區別 

11、MyBatis 框架有哪些註解? 

12、MyBatis 如何進行關聯關係(一對一,一對多,多對多),以及雙向關聯關係查詢? 

13、MyBatis 有幾種快取,獲取 Sqlsession 後,查詢資料的順序;MyBatis 中與Hibernate 中獲取 session 後,查詢資料的順序有什麼區別? 

14、簡述 MyBatis 的外掛執行原理,以及如何編寫一個外掛 

14、MyBatis 怎樣處理延遲載入? 

15、整合 Spring MVC+Spring+MyBatis 有哪些步驟? 

16、什麼是 MyBatis 的介面繫結,有什麼好處 

17、最佳實踐中,通常一個 Xml 對映檔案,都會寫一個 Dao 介面與之對應,請問,這個 Dao 介面的工作原理是什麼?Dao 接口裡的方法,引數不同時,方法能過載嗎? 

18、MyBatis 執行批量插入,能返回資料庫主鍵列表嗎? 

19、MyBatis 動態 SQL 是做什麼的?都有哪些動態 SQL?能簡述一下動態 SQL 的執行原理

20、MyBatis 是如何將 SQL 執行結果封裝為目標物件並返回的?都有哪些對映形式? 

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

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

30、簡述 MyBatis 的 Xml 對映檔案和 MyBatis 內部資料結構之間的對映關係? 

31、為什麼說 MyBatis 是半自動 ORM 對映工具?它與全自動的區別在哪裡? 

32、Mybatis 如何與 LOG4J 結合列印日誌 

33、MyBatis 如何執行儲存過程 

34、Mybatis 資料來源管理方式有幾種? 

35、MyBatis 引入 XXX.mapper 對映檔案有幾種方式? 

36、MyBatis 事務管理有幾種方式? 

37、什麼樣的需求使用 mybatis 框架更好?什麼樣的需求使用 hibernate 框架更好? 

38、解釋下 DefaultSqlSessionFactory 的作用? 

39、解釋下 SqlSessionFactoryBuilder 的作用? 

40、說出 MyBatis 快取和 Hibernate 快取的區別? 

41、MyBatis 中 sql 語句執行型別有幾種方式?(ExecutorType) 

42、MyBatis 中 ObjectFactory 是什麼? 

43、MyBatis 中 TypeHandler 是什麼?