1000道互聯(lián)網(wǎng)java面試題(一)Mybatis面試題

打個(gè)廣告,java技術(shù)交流群130031711,群里好多學(xué)習(xí)資料。歡迎各位萌新大佬踴躍加入!

本文資料來源

我不知道別人是不是也像我這樣,看過一次的題目,過不幾天就忘了。尤其是一些冷門的知識(shí)點(diǎn),可能轉(zhuǎn)頭就忘了。
算了,想說的話太多又覺得沒有必要。
這又是一個(gè)記錄系列??催^的東西一定要記錄下來才會(huì)深刻。
然后這個(gè)資料是一個(gè)朋友傳給我的,在此表示感謝。另外這個(gè)資料的名字就叫做1000道題。
因?yàn)槲乙呀?jīng)看過一半左右了,有些我覺得略有落伍的知識(shí)點(diǎn)或者不好理解的知識(shí)點(diǎn)我就不列出來了。所以不出意外我整理出來的都是我覺得被問到的幾率大,好理解而且有用的知識(shí)點(diǎn)。個(gè)人色彩比較濃,不喜勿噴。

Mybatis面試題

1. 什么是mybatis?
百度百科-orm框架
  1. Mybatis 是一個(gè)半 ORM(對(duì)象關(guān)系映射)框架,它封裝了 JDBC,開發(fā)時(shí)只需要關(guān)注 SQL 語句本身,不需要處理加載驅(qū)動(dòng)、創(chuàng)建連接、創(chuàng)建 statement 等繁雜的過程。程序員直接編寫原生sql語句,可以嚴(yán)格控制 sql 執(zhí)行性能,靈活度高。
  2. MyBatis 可以使用 XML 或注解來配置和映射原生信息,將 POJO(實(shí)體對(duì)象) 映射成數(shù)據(jù)庫(kù)中的記錄,避免了幾乎所有的 JDBC 代碼和手動(dòng)設(shè)置參數(shù)以及獲取結(jié)果集。
  3. 通過 xml 文件或注解的方式將要執(zhí)行的各種 statement 配置起來,并通過 java 對(duì)象和 statement 中 sql 的動(dòng)態(tài)參數(shù)進(jìn)行映射生成最終執(zhí)行的 sql 語句,最后由 mybatis框架執(zhí)行sql并將結(jié)果映射為 java 對(duì)象并返回。(從執(zhí)行 sql 到返 回 result 的過程)。

簡(jiǎn)單來講,我覺得最主要的一句話概括:就是對(duì)jdbc進(jìn)行了封裝,從而使得開發(fā)更加容易。另外我不知道這個(gè)書是什么時(shí)候出的,但是在2020年的現(xiàn)在,單純使用mybatis的也不算很多了吧。反正我是從去年開始用mybatis plus的。在mybatis的基礎(chǔ)上又是一層封裝。不說實(shí)現(xiàn)的原理,反正直接使用給人的感覺和jpa越來越像了,都是有了簡(jiǎn)單sql的直接調(diào)用。比如insert,update,del,簡(jiǎn)單的select等。而且mp的條件構(gòu)造器是真的好用。。哈哈!扯遠(yuǎn)了,上面的三點(diǎn)是書中對(duì)mybatis的介紹,都是很容易理解的,我就不多說了。

2. Mybaits的優(yōu)點(diǎn):

其實(shí)優(yōu)點(diǎn)要相對(duì)用說的,我感覺這個(gè)答案適合用自己的話說。

  1. 首先,與原生jdbc相比,因?yàn)樽隽艘粚臃庋b,所以減少了很多繁雜的操作,消除了大量冗余的代碼。不需要手動(dòng)開關(guān)連接。
  2. 基于sql語句編程,比較靈活。不會(huì)對(duì)應(yīng)用程序或者數(shù)據(jù)庫(kù)的現(xiàn)有設(shè)計(jì)有任何影響。而且mybatis的sql可以寫在XML文件里,也算是解除了sql與程序代碼的耦合。便于統(tǒng)一管理。支持編寫動(dòng)態(tài)sql語句。(ps:mybatis也支持注釋上寫sql。不過一般不建議這么用)
  3. jdbc支持的數(shù)據(jù)庫(kù)mybatis都支持(這個(gè)很好理解吧)。很好的數(shù)據(jù)庫(kù)兼容。
  4. 能與spring很好的集成。
  5. 提供映射標(biāo)簽,支持對(duì)象與數(shù)據(jù)庫(kù)的字段關(guān)系映射(我理解就是能把對(duì)象持久化到數(shù)據(jù)庫(kù)中)。
3. Mybatis的缺點(diǎn):
  1. SQL 語句的編寫工作量較大,尤其當(dāng)字段多、關(guān)聯(lián)表多時(shí),對(duì)開發(fā)人員編寫 SQL 語句的功底有一定要求。(感覺這個(gè)缺點(diǎn)其實(shí)不算是缺點(diǎn)。因?yàn)橛胘dbc還是用hibernate也都是這樣的。尤其是復(fù)雜sql 的時(shí)候hibernate還不如mybatis呢)
  2. SQL 語句依賴于數(shù)據(jù)庫(kù),導(dǎo)致數(shù)據(jù)庫(kù)移植性差,不能隨意更換數(shù)據(jù)庫(kù)。(這個(gè)怎么說呢,確實(shí)是和hibernate相比這個(gè)算是一個(gè)缺點(diǎn)。不過也僅僅限于簡(jiǎn)單的sql。我記得我以前就說過:復(fù)雜SQL多的用mybatis。簡(jiǎn)單sql多的可以直接jpa。我現(xiàn)在也是這個(gè)觀點(diǎn)。不過mp的出現(xiàn),讓mybatis處理簡(jiǎn)單sql也更方便了一些。現(xiàn)在總體上我還是喜歡mybatis)
