MyBatis面試題集錦(精選)

點擊上方“Coder編程”,選擇“置頂公衆號”

技術文章第一時間送達!

MyBatis面試題集錦(精選)

以下來自網絡收集,找不到原文出處。此次主要爲了面試準備收集,希望對大家有所幫助~~~~

1. 簡單談一談MyBatis?

Mybatis是一個優秀的持久層框架,它對jdbc的操作數據庫的過程進行封裝,使得開發者只需要專注於SQL語句本身,而不用去關心註冊驅動,創建connection
Mybatis通過xml文件配置或者註解的方式將要執行的各種statement配置起來,並通過java對象和statement中的sql進行映射成最終執行的sql語句,最後由Mybatis框架執行sql並將結果映射成java對象並返回。

2.Mybatis分爲哪三層?

(1)API接口層:提供給外部使用的接口API
(2)數據處理層:負責具體的SQL
(3)基礎支撐層:負責最基礎的功能支撐,如連接管理,事務管理,配置加載和緩存處理

3. Mybatis相比JDBC有哪些優點?

(1)數據庫鏈接創建,釋放頻繁造成系統資源浪費會影響系統性能,使用數據庫可以解決
解決:在覈心配置文件SqlMapConfig.xml中配置數據鏈接池,使用數據鏈接池管理數據庫鏈接
(2)Sql寫在代碼中不易於維護,修改需要變動java代碼
            在映射文件XXXMapper.xml文件中配置sql語句與Java代碼分離
(3)向Sql語句傳輸參數麻煩,因爲Sql語句的WHERE條件不一定,可能多也可能少,佔位符需要和參數一一對應
           Mybatis可以自動將Java對象映射到sql語句
(4)對結果集解析麻煩,sql變化導致解析代碼變化,且解析前需要遍歷,將數據庫記錄封裝成pojo對象解析更加方便
            Mybatis可以自動將sql執行結果映射到Java對象

4. Mybatis #{}和${}的區別是什麼?

  • ${}Properties文件中的變量佔位符,它可以用於標籤屬性值和sql內部,屬於靜態文本替換,比如${driver}會被靜態替換爲com.mysql.jdbc.Driver

  • #{}是sql的參數佔位符,Mybatis會將sql中的#{}替換爲?號,在sql執行前會使用PreparedStatement的參數設置方法,按序給sql的?號佔位符設置參數值,比如ps.setInt(0, parameterValue)#{item.name}的取值方式爲使用反射從參數對象中獲取item對象的name屬性值,相當於param.getItem().getName()

  • #{}是預編譯處理,${}是字符串替換。

  • Mybatis在處理#{}時,會將sql中的#{}替換爲?號,調用PreparedStatement的set方法來賦值;Mybatis在處理${}時,就是把${}替換成變量的值。

  • 使用#{}可以有效的防止SQL注入,提高系統安全性。

  • 一般能用#的就別用$,$方式一般用於傳入數據庫對象,例如傳入表名。

用法

select * from user where name = #{name}; 
select * from user where name = '${name}'; 

必須加單引號

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

(1)Hibernate屬於全自動ORM映射工具,使用Hibernate查詢關聯對象或者關聯集合對象時,可以根據對象關係模型直接獲取,所以它是全自動的。而Mybatis在查詢關聯對象或關聯集合對象時,需要手動編寫sql來完成,所以,稱之爲半自動ORM映射工具。
(2)Mybatis直接編寫原生態sql,可以嚴格控制sql執行性能,靈活度高,非常適合對關係數據模型要求不高的軟件開發,因爲這類軟件需求變化頻繁,一但需求變化要求迅速輸出成果。但是靈活的前提是mybatis無法做到數據庫無關性,如果需要實現支持多種數據庫的軟件,則需要自定義多套sql映射文件,工作量大。
(3)Hibernate對象/關係映射能力強,數據庫無關性好,對於關係模型要求高的軟件,如果用hibernate開發可以節省很多代碼,提高效率。

6. 說一說Mybatis的優點缺點以及適用場合?

