Java面試題沖刺第十天--MyBatis2
面試題1:說說你對Mybatis的理解?
Mybatis是一個持久層的框架,是apache下的頂級項目。

Mybatis內(nèi)部封裝了jdbc,開發(fā)者只需要關(guān)注sql語句本身,而不需要花費精力去處理加載驅(qū)動、創(chuàng)建連接、創(chuàng)建statement等繁雜的過程。
mybatis通過xml或注解的方式將要執(zhí)行的各種statement配置起來,并通過java對象和statement中sql的動態(tài)參數(shù)進(jìn)行映射生成最終執(zhí)行的sql語句,最后由mybatis框架執(zhí)行sql并將結(jié)果映射為java對象并返回。
MyBatis 支持定制化 SQL、存儲過程以及高級映射。MyBatis 避免了幾乎所有的 JDBC 代碼和手動設(shè)置參數(shù)以及獲取結(jié)果集。MyBatis 可以使用簡單的 XML 或注解來配置和映射原生信息,將接口和 Java 的 POJO映射成數(shù)據(jù)庫中的記錄。
追問1:說一下MyBatis的工作原理和流程吧。
1.讀取 MyBatis 配置文件:mybatis-config.xml為 MyBatis 的全局配置文件,配置了 MyBatis 的運行環(huán)境等信息,例如數(shù)據(jù)庫連接信息。
2.加載映射文件:映射文件即 SQL 映射文件,該文件中配置了操作數(shù)據(jù)庫的 SQL 語句,需要在 MyBatis 配置文件 mybatis-config.xml 中加載。