4. Mybatis框架適用于什么場(chǎng)景?
  1. MyBatis 專注于 SQL 本身,是一個(gè)足夠靈活的 DAO 層解決方案。
  2. 對(duì)性能的要求很高,或者需求變化較多的項(xiàng)目,如互聯(lián)網(wǎng)項(xiàng)目,MyBatis 將是 不錯(cuò)的選擇。

其實(shí)我覺得這個(gè)題的答案太籠統(tǒng)了。而且我有好多技術(shù)交流群嘛,看到過的jpa黨和mybatis黨吵架好多次。所以聽到看到好說說法。首先什么叫對(duì)性能要求高使用mybatis?jpa性能不好?這個(gè)是一種傳統(tǒng)的錯(cuò)覺。最開始我記得我學(xué)習(xí)的時(shí)候確實(shí)聽說過hibernate比較重,性能略差。但是我之前一個(gè)群友較真,用兩個(gè)框架分別跑一樣功能的接口。hibernate偶爾性能比mybatis還好呢!jpa沒用想的那么差了。
另外需求變化較多的項(xiàng)目。都是簡(jiǎn)單的crud,哪怕需求變化較多也應(yīng)該是jpa首選吧?我覺得我的角度,是當(dāng)字段多,關(guān)聯(lián)表多,業(yè)務(wù)功能復(fù)雜的時(shí)候,mybatis是首選。
但是具體情況具體分析,這個(gè)又不是1+1等于2這種有確切答案的。我就不多BB了。

5. Mybatis與Hibernate有哪些不同?
  1. Mybatis 和 hibernate 不同,它不完全是一個(gè) ORM 框架,因?yàn)?MyBatis 需要程序員自己編寫 Sql 語句。
  2. Mybatis 直接編寫原生態(tài) sql,可以嚴(yán)格控制 sql 執(zhí)行性能,靈活度高,非常適合對(duì)關(guān)系數(shù)據(jù)模型要求不高的軟件開發(fā),因?yàn)檫@類軟件需求變化頻繁,一但需求變化要求迅速輸出成果。但是靈活的前提是 mybatis 無法做到數(shù)據(jù)庫(kù)無關(guān)性, 如果需要實(shí)現(xiàn)支持多種數(shù)據(jù)庫(kù)的軟件,則需要自定義多套 sql 映射文件,工作量大。
  3. Hibernate 對(duì)象/關(guān)系映射能力強(qiáng),數(shù)據(jù)庫(kù)無關(guān)性好,對(duì)于關(guān)系模型要求高的軟件,如果用 hibernate 開發(fā)可以節(jié)省很多代碼,提高效率

又來BB了,首先其實(shí)兩者最大的不同就是Mybatis是半ORM框架,要自己寫sql語句。而Hibernate是全自動(dòng)ORM框架,hibernate可以自動(dòng)生成SQL語句,自動(dòng)執(zhí)行。而剩下的點(diǎn)是這個(gè)問題衍生出來的:

  • 因?yàn)閙ybatis自己寫sql,所以如果數(shù)據(jù)庫(kù)換了,sql也不一樣。所以要全盤更改或者一開始就寫多套sql映射文件。而Hibernate不同,sql也不是我們寫的,數(shù)據(jù)庫(kù)換了hibernate自己就會(huì)換語句??偨Y(jié)起來數(shù)據(jù)庫(kù)無關(guān)性不一樣。
  • 另外因?yàn)閔ibernate是對(duì)象/數(shù)據(jù)庫(kù)直接映射的,所以如果數(shù)據(jù)庫(kù)頻繁改動(dòng),會(huì)對(duì)代碼的改動(dòng)較大(不過我覺得現(xiàn)在二者的高度封裝,在單純的調(diào)用上來講已經(jīng)差不多了)。而mybatis則相對(duì)靈活。
6. #{}和${}的區(qū)別是什么?

這個(gè)題我覺得蠻好的,劃重點(diǎn)?。?!

#{}是預(yù)編譯處理,${}是字符串替換。

Mybatis 在處理#{}時(shí),會(huì)將 sql 中的#{}替換為?號(hào),調(diào)用 PreparedStatement 的 set 方法來賦值;
Mybatis 在處理{}時(shí),就是把{}替換成變量的值。
使用#{}可以有效的防止 SQL 注入,提高系統(tǒng)安全性。

7. 當(dāng)實(shí)體類中的屬性名和表中的字段名不一樣 ,怎么辦?

這個(gè)題怎么說呢,因?yàn)橘Y料中是用xml實(shí)現(xiàn)的,
方法1:在sql語句中用別名的方法實(shí)現(xiàn)的。

<select id=”selectorder” parametertype=”int” resultetype=” me.gacl.domain.order”>
 select order_id id, order_no orderno ,order_price price form orders where order_id=#{id}; 
</select>

方法2: 通過<resultMap>來映射字段名和實(shí)體類屬性名的一一對(duì)應(yīng)的關(guān)系。

<select id="getOrder" parameterType="int" resultMap="orderresultmap">
 select * from orders where order_id=#{id}
 </select>