Mybaits的優點
(1)基於SQL語句編程,相當靈活,不會對應用程序或者數據庫的現有設計造成任何影響,SQL寫在XML裏,解除sql與程序代碼的耦合,便於統一管理;提供XML標籤,支持編寫動態SQL語句,並可重用。
(2)與JDBC相比,減少了50%以上的代碼量,消除了JDBC大量冗餘的代碼,不需要手動開關連接;
(3)很好的與各種數據庫兼容(因爲MyBatis使用JDBC來連接數據庫,所以只要JDBC支持的數據庫MyBatis都支持)。
(4)能夠與Spring很好的集成;
(5)提供映射標籤,支持對象與數據庫的ORM字段關係映射;提供對象關係映射標籤,支持對象關係組件維護。
MyBatis框架的缺點
(1)SQL語句的編寫工作量較大,尤其當字段多、關聯表多時,對開發人員編寫SQL語句的功底有一定要求。
(2)SQL語句依賴於數據庫,導致數據庫移植性差,不能隨意更換數據庫。
MyBatis框架適用場合
(1)MyBatis專注於SQL本身,是一個足夠靈活的DAO層解決方案。
(2)對性能的要求很高,或者需求變化較多的項目,如互聯網項目,MyBatis將是不錯的選擇。

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

能,JDBC都能,Mybatis當然也能。
1、對於支持生成自增主鍵的數據庫:增加 useGenerateKeyskeyProperty<insert>標籤屬性。
2、不支持生成自增主鍵的數據庫:使用<selectKey>