3.構(gòu)造會話工廠:通過 MyBatis 的環(huán)境等配置信息構(gòu)建會話工廠 SqlSessionFactory。
4.創(chuàng)建會話對象:由會話工廠創(chuàng)建 SqlSession 對象,該對象中包含了執(zhí)行 SQL 語句的所有方法。
5.Executor 執(zhí)行器:MyBatis 底層定義了一個 Executor 接口來操作數(shù)據(jù)庫,它將根據(jù) SqlSession 傳遞的參數(shù)動態(tài)地生成需要執(zhí)行的 SQL 語句,同時負(fù)責(zé)查詢緩存的維護(hù)。
6.MappedStatement 對象:在 Executor 接口的執(zhí)行方法中有一個 MappedStatement 類型的參數(shù),該參數(shù)是對映射信息的封裝,用于存儲要映射的 SQL 語句的 id、參數(shù)等信息。
7.輸入?yún)?shù)映射:輸入?yún)?shù)類型可以是 Map、List 等集合類型,也可以是基本數(shù)據(jù)類型和 POJO 類型。輸入?yún)?shù)映射過程類似于 JDBC 對 preparedStatement 對象設(shè)置參數(shù)的過程。
8.輸出結(jié)果映射:輸出結(jié)果類型可以是 Map、 List 等集合類型,也可以是基本數(shù)據(jù)類型和 POJO 類型。輸出結(jié)果映射過程類似于 JDBC 對結(jié)果集的解析過程。
追問2:列舉幾個MyBatis的核心組件,說說分別干啥用?
| 組件名稱 | 功能 |
|---|---|
| SqlSession | MyBatis工作的主要頂層API,用于和數(shù)據(jù)庫交互的會話,完成必要數(shù)據(jù)庫增刪改查功能 |
| Executor | MyBatis執(zhí)行器,是MyBatis 調(diào)度的核心,負(fù)責(zé)SQL語句的生成和查詢緩存的維護(hù) |
| StatementHandler | 封裝了JDBC Statement操作,負(fù)責(zé)對JDBC statement 的操作,如設(shè)置參數(shù)、將Statement結(jié)果集轉(zhuǎn)換成List集合。 |
| ParameterHandler | 負(fù)責(zé)對用戶傳遞的參數(shù)轉(zhuǎn)換成JDBC Statement 所需要的參數(shù), |
| ResultSetHandler | 負(fù)責(zé)將JDBC返回的ResultSet結(jié)果集對象轉(zhuǎn)換成List類型的集合; |
| TypeHandler | 負(fù)責(zé)java數(shù)據(jù)類型和jdbc數(shù)據(jù)類型之間的映射和轉(zhuǎn)換 |
| MappedStatement | 維護(hù)一條<select|update|delete|insert>節(jié)點的封裝, |
| SqlSource | 負(fù)責(zé)根據(jù)用戶傳遞的parameterObject,動態(tài)生成SQL語句,將信息封裝到BoundSql對象中,并返回 |
| BoundSql | 表示動態(tài)生成的SQL語句以及相應(yīng)的參數(shù)信息 |
| Configuration | MyBatis所有的配置信息都維持在Configuration對象之中。 |
面試題2:(問幾個實際使用的問題)Mybatis動態(tài)sql是做什么的?都有哪些動態(tài)sql?
動態(tài)sql是指在進(jìn)行sql操作的時候,傳入的參數(shù)對象或者參數(shù)值,根據(jù)匹配的條件,有可能需要動態(tài)的去判斷是否為空、循環(huán)、拼接等情況,用于輔助開發(fā)者更方便的進(jìn)行半自動化的SQL開發(fā);
Mybatis動態(tài)sql可以讓我們在Xml映射文件內(nèi),以標(biāo)簽的形式編寫動態(tài)sql,完成邏輯判斷和動態(tài)拼接sql的功能。
Mybatis提供了9種動態(tài)sql標(biāo)簽:trim、where、set、foreach、if、choose、when、otherwise、bind。
其執(zhí)行原理為,使用OGNL從sql參數(shù)對象中計算表達(dá)式的值,根據(jù)表達(dá)式的值動態(tài)拼接sql,以此來完成動態(tài)sql的功能。
追問1:Xml映射文件中,除了常見的select|insert|updae|delete標(biāo)簽之外,你還常用哪些標(biāo)簽?
用于Mybatis的Mapper文件中,有很多常見標(biāo)簽如:<resultMap>、<parameterMap>、<sql>、<include>、<namespace>等等,需要的話可以挨個解釋一下其作用。
追問2:Mybatis是如何將sql執(zhí)行結(jié)果封裝為目標(biāo)對象并返回的?都有哪些映射形式?
我們首先要根據(jù)代碼中實體類和數(shù)據(jù)表中的列名是否一一對應(yīng),如果對應(yīng)上就可以直接返回。但多字段無法對應(yīng)的情況怎么返回?
第一種:使用sql列的別名功能,將列的別名書寫為對象屬性名,強(qiáng)行與實體類保持一致,但不方便維護(hù)。
第二種:使用resultMap標(biāo)簽,逐一定義數(shù)據(jù)庫列名和對象屬性名之間的映射關(guān)系,處理起來就比較清晰。
<resultMap type="com.xxxx.entity.Task" id="task"> <id column="taskId" property="id"/> <result column="taskName" property="task_name"/> <result column="frequency" property="frequency"/> <result column="updateTime" property="updateTime"/> <result column="description" property="description"/> <result column="modifier" property="modifier"/> <result column="remark" property="remark"/> </resultMap>
有了列名與屬性名的映射關(guān)系后,Mybatis通過反射創(chuàng)建對象,同時使用反射給對象的屬性逐一賦值并返回,那些找不到映射關(guān)系的屬性,是無法完成賦值的。
追問3:MyBatis中接口綁定你都用過哪幾種方式?
我們一般通過注解綁定或在Mapper中進(jìn)行綁定:
1.注解綁定:在接口的方法上面加上 @Select、@Update等注解里面包含Sql語句來綁定,Sql語句比較簡單的時候,推薦注解綁定。
2.Mapper標(biāo)簽綁定:通過xml里面寫SQL來綁定, 指定xml映射文件里面的namespace必須為接口類的全路徑名,select標(biāo)簽中的id來定義接口名稱,須一一對應(yīng)。
<mapper namespace="com.xxxxx.dao.TaskDao"> <!-- 查詢?nèi)蝿?wù)信息 --> <select id="getAllTaskDao" parameterType="int" resultMap="task" > SELECT taskId,taskName,frequency,updateTime,description,modifier,remark from task_info </select> </mapper>
追問4:我們知道insert 方法總是返回一個int值 ,這個值代表的是插入的行數(shù)。那我如何獲取自動生成的主鍵(id)值?
如果采用自增長策略,自動生成的鍵值在 insert 方法執(zhí)行完后可以被設(shè)置到傳入的參數(shù)對象中。
<insert id=”insertUser” usegeneratedkeys=”true” keyproperty=”id”>
insert into users_info (id,name) values (null,#{name})
</insert>
追問5:有兩個XML文件和這個Dao建立關(guān)系,如何避免沖突?
不管有幾個XML和Dao建立關(guān)系,只要保證namespace+id唯一即可。
面試題3:用過Mybatis的一級、二級緩存么?用過的話說一下原理。
先說緩存,合理使用緩存是優(yōu)化中最常見的,將從數(shù)據(jù)庫中查詢出來的數(shù)據(jù)放入緩存中,下次使用時不必從數(shù)據(jù)庫查詢,而是直接從緩存中讀取,避免頻繁操作數(shù)據(jù)庫,減輕數(shù)據(jù)庫的壓力,同時提高系統(tǒng)性能。
一級緩存
一級緩存是SqlSession級別的緩存。在操作數(shù)據(jù)庫時需要構(gòu)造sqlSession對象,在對象中有一個數(shù)據(jù)結(jié)構(gòu)用于存儲緩存數(shù)據(jù)。不同的sqlSession之間的緩存數(shù)據(jù)區(qū)域是互相不影響的。也就是他只能作用在同一個sqlSession中,不同的sqlSession中的緩存是互相不能讀取的。
一級緩存的工作原理:
- 與Redis同理,用戶發(fā)起查詢請求,查找某條數(shù)據(jù),sqlSession先去緩存中查找,是否有該數(shù)據(jù),如果有,直接返回;如果沒有,從數(shù)據(jù)庫中查詢,并將查詢到的數(shù)據(jù)放入一級緩存區(qū)域,供下次查找使用。
- 但sqlSession執(zhí)行commit,即
增刪改操作時會清空緩存。這么做的目的是避免臟讀。
二級緩存
為什么要有二級緩存?
二級緩存是mapper級別的緩存,多個SqlSession去操作同一個Mapper的sql語句,多個SqlSession可以共用二級緩存,二級緩存是跨SqlSession的。二級緩存的作用范圍更大。
在實際開發(fā)中,MyBatis通常和Spring進(jìn)行整合開發(fā)。Spring將事務(wù)放到Service中管理,對于每一個service中的sqlsession是不同的,這是通過mybatis-spring中的org.mybatis.spring.mapper.MapperScannerConfigurer創(chuàng)建sqlsession自動注入到service中的。 每次查詢之后都要進(jìn)行關(guān)閉sqlSession,關(guān)閉之后數(shù)據(jù)即被清空。所以spring整合之后,如果沒有事務(wù),一級緩存是沒有意義的。
二級緩存的配置方式
MyBatis對二級緩存的支持粒度很細(xì),它會指定某一條查詢語句是否使用二級緩存。
3個必要配置:
MyBatis支持二級緩存的總開關(guān),全局配置變量cacheEnabled=true
在mybatis-config.xml添加
<settings>
<setting name="cacheEnabled" value="true"/><!-- 二級緩存 -->
</settings>
該select語句所在的Mapper,配置<cache> 或<cached-ref>節(jié)點
<mapper namespace="com.xxxx.dao.TaskDao">
<cache/>
<insert id="addxxx" parameterType="xxx" >
insert into xxx (name, price) values (#{name}, #{price})
</insert>
<select id="listXXX" resultType="xxx">
select * from xxx
</select>
</mapper>
該select語句的參數(shù) useCache=true
<select id="selectXXXX" resultMap="task" parameterType="java.util.Map" useCache="true">
追問1:一級緩存和二級緩存的使用順序
MyBatis查詢數(shù)據(jù)的順序是:
二級緩存 👉 一級緩存 👉 數(shù)據(jù)庫
總結(jié)
本篇文章就到這里了,希望能給你帶來幫助,也希望您能夠多多關(guān)注腳本之家的更多內(nèi)容!
相關(guān)文章
劍指Offer之Java算法習(xí)題精講鏈表專項訓(xùn)練
跟著思路走,之后從簡單題入手,反復(fù)去看,做過之后可能會忘記,之后再做一次,記不住就反復(fù)做,反復(fù)尋求思路和規(guī)律,慢慢積累就會發(fā)現(xiàn)質(zhì)的變化2022-03-03
Springmvc應(yīng)用Mongodb分頁實現(xiàn)
這篇文章主要為大家詳細(xì)介紹了Springmvc應(yīng)用Mongodb分頁實現(xiàn),具有一定的參考價值,感興趣的小伙伴們可以參考一下2017-11-11
Java 中jasperReport實現(xiàn)動態(tài)列打印的實現(xiàn)代碼
這篇文章主要介紹了Java 中jasperReport實現(xiàn)動態(tài)列打印的實現(xiàn)代碼的相關(guān)資料,希望通過本文大家能掌握這部分內(nèi)容,需要的朋友可以參考下2017-09-09
Java中@Autowired與@Resource注解的區(qū)別詳解
這篇文章主要介紹了Java中@Autowired與@Resource注解的區(qū)別詳解,@Resource的作用相當(dāng)于@Autowired,只不過@Autowired按byType自動注入,而@Resource默認(rèn)按 byName自動注入罷了,@Resource有兩個屬性是比較重要的,需要的朋友可以參考下2023-11-11
Java 數(shù)據(jù)結(jié)構(gòu)線性表之順序存儲詳解原理
線性表的順序存儲是指用一組地址連續(xù)的存儲單元依次存儲線性表中的各個元素、使得線性表中在邏輯結(jié)構(gòu)上相鄰的數(shù)據(jù)元素存儲在相鄰的物理存儲單元中,即通過數(shù)據(jù)元素物理存儲的相鄰關(guān)系來反映數(shù)據(jù)元素之間邏輯上的相鄰關(guān)系2021-10-10
Java class文件格式之屬性詳解_動力節(jié)點java學(xué)院整理
這篇文章主要介紹了Java class文件格式之屬性詳解,需要的朋友可以參考下2017-06-06