<resultMap type=”me.gacl.domain.order” id=”orderresultmap”> 
<!–用id屬性來映射主鍵字段–>
 <id property=”id” column=”order_id”>
<!–用result屬性來映射非主鍵字段,property為實(shí)體類屬性名,column 為數(shù)據(jù)表中的屬性–> 
<result property = “orderno” column =”order_no”/>
 <result property=”price” column=”order_price” /> 
</reslutMap>

但是說真的,我感覺我快一年沒寫xml了。。用mybatis plus的時(shí)候,可以直接在實(shí)體對(duì)象上用注釋標(biāo)注出這個(gè)屬性的數(shù)據(jù)庫(kù)中字段名是什么(如果是下劃線轉(zhuǎn)駝峰是不用寫的。這個(gè)是默認(rèn)配置)。

8. 模糊查詢like語句該怎么寫?

這個(gè)題怎么說呢,我覺得是蠻水的,然后答案還不少,都是xml代碼。所以我也不說了,其實(shí)就是在參數(shù)上加通配符。想怎么模糊就怎么加。另外也可以在sql語句中拼接“%”,但是我沒這么用過。據(jù)說是會(huì)引入sql注入。
然后再吹一波mybatis plus。mp的條件構(gòu)造器中可以直接like(字段,值)。說實(shí)話我沒看過mp源碼是怎么實(shí)現(xiàn)的。但是既然都成為框架了,我還是覺得應(yīng)該很安全的。

9. 通常一個(gè)Xml映射文件,都會(huì)寫一個(gè)Dao接口與之對(duì)應(yīng),請(qǐng)問,這個(gè)Dao接口的工作原理是什么?Dao接口里的方法,參數(shù)不同時(shí),方法能重載嗎?

Dao 接口即 Mapper 接口。接口的全限名,就是映射文件中的 namespace 的值; 接口的方法名,就是映射文件中 Mapper 的 Statement 的 id 值;接口方法內(nèi)的參數(shù)就是傳遞給 sql 的參數(shù)。
Mapper 接口是沒有實(shí)現(xiàn)類的,當(dāng)調(diào)用接口方法時(shí),接口全限名+方法名拼接字符 串作為 key 值,可唯一定位一個(gè) MapperStatement。在 Mybatis 中,每一個(gè) <select>、<insert>、<update>、<delete>標(biāo)簽,都會(huì)被解析為一個(gè) MapperStatement 對(duì)象。
Mapper 接口里的方法,是不能重載的,因?yàn)槭鞘褂?strong>全限名+方法名的保存和尋找策略。Mapper 接口的工作原理是 JDK 動(dòng)態(tài)代理,Mybatis 運(yùn)行時(shí)會(huì)使用 JDK 動(dòng)態(tài)代理為 Mapper 接口生成代理對(duì)象proxy,代理對(duì)象會(huì)攔截接口方法,轉(zhuǎn)而執(zhí)行 MapperStatement 所代表的 sql,然后將 sql 執(zhí)行結(jié)果返回。

這個(gè)說的比較長(zhǎng)。然后我長(zhǎng)話短說總結(jié)下:首先mybatis每個(gè)方法對(duì)應(yīng)的是一個(gè)sql。key是接口全限名+方法名,value是對(duì)應(yīng)的sql。然后如果重載的話會(huì)破壞這種一對(duì)一的對(duì)應(yīng)關(guān)系。所以不能重載!

10. Mybatis是如何進(jìn)行分頁的?分頁插件的原理是什么?
  • Mybatis 使用 RowBounds 對(duì)象進(jìn)行分頁,它是針對(duì) ResultSet 結(jié)果集執(zhí)行的內(nèi) 存分頁,而非物理分頁。(簡(jiǎn)單來說一次都查出來,然后再把查出來的結(jié)果分頁)
  • 也可以在 sql 內(nèi)直接書寫帶有物理分頁的參數(shù)來完成物理分頁功能,也可以使用分頁插件來完成物理分頁。

分頁插件的基本原理是使用 Mybatis 提供的插件接口,實(shí)現(xiàn)自定義插件,在插件 的攔截方法內(nèi)攔截待執(zhí)行的 sql,然后重寫sql,根據(jù) dialect 方言,添加對(duì)應(yīng)的物理分頁語句和物理分頁參數(shù)。
簡(jiǎn)單說分頁分兩種:
1.查詢出來的就是想要的頁數(shù)。(物理分頁)
2.查詢?nèi)吭俅a處理給想要的頁數(shù)(內(nèi)存分頁)
分頁插件的基本實(shí)現(xiàn)都是把sql攔截添加上物理分頁后再執(zhí)行。

再次不要臉的安利mybatis plus中已經(jīng)封裝好了一個(gè)分頁的實(shí)現(xiàn)。代碼如下(10是默認(rèn)每頁條數(shù),page是頁數(shù)):


image.png

其實(shí)還有好多現(xiàn)成的分頁插件,我就不多說了,百度一大堆。甚至知道原理了自己也可以做個(gè)分頁的封裝類。

11. Mybatis是如何將sql執(zhí)行結(jié)果封裝為目標(biāo)對(duì)象并返回的?都有哪些映射形式?
  • 第一種是使用<resultMap>標(biāo)簽,逐一定義數(shù)據(jù)庫(kù)列名和對(duì)象屬性名之間的映 射關(guān)系。
  • 第二種是使用 sql 列的別名功能,將列的別名書寫為對(duì)象屬性名。