<insert id="insertAuthor" useGeneratedKeys="true"
    keyProperty="id">
  insert into Author (username, password, email, bio) values
  <foreach item="item" collection="list" separator=",">
    (#{item.username}, #{item.password}, #{item.email}, #{item.bio})
  </foreach>
</insert>

8.在開發過程中,經常遇到插入重複的現象,這種情況該如何解決呢?

插入的過程一般都是分兩步的:
先判斷是否存在記錄,沒有存在則插入否則不插入。如果存在併發操作,那麼同時進行了第一步,然後大家都發現沒有記錄,然後都插入了數據從而造成數據的重複。解決插入重複的思路可以是這樣的:

(1)判斷數據庫是否有數據,有的話則無所作爲。沒有數據的話,則進行下面第2步
(2)向redis set key,其中只有一個操作a會成功,其他併發的操作b和c會失敗的
(3)上面set key 成功的操作a,開始執行插入數據操作,無論是否插入數據成功,都在最後del key。【注】插入不成功可以多嘗試幾次,增加成功的概率。
(4)上面set key 失敗的操作b和c,sleep一下,然後再判斷數據庫是否有數據,有數據則無所做爲,沒有數據則重複上面的set key,此時是b和c在競爭,失敗者則無所作爲,成功者則開始插入數據,然後無論插入成功還是失敗則都要del key。【注】既然是併發了,本身就是異常情況,就沒有必要考慮用戶體驗了,就可以多sleep一會兒也無妨,不過對於單線程多事件處理的開發模式不要sleep太久。

總之,上面的過程就是:線程a 線程b 線程c,同時插入數據。如果線程a拿到鎖之後,讓它插入數據,它插入成功了,那麼線程b 線程c啥也不用做;它插入失敗了,線程b 線程c則搶鎖,誰搶到了誰插入數據,不管最後是否成功,程序走到此步就可以了,已經完成了既定兩個目標:執行插入,不重複插入。

9. 數據庫鏈接中斷如何處理?

我們知道,數據庫的訪問底層是通過tcp實現的,如果數據庫鏈接中斷,那麼應用程序是不知道的,是探測不出的,那麼程序會卡住,一直在等待,會等待嚇人的幾十分鐘,這種情況會把人鬱悶死,真不如及時來個彈框,告訴用戶系統暫時無法使用,讓用戶離開呢。所以,面對數據庫連接中斷的異常,該怎麼設置mybatis呢?

要想吃透這個問題,要明白鏈接中斷產生的原因。這裏面會涉及到網絡通信的問題。在數據庫鏈接中,connection操作可不是計算1+1這樣的形式,它低層是個循環處理過程,既然是循環處理過程那麼自然就跟時間扯上關係了,跟時間有關的設置有:max_idle_time,connect_timeout。max_idle_time表明最大的空閒時間,超過這個時間socket就會關閉,這樣操作系統會省心省力一些,畢竟操作系統維持一個socket也是花費不少精力的。connect_timeout表明鏈接的超時時間,我們知道,網絡環境就是跟潮水一樣,一波一波的,總是在波動,既是數據庫服務器活的槓槓的,但是因爲網絡用塞,客戶端仍然連不上服務器端,這個時候就要設置timeout,別一直傻等着。

10.當實體類中的屬性名和表中的字段名不一樣,怎麼辦 ?

第1種解決方案:通過在查詢的sql語句中定義字段名的別名,讓字段名的別名和實體類的屬性名一致。

<select id="getOrder" parametertype="int" resultetype="cn.mybatis.domain.order">
    select order_id id, order_no orderNo ,order_price price form orders where order_id=#{id};
</select>

第2種解決方案:通過來映射字段名和實體類屬性名的一一對應的關係。

<select id="getOrder" parameterType="int" resultMap="orderResultMap">
    select * from orders where order_id=#{id}
</select>

<resultMap id="orderResultMap" type="cn.mybatis.domain.order" >
    <!–用id屬性來映射主鍵字段–>
    <id property="id" column="order_id">
    <!–用result屬性來映射非主鍵字段,property爲實體類屬性名,column爲數據表中的屬性–>
    <result property= "orderNo" column="order_no"/>
    <result property="price" column="order_price"/>
</reslutMap>

11. 分別介紹一下JDBC核心對象與Mybatis核心對象?

JDBC有四個核心對象
(1)DriverManager,用於註冊數據庫連接
(2)Connection,與數據庫連接對象
(3)Statement/PrepareStatement,操作數據庫SQL語句的對象
(4)ResultSet,結果集或一張虛擬表

MyBatis也有四大核心對象
(1)SqlSession對象,該對象中包含了執行SQL語句的所有方法,類似於JDBC裏面的Connection
(2)Executor接口,它將根據SqlSession傳遞的參數動態地生成需要執行的SQL語句,同時負責查詢緩存的維護。類似於JDBC裏面的Statement/PrepareStatement
(3)MappedStatement對象,該對象是對映射SQL的封裝,用於存儲要映射的SQL語句的id、參數等信息。
(4)ResultHandler對象,用於對返回的結果進行處理,最終得到自己想要的數據格式或類型。可以自定義返回類型

12.Mybatis中Dao接口的工作原理是什麼?

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

Dao接口即Mapper接口。接口的全限名,就是映射文件中的namespace的值;接口的方法名,就是映射文件中MapperStatementid值;接口方法內的參數,就是傳遞給sql的參數。

Mapper接口是沒有實現類的,當調用接口方法時,接口全限名+方法名拼接字符串作爲key值,可唯一定位一個MapperStatement。在Mybatis中,每一個 <select><insert><update><delete>標籤,都會被解析爲一個MapperStatement對象。

舉例來說:cn.mybatis.mappers.StudentDao.findStudentById,可以唯一找到namespacecom.mybatis.mappers.StudentDao下面 idfindStudentById的 MapperStatement。

Mapper接口裏的方法,是不能重載的,因爲是使用 全限名+方法名 的保存和尋找策略。Mapper 接口的工作原理是JDK動態代理,Mybatis運行時會使用JDK動態代理爲Mapper接口生成代理對象proxy,代理對象會攔截接口方法,轉而執行MapperStatement所代表的sql,然後將sql執行結果返回。

13.spring和mybatis整合之後爲什麼一級緩存會失效?

一級緩存使用者可以隨時使用或者銷燬緩存,從SqlSession對象打開時緩存就已經存在。當關閉SqlSession對象緩存就失效。
當與spring整合的時候,直接跳過SqlSession對象,無法直接操作到SqlSession對象,spring在操作SqlSession的時候,不知道用戶什麼時候關閉,所以每調用完一個dao方法就關閉了,所以導致一級緩存失效。
如果開啓了事務,一級緩存就會生效,因爲開啓了事務,執行完dao就不會銷燬,因爲一旦銷燬,事務就沒有了,你開啓了事務,spring就知道你什麼時候需要結束

14. MyBatis與Hibernate有哪些不同?

(1)Mybatis和hibernate不同,它不完全是一個ORM框架,因爲MyBatis需要程序員自己編寫Sql語句。
(2)Mybatis直接編寫原生態sql,可以嚴格控制sql執行性能,靈活度高,非常適合對關係數據模型要求不高的軟件開發,因爲這類軟件需求變化頻繁,一但需求變化要求迅速輸出成果。但是靈活的前提是mybatis無法做到數據庫無關性,如果需要實現支持多種數據庫的軟件,則需要自定義多套sql映射文件,工作量大。
(3)Hibernate對象/關係映射能力強,數據庫無關性好,對於關係模型要求高的軟件,如果用hibernate開發可以節省很多代碼,提高效率。

15. 模糊查詢like語句該怎麼寫?

第1種:在Java代碼中添加sql通配符。

String wildcardname = “%smi%”;
list<name> names = mapper.selectlike(wildcardname);

<select id=”selectlike”>
 select * from foo where bar like #{value}
</select>

第2種:在sql語句中拼接通配符,會引起sql注入

String wildcardname = “smi”;
list<name> names = mapper.selectlike(wildcardname);

<select id=”selectlike”>
     select * from foo where bar like "%"#{value}"%"
</select>

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

  • Mybatis使用RowBounds對象進行分頁,它是針對ResultSet結果集執行的內存分頁,而非物理分頁。可以在sql內直接書寫帶有物理分頁的參數來完成物理分頁功能,也可以使用分頁插件來完成物理分頁。

  • 分頁插件的基本原理是使用Mybatis提供的插件接口,實現自定義插件,在插件的攔截方法內攔截待執行的sql,然後重寫sql,根據dialect方言,添加對應的物理分頁語句和物理分頁參數。

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

第一種是使用標籤,逐一定義數據庫列名和對象屬性名之間的映射關係。

第二種是使用sql列的別名功能,將列的別名書寫爲對象屬性名。

有了列名與屬性名的映射關係後,Mybatis通過反射創建對象,同時使用反射給對象的屬性逐一賦值並返回,那些找不到映射關係的屬性,是無法完成賦值的。

18. Mybatis動態sql有什麼用?執行原理?有哪些動態sql?

Mybatis動態sql可以在Xml映射文件內,以標籤的形式編寫動態sql,執行原理是根據表達式的值 完成邏輯判斷並動態拼接sql的功能。

Mybatis提供了9種動態sql標籤:trim | where | set | foreach | if | choose | when | otherwise | bind。

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

<resultMap><parameterMap><sql><include><selectKey>,加上動態sql的9個標籤,其中<sql>sql片段標籤,通過<include>標籤引入sql片段,<selectKey>爲不支持自增的主鍵生成策略標籤。

20.在mapper中如何傳遞多個參數?

(1)第一種:

//DAO層的函數
Public UserselectUser(String name,String area);  
    //對應的xml,#{0}代表接收的是dao層中的第一個參數,#{1}代表dao層中第二參數,更多參數一致往後加即可。
<select id="selectUser"resultMap="BaseResultMap">  
    select *  fromuser_user_t   whereuser_name = #{0} anduser_area=#{1}  
</select>  

(2)第二種:使用 @param 註解:

public interface usermapper {
   user selectuser(@param(“username”) string username,@param(“hashedpassword”) string hashedpassword);
}

然後,就可以在xml像下面這樣使用(推薦封裝爲一個map,作爲單個參數傳遞給mapper):

<select id=”selectuser” resulttype=”user”>
         select id, username, hashedpassword
         from some_table
         where username = #{username}
         and hashedpassword = #{hashedpassword}
</select>

(3)第三種:多個參數封裝成map

try{
//映射文件的命名空間.SQL片段的ID,就可以調用對應的映射文件中的SQL
//由於我們的參數超過了兩個,而方法中只有一個Object參數收集,因此我們使用Map集合來裝載我們的參數
Map<String, Object> map = new HashMap();
     map.put("start", start);
     map.put("end", end);
     return sqlSession.selectList("StudentID.pagination", map);
 }catch(Exception e){
     e.printStackTrace();
     sqlSession.rollback();
    throw e; 
 }finally{
    MybatisUtil.closeSqlSession();
 }

採用註解的方式進行增刪改查也比較類似~

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

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

22.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,支持延遲加載的原理都是一樣的。

23.介紹一下Mybatis的一級、二級緩存?

1)一級緩存: 基於 PerpetualCache 的 HashMap 本地緩存,其存儲作用域爲 Session,當 Session flush 或 close 之後,該 Session 中的所有 Cache 就將清空,默認打開一級緩存。