這個(gè)其實(shí)可以參考上面說的實(shí)體類的屬性和列的名字不一樣時(shí)怎么處理。不過我之前就說過現(xiàn)在很少用xml做映射了,而是直接entity和表映射。如果名字是一樣的(下劃線轉(zhuǎn)駝峰算是默認(rèn)的,可以算作是一樣的)直接映射。如果屬性名和列名不一樣是有注解可以表示當(dāng)前屬性對(duì)應(yīng)哪個(gè)列名的。
有了列名與屬性名的映射關(guān)系后,Mybatis通過反射創(chuàng)建對(duì)象,同時(shí)使用反射給對(duì)象的屬性逐一賦值并返回,那些找不到映射關(guān)系的屬性,是無法完成賦值的。

12. 如何執(zhí)行批量插入?

其實(shí)這個(gè)批量插入和批量刪除,一次一調(diào)用肯定是性能不太好,所以批量操作就顯得很重要了。簡(jiǎn)單說下。有兩種實(shí)現(xiàn)方式:

  • 第一種:xml標(biāo)簽實(shí)現(xiàn)。我忘了上面有沒有介紹xml的9種動(dòng)態(tài)標(biāo)簽了。。沒介紹下面也會(huì)介紹到的。這里簡(jiǎn)單說一下。可以用foreach標(biāo)簽實(shí)現(xiàn)。如下代碼:
    在dao層配置方法:
void batchInsert(@Param("orderItemList") List<User> user);

然后在xml中寫sql:

<!--批量插入-->
  <insert id="batchInsert" parameterType="list">
    insert into  tb_order_item(id,user_name,password,nick_name,create_time,update_time)
    values
    <foreach collection="orderItemList" index="index" item="item" separator=",">
      (
        #{item.id},#{item.userName},#{item.password},#{item.nickName}},now(),now()
      )
    </foreach>
  </insert>

如上代碼,這樣就實(shí)現(xiàn)了一個(gè)批量插入的功能(批量刪除差不多也是同理,只不過不用是集合而是數(shù)組就行了)

  • 第二種就是使用ExecutorType.BATCH方式執(zhí)行批量操作。
    Mybatis內(nèi)置的ExecutorType有3種,默認(rèn)的是simple,該模式下它為每個(gè)語句的執(zhí)行創(chuàng)建一個(gè)新的預(yù)處理語句,單條提交sql;而batch模式重復(fù)使用已經(jīng)預(yù)處理的語句,并且批量執(zhí)行所有更新語句,顯然batch性能將更優(yōu);
    但batch模式也有自己的問題,比如在Insert操作時(shí),在事務(wù)沒有提交之前,是沒有辦法獲取到自增的id,這在某型情形下是不符合業(yè)務(wù)要求的。
    下面是代碼示例:
    首先是創(chuàng)建一個(gè)簡(jiǎn)單的單條插入的方法(我就不創(chuàng)建了),然后代碼中用batch的方法插入:
        SqlSessionFactory factory = MyBatisTools.getInstance().getSessionFactory();
        SqlSession sqlSession = factory.openSession(ExecutorType.BATCH, false);
        UserDao userDao = sqlSession.getMapper(UserDao.class);
        for(User user : users) {
            userDao.save(user);
            }
        }
        sqlSession.flushStatements();
        sqlSession.commit();//真正插入操作。
        sqlSession.close();

這段代碼實(shí)際寫的時(shí)候可能是有異常的,反正我是整個(gè)扔出去了,正常該try-catch還是要catch下的。記得sqlSession最后要關(guān)閉。

13. 如何獲取自動(dòng)生成的(主)鍵值?

insert 方法總是返回一個(gè) int 值 ,這個(gè)值代表的是插入的行數(shù)。
如果采用自增長(zhǎng)策略,自動(dòng)生成的鍵值在 insert 方法執(zhí)行完后可以被設(shè)置到傳入 的參數(shù)對(duì)象中。如果是mybatis的話,在xml中配置下就行。
insert 標(biāo)簽中 中加入 usegeneratedkeys=”true” keyproperty=”id”。如下:

<insert id=”insertname” usegeneratedkeys=”true” keyproperty=”id”>
    insert into names (name) values (#{name})
</insert>

然后在我們調(diào)用的時(shí)候如insert完,之前的哪個(gè)實(shí)體對(duì)象的id會(huì)自動(dòng)被傳入,直接獲取就行了。

name name = new name();
name.setname(“fred”);
int rows = mapper.insertname(name);
// 完成后,id 已經(jīng)被設(shè)置到對(duì)象中
system.out.println(“generated key value = ” + name.getid());

繼續(xù)和實(shí)際聯(lián)系,一直我都說的mybatis plus其實(shí)這塊做的更好。如果在entity的主鍵上設(shè)置了id自增,那么插入后會(huì)自動(dòng)傳id到實(shí)體,不用做額外的配置操作。

14. 在mapper中如何傳遞多個(gè)參數(shù)?

其實(shí)這個(gè)題怎么說呢,我覺得真的蠻老了。。簡(jiǎn)單來說三種方式:

  • dao層直接傳多個(gè),用0,1,2,3獲取。
  • 用注解的方式為每一個(gè)參數(shù)確定名字。到時(shí)候根據(jù)名字獲取。
  • 封裝成map。然后從map中取對(duì)應(yīng)key的值。

總體來說現(xiàn)在常用的我感覺是第二種方式的更簡(jiǎn)版。就是不非要寫注解了。但是也可以直接用#{變量名}來獲取。不過這個(gè)變量名非要一模一樣應(yīng)該都可以理解。然后我就不貼代碼了。這個(gè)題理解就得了。

Mybatis動(dòng)態(tài)sql有什么用?執(zhí)行原理?有哪些動(dòng)態(tài)sql?

Mybatis 動(dòng)態(tài) sql 可以在 Xml 映射文件內(nèi),以標(biāo)簽的形式編寫動(dòng)態(tài) sql,執(zhí)行原理 是根據(jù)表達(dá)式的值 完成邏輯判斷并動(dòng)態(tài)拼接 sql 的功能。
Mybatis 提供了 9 種動(dòng)態(tài) sql 標(biāo)簽:trim | where | set | foreach | if | choose | when | otherwise | bind。
這個(gè)怎么說呢,之前我就說了九個(gè)動(dòng)態(tài)標(biāo)簽。?,F(xiàn)在算是列出來了。都是單純的條件判斷。我感覺稍微有編程功底的都能見名知意。實(shí)在不行具體用法百度吧。

16. Xml映射文件中,除了常見的select|insert|updae|delete標(biāo)簽之外,還有哪些標(biāo)簽?

<resultMap>、<parameterMap>、<sql>、<include>、 <selectKey>,加上動(dòng)態(tài) sql 的 9 個(gè)標(biāo)簽,其中<sql>為 sql 片段標(biāo)簽,通過 <include>標(biāo)簽引入 sql 片段,<selectKey>為不支持自增的主鍵生成策略標(biāo)簽。另外之前主鍵自增獲取主鍵的那兩個(gè)。

17. Mybatis 的 Xml 映射文件中,不同的 Xml 映射文件,id 是否可以重復(fù)?

這個(gè)題其實(shí)和dao方法不能重載是一樣的。namespace不一樣可以重復(fù),否則不可以。不多說了。

18. 為什么說Mybatis是半自動(dòng)ORM映射工具?它與全自動(dòng)的區(qū)別在哪里?

Hibernate 屬于全自動(dòng) ORM 映射工具,使用 Hibernate 查詢關(guān)聯(lián)對(duì)象或者關(guān)聯(lián) 集合對(duì)象時(shí),可以根據(jù)對(duì)象關(guān)系模型直接獲取,所以它是全自動(dòng)的。而 Mybatis 在查詢關(guān)聯(lián)對(duì)象或關(guān)聯(lián)集合對(duì)象時(shí),需要手動(dòng)編寫 sql 來完成,所以,稱之為半自 動(dòng) ORM 映射工具。

19. 一對(duì)一、一對(duì)多的關(guān)聯(lián)查詢 ?

就是在xml文件中配置子集。我隨便網(wǎng)上找了個(gè)demo:

 <resultMap type="sol.erms.model.entity.process.use.ErmsUseMainExport" id="useMainResult">
        <result column="USE_NO" property="useNo" />
        <result column="ERMS_LENDER" property="ermsLender" />
        <result column="ERMS_LENDINGPURPOSE" property="ermsLendingpurpose" />
        <collection property="archList" ofType="sol.erms.model.entity.process.use.ERMSUseExport" column="id">
            <result column="ARCH_NAME" jdbcType="TIMESTAMP" property="archName" />
            <result column="ARCH_NO" jdbcType="VARCHAR" property="archNo" />
            <result column="ERMS_APPLYMODE" property="ermsApplymode" />
            <result column="START_USE_DATE" property="startUseDate" />
            <result column="ERMS_LENDHANDLER" property="ermsLendhandler" />
            <result column="ERMS_BORROW_REVIEWER" property="ermsBorrowReviewer" />
            <result column="ERMS_RETURNTIME" property="ermsReturntime" />
            <result column="ERMS_RETURNER" property="ermsReturner" />
            <result column="ERMS_RETURN_HANDLER" property="ermsReturnhandler" />
            <result column="ERMS_RETURN_REVIEWER" property="ermsReturnReviewer" />
        </collection>
    </resultMap>
 
<!--條件查詢借閱+檔案(集合)數(shù)據(jù)-->
    <select id="getMainListUseCollectionByCondition" resultMap="useMainResult">
        SELECT
            m.`USE_NO`,
            m.`ERMS_LENDER`,
            m.`ERMS_LENDINGPURPOSE`,
            m.ERMS_APPLYMODE,
            u.START_USE_DATE,
            u.ERMS_LENDHANDLER,
            u.ERMS_BORROW_REVIEWER,
            u.ERMS_RETURNTIME,
            u.ERMS_RETURNER,
            u.ERMS_RETURN_HANDLER,
            u.ERMS_RETURN_REVIEWER,
            a.ARCH_NO,
            a.ARCH_NAME
        FROM
            erms_use_main m
            LEFT JOIN erms_use u ON m.ID = u.ERMS_USE_MAIN_ID
            LEFT JOIN erms_archived_arch_ru a ON u.ERMS_ARCH_FORM_ID = a.ID
        <where>
            <if test="DEL_STATUS!=null and DEL_STATUS!=''">
                m.DEL_STATUS = #{DEL_STATUS}
            </if>
            <if test="ids!=null and ids.size()>0">
                m.ID IN
                <foreach collection="ids" item="id" open="(" close=")" separator="," >
                    #{id}
                </foreach>
            </if>
        </where>
    </select>

差不多就這樣。但是我個(gè)人的話習(xí)慣于分兩次查詢。。不過也要酌情使用吧。

20. MyBatis實(shí)現(xiàn)一對(duì)一,一對(duì)多有幾種方式?具體怎么操作的?

有聯(lián)合查詢和嵌套查詢,聯(lián)合查詢是幾個(gè)表聯(lián)合查詢,只查詢一次, 通過在 resultMap 里面配置 association 節(jié)點(diǎn)配置一對(duì)一的類就可以完成;
嵌套查詢是先查一個(gè)表,根據(jù)這個(gè)表里面的結(jié)果的 外鍵 id,去再另外一個(gè)表里面 查詢數(shù)據(jù),也是通過 association 配置,但另外一個(gè)表的查詢通過 select 屬性配置。
簡(jiǎn)單的貼兩個(gè)例子:
一對(duì)一:

<mapper namespace="com.mapper.TeacherMapper">
    <resultMap type="Teacher" id="teacherPositionResultMap">
        <id property="id" column="id"/>
        <result property="no" column="t_no"/>
        <result property="name" column="t_name"/>
        <!-- association:配置的一對(duì)一屬性 -->
        <!-- property:名字
             javaType:類型
         -->
        <association property="pos" javaType="Position">
            <id property="id" column="id"/>
            <result property="name" column="t_pos_name"/>
        </association>
    </resultMap>
    
    <!-- 一對(duì)一關(guān)聯(lián)查詢,查詢老師及其對(duì)應(yīng)的職位 -->
    <!-- 注意:id不能相同,當(dāng)多個(gè)值傳入,比如包裝類的時(shí)候,我們才能夠用SQL片段的形式來做if判斷,單個(gè)值是不行的 -->
    <select id="queryTeacherPositionResultMapById" resultMap="teacherPositionResultMap" parameterType="Integer">
        SELECT *
        FROM tb_teacher t
        LEFT JOIN tb_position p
        ON t.position_id = p.id
        where t.id = #{id}
    </select>
 
    <select id="queryTeacherPositionResultMap" resultMap="teacherPositionResultMap">
        SELECT *
        FROM tb_teacher t
        LEFT JOIN tb_position p
        ON t.`position_id` = p.id
    </select> 
</mapper>

如上圖,因?yàn)槭且粚?duì)一的關(guān)系,所以結(jié)果集的封裝用association來表示。下面的查詢語句正常,結(jié)果集用我們之前定義的格式接收。
一對(duì)多:

<mapper namespace="com.mapper.PositionMapper"> 
    <resultMap type="Position" id="positionTeacherResultMap">
        <id property="id" column="id"/>
        <result property="name" column="t_pos_name"/> <!-- t_name -->
        <!-- 
        property同association中的一樣是屬性名稱(javaBean中的);
        javaType也同association中的是類型,
        最后多了一個(gè)OfType,因?yàn)橐粚?duì)多,不像一對(duì)一是單個(gè)的!我們這里是List集合,list和List都可以。
        一對(duì)多其中是放的一個(gè)集合所以這個(gè)是集合的泛型的類型,這里我們的list中放的是Teacher:
        所以這里是Teacher。
         -->
        <collection property="teacherList" javaType="list" ofType="Teacher" >
            <!-- 
                一對(duì)多出現(xiàn)的問題:
                    當(dāng)數(shù)據(jù)庫(kù)表中,主表的主鍵id和明細(xì)表的 ...
                    當(dāng)表中的字段名相同時(shí)怎么辦?多表聯(lián)查?
                    
                    注意:Mybatis中做多表聯(lián)查的時(shí)候,不管是
                    一對(duì)一、一對(duì)多、一對(duì)多對(duì)多:多對(duì)多:
                    都不能有字段重名的情況:不管是主鍵還是普通字段。
                    一旦字段重名的話,就會(huì)造成數(shù)據(jù)少自動(dòng)賦值,或者覆蓋,甚至重復(fù)賦值!
                    規(guī)避和解決此類問題的方法:
                        1.盡量不要表間重名,mybatis里處理起來很麻煩!id和普通字段都是。
                        但是在表多的時(shí)候,很難不會(huì)出現(xiàn)字段重名的情況。主鍵id最容易重名!
                        那么就要用以下的辦法了!
                        
                        2.在mybatis中寫原生SQL進(jìn)行查詢的時(shí)候,查的字段盡可能的少,這
                        也影響速率,強(qiáng)烈禁止使用*,用多少查多少!這樣也能及時(shí)發(fā)現(xiàn)字段重
                        名的情況!
                        
                        3.最后如果真的需要查出重名的字段,并且修改數(shù)據(jù)庫(kù)字段名造成的更改
                        過大,這里推薦的方式是給字段取別名,在寫resultMap映射的時(shí)候,其
                        中的column屬性就填寫SQL語句中查出字段取的別名,這樣就能解決重復(fù)
                        問題了!
             -->
            <id property="id" column="t_id"/>
            <result property="no" column="t_no"/>
            <result property="name" column="t_name"/>   
        </collection>
    </resultMap>
    
    <select id="queryPositionTeacherResultMapById" resultMap="positionTeacherResultMap" 
        parameterType="Integer">
        SELECT 
        p.*, 
        t.id t_id,
        t.t_name,
        t.t_no
        FROM tb_position p
        LEFT JOIN tb_teacher t
        ON p.id = t.position_id
        WHERE p.id = #{id}
    </select>
    
    <select id="queryPositionTeacherResultMap" resultMap="positionTeacherResultMap" >
        SELECT 
        p.*, 
        t.id t_id,
        t.t_name,
        t.t_no
        FROM tb_position p
        LEFT JOIN tb_teacher t
        ON p.id = t.position_id
        
    </select>