2)二級緩存與一級緩存其機制相同,默認也是採用 PerpetualCache,HashMap 存儲,不同在於其存儲作用域爲 Mapper(Namespace),並且可自定義存儲源,如 Ehcache。默認不打開二級緩存,要開啓二級緩存,使用二級緩存屬性類需要實現Serializable序列化接口(可用來保存對象的狀態),可在它的映射文件中配置;

3)對於緩存數據更新機制,當某一個作用域(一級緩存 Session/二級緩存Namespaces)的進行了C/U/D 操作後,默認該作用域下所有 select 中的緩存將被 clear 掉並重新更新,如果開啓了二級緩存,則只根據配置判斷是否刷新。

24. 使用MyBatis的mapper接口調用時有哪些要求?

①  Mapper接口方法名和mapper.xml中定義的每個sql的id相同;
②  Mapper接口方法的輸入參數類型和mapper.xml中定義的每個sql 的parameterType的類型相同;
③  Mapper接口方法的輸出參數類型和mapper.xml中定義的每個sql的resultType的類型相同;
④  Mapper.xml文件中的namespace即是mapper接口的類路徑。

25.二級緩存中要注意的事情?

二級緩存裏面的數據不能存放那種一直累加到很大的數據
二級緩存是基於命名空間的,當多個命名空間操作同一張表的時候,最好不要用二級緩存,當一個命名空間insert之的一,不會刷新緩存,用另一個命名空間select的時候還是會查詢緩存