</mapper>

上面是我百度中找到的注釋寫的賊全的一個(gè)記錄。在此表白作者大大。順便附上傳送門Mybatis一對(duì)一、一對(duì)多、多對(duì)多查詢。+MYSQL
如果看了我這簡(jiǎn)單的兩個(gè)例子不懂可以點(diǎn)過去看看原文。
然后這個(gè)關(guān)聯(lián)查詢就到這里。重點(diǎn)代碼片段中說了,盡量不要字段名重復(fù)。

21. Mybatis 是否支持延遲加載?如果支持,它的實(shí)現(xiàn)原理是什么?

Mybatis 僅支持 association 關(guān)聯(lián)對(duì)象和 collection 關(guān)聯(lián)集合對(duì)象的延遲加載,association 指的就是一對(duì)一,collection 指的就是一對(duì)多查詢。在 Mybatis配置文件中,可以配置是否啟用延遲加載 lazyLoadingEnabled=true|false。
它的原理是,使用 CGLIB 創(chuàng)建目標(biāo)對(duì)象的代理對(duì)象,當(dāng)調(diào)用目標(biāo)方法時(shí),進(jìn)入攔截器方法,比如調(diào)用 a.getB().getName(),攔截器 invoke()方法發(fā)現(xiàn)a.getB()是null 值,那么就會(huì)單獨(dú)發(fā)送事先保存好的查詢關(guān)聯(lián) B 對(duì)象的 sql,把 B 查詢上來,然后調(diào)用 a.setB(b),于是 a 的對(duì)象 b 屬性就有值了,接著完成 a.getB().getName()方法的調(diào)用。這就是延遲加載的基本原理。
當(dāng)然了,不光是 Mybatis,幾乎所有的包括 Hibernate,支持延遲加載的原理都
是一樣的。
這個(gè)我覺得敘述的挺清楚的。就是在調(diào)用的時(shí)候判斷是不是null,是null則把子查詢的結(jié)果set進(jìn)來。之前我有個(gè)疑問就是如果獲取到對(duì)象不做任何處理直接返回給前端這個(gè)關(guān)聯(lián)查詢會(huì)不會(huì)是null、問了大佬之后發(fā)現(xiàn)發(fā)送給前端的時(shí)候json序列化會(huì)自動(dòng)調(diào)用get方法。。所以說其實(shí)方法也是調(diào)用了個(gè)。也就是說返回的對(duì)象是有關(guān)聯(lián)查詢的東西的。

22. Mybatis 的一級(jí)、二級(jí)緩存?

1)一級(jí)緩存: 基于 PerpetualCache 的 HashMap 本地緩存,其存儲(chǔ)作用域?yàn)镾ession,當(dāng) Session flush 或 close 之后,該 Session 中的所有 Cache 就將清空,默認(rèn)打開一級(jí)緩存。
2)二級(jí)緩存與一級(jí)緩存其機(jī)制相同,默認(rèn)也是采用 PerpetualCache,HashMap存儲(chǔ),不同在于其存儲(chǔ)作用域?yàn)?Mapper(Namespace),并且可自定義存儲(chǔ)源,如 Ehcache。默認(rèn)不打開二級(jí)緩存,要開啟二級(jí)緩存,使用二級(jí)緩存屬性類需要實(shí)現(xiàn) Serializable 序列化接口(可用來保存對(duì)象的狀態(tài)),可在它的映射文件中配置<cache/> ;
3)對(duì)于緩存數(shù)據(jù)更新機(jī)制,當(dāng)某一個(gè)作用域(一級(jí)緩存 Session/二級(jí)緩存Namespaces)的進(jìn)行了 C/U/D 操作后,默認(rèn)該作用域下所有 select 中的緩存將被 clear。
關(guān)于這個(gè)題其實(shí)是真的涉及到了我的知識(shí)盲區(qū),真的是用的賊少。我覺得正常邏輯都用不到這個(gè)玩意。
簡(jiǎn)單的說一下:在一個(gè)方法中,如果進(jìn)行了查詢操作。在沒有ud的情況下,再次進(jìn)行這個(gè)操作就會(huì)直接從緩存拿數(shù)據(jù)。這個(gè)就是一級(jí)緩存。不過如果中間進(jìn)行了ud操作,則會(huì)清空緩存,第二次緩存中沒數(shù)據(jù)還會(huì)再去數(shù)據(jù)庫(kù)查詢。
二級(jí)緩存是mapper級(jí)別的緩存,多個(gè)SqlSession去操作同一個(gè)Mapper的sql語句,多個(gè)SqlSession去操作數(shù)據(jù)庫(kù)得到數(shù)據(jù)會(huì)存在二級(jí)緩存區(qū)域,多個(gè)SqlSession可以共用二級(jí)緩存,二級(jí)緩存是多個(gè)SqlSession共享的。
UserMapper有一個(gè)二級(jí)緩存區(qū)域(按namespace分,如果namespace相同則使用同一個(gè)相同的二級(jí)緩存區(qū)),其它mapper也有自己的二級(jí)緩存區(qū)域(按namespace分)。
也是就是說擁有相同的namespace的UserMapper共享一個(gè)二級(jí)緩存。
同理一個(gè)說的很清楚的帖子傳送門:mybatis中一級(jí)緩存和二級(jí)緩存

23. 什么是 MyBatis 的接口綁定?有哪些實(shí)現(xiàn)方式?