26.MyBatis底層實現原理?

MyBatis是一個持久層框架,實現了ORM思想,可以將查詢的結果集自動轉換成Java對象,也可以將Java對象轉換成一條數據插入到數據庫表當中。
那麼,查詢結果集是如何自動轉換成Java對象的呢?實際上這裏使用了反射機制,在配置文件中假設編寫了一條select語句,查詢之後,列名與屬性名要一一對應(不對應的可以採用給列起別名),然後每個列名前添加“set”,通過反射機制獲取set方法,然後再通過反射機制的method.invoke()來調用這個set方法,給Java對象的屬性賦值。這樣就完成了對象的封裝。
另外,Java對象是如何轉換成一條記錄插入到數據庫的呢?假設在配置文件中編寫了一條insert語句,那麼這條語句需要的值從哪裏來呢,在mybatismapper配置中有parameterType屬性,該屬性是專門給sql語句佔位符傳值的,其實這裏也是使用了反射機制,其中sql語句的佔位符采用#{},其中大括號當中需要提供java對象的屬性名,該屬性名和get進行拼接得到get方法名,然後通過反射機制獲取該get方法,再通過method.invoke()來調用這個get方法,這樣就可以獲取到對應的屬性值,然後傳入了。

其實MyBatis設計最牛的地方當然是採用JDK動態代理的方式生成DAO接口的實現類了。其中DAO接口中的每一個方法名對應sql語句的id。DAO接口中的方法不允許重載,因爲id是不允許重複的。

參考文章:

http://www.mybatis.cn/category/interview/

推薦

Spring面試題集錦(精選)

SpringMVC面試題集錦(精選)

Spring全家桶註解一覽(精選)

https://www.processon.com/i/5cd53c2fe4b01941c8cf1c21(ProcessOn是一個在線作圖工具的聚合平臺~)

文末

歡迎關注個人微信公衆號:Coder編程
歡迎關注Coder編程公衆號,主要分享數據結構與算法、Java相關知識體系、框架知識及原理、Spring全家桶、微服務項目實戰、DevOps實踐之路、每日一篇互聯網大廠面試或筆試題以及PMP項目管理知識等。更多精彩內容正在路上~

文章收錄至
Github: https://github.com/CoderMerlin/coder-programming
Gitee: https://gitee.com/573059382/coder-programming
歡迎關注並star~

微信公衆號
發佈了211 篇原創文章 · 獲贊 248 · 訪問量 53萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章