接口綁定,就是在 MyBatis 中任意定義接口,然后把接口里面的方法和 SQL 語句綁定, 我們直接調(diào)用接口方法就可以,這樣比起原來了 SqlSession 提供的方法我們可以有更加靈活的選擇和設(shè)置。
接口綁定有兩種實(shí)現(xiàn)方式,一種是通過注解綁定,就是在接口的方法上面加上@Select、@Update 等注解,里面包含 Sql 語句來綁定;另外一種就是通過 xml里面寫 SQL 來綁定, 在這種情況下,要指定 xml 映射文件里面的 namespace 必須為接口的全路徑名。當(dāng) Sql 語句比較簡(jiǎn)單時(shí)候,用注解綁定, 當(dāng) SQL 語句比較復(fù)雜時(shí)候,用 xml 綁定,一般用 xml 綁定的比較多。
這個(gè)怎么說呢,是很基礎(chǔ)的東西。所謂的接口綁定就是怎么把接口全限定名+方法名 和 sql語句對(duì)應(yīng)起來。xml和接口上注釋兩種方法。
然后xml 的好處就是和代碼分離。我記得上文我就說過用mybatis 的話使用注釋寫sql會(huì)挨罵。。反正用法就這兩個(gè)。沒啥好說的,這個(gè)題目越往下其實(shí)都有點(diǎn)重復(fù)了。

24. 使用 MyBatis 的 mapper 接口調(diào)用時(shí)有哪些要求?
  • Mapper 接口方法名和 mapper.xml 中定義的每個(gè) sql 的 id 相同;
  • Mapper 接口方法的輸入?yún)?shù)類型和 mapper.xml 中定義的每個(gè) sql 的
    parameterType 的類型相同;
  • Mapper 接口方法的輸出參數(shù)類型和 mapper.xml 中定義的每個(gè) sql 的
    resultType 的類型相同;
  • Mapper.xml 文件中的 namespace 即是 mapper 接口的類路徑。
25. Mapper 編寫有哪幾種方式?

這個(gè)題其實(shí)我最近幾乎沒啥應(yīng)用了,所以簡(jiǎn)單的概述下得了。畢竟我現(xiàn)在項(xiàng)目中都是mybatisplus然后直接繼承BaseMapper了然后用@Mapper注解實(shí)現(xiàn)了。有點(diǎn)類似于下面的第三種方法。
三種方式:

  • 接口實(shí)現(xiàn)類繼承 SqlSessionDaoSupport:使用此種方法需要編寫mapper 接口,mapper 接口實(shí)現(xiàn)類、mapper.xml 文件。
  • 使用org.mybatis.spring.mapper.MapperFactoryBean 此方法需要在SqlMapConfig.xml中配置mapper.xml的位置,還需定義mapper接口。
  • 使用mapper掃描器 需要編寫mapper.xml文件,需要mapper接口,配置mapper掃描器,使用掃描器從spring容器中獲取mapper的實(shí)現(xiàn)對(duì)象。
26. 簡(jiǎn)述 Mybatis 的插件運(yùn)行原理,以及如何編寫一個(gè)插件。

答:Mybatis 僅可以編寫針對(duì) ParameterHandler、ResultSetHandler、StatementHandler、Executor 這 4 種接口的插件,Mybatis 使用 JDK 的動(dòng)態(tài)代理,為需要攔截的接口生成代理對(duì)象以實(shí)現(xiàn)接口方法攔截功能,每當(dāng)執(zhí)行這 4 種接口對(duì)象的方法時(shí),就會(huì)進(jìn)入攔截方法,具體就是 InvocationHandler 的 invoke()方法,當(dāng)然,只會(huì)攔截那些你指定需要攔截的方法。
編寫插件:實(shí)現(xiàn) Mybatis 的 Interceptor 接口并復(fù)寫 intercept()方法,然后在給
插件編寫注解,指定要攔截哪一個(gè)接口的哪些方法即可,記住,別忘了在配置文件中配置你編寫的插件。
其實(shí)這個(gè)題可以想象上面說的延遲加載的題目。就是A中有B。B是什么時(shí)候被插入到A的一個(gè)屬性中的?就是在方法調(diào)用的時(shí)候攔截器發(fā)現(xiàn)是null則會(huì)set進(jìn)去。
這個(gè)我只能說看了一些理論的技術(shù)貼,但是切切實(shí)實(shí)的沒有實(shí)踐過。尤其是現(xiàn)在習(xí)慣了用mybatis plus的我來說,這些底層的實(shí)現(xiàn)真的有點(diǎn)遙遠(yuǎn)。
但是學(xué)到既得到。這二十幾道題我是分了好多天才算是整理完的。一點(diǎn)一點(diǎn)細(xì)品,竟然也覺得對(duì)mybatis甚至對(duì)orm框架對(duì)mybatis和jpa的區(qū)別等認(rèn)識(shí)都清晰了不少。有時(shí)候一個(gè)清楚的認(rèn)知可能對(duì)暫時(shí)的使用沒有什么太大的影響,但是我覺得對(duì)于眼界的開拓和邏輯思維的理解都是有好處的。
剩下的面試題還很多,容我一點(diǎn)點(diǎn)整理發(fā)出來。

這篇筆記就記到這里,如果稍微幫到你了記得點(diǎn)個(gè)喜歡點(diǎn)個(gè)關(guān)注。也祝大家工作順順利利!另外這本書的原文件在群里有,歡迎各位萌新大佬踴躍加群!群號(hào)130031711

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

友情鏈接更多精彩內(nèi)容