Mybatis相關面試題總結

一、簡介

本文將總結一些關於持久化層框架Mybatis的面試題。

二、面試題

【1】什麼是Mybatis?

MyBatis是一個可以自定義SQL、動態SQL、存儲過程和高級映射的持久層框架。

【2】請談談Mybatis的緩存?

MyBatis的緩存分爲一級緩存和二級緩存,一級緩存放在session裏面,默認開啓一級緩存;二級緩存存放在它的命名空間中,默認是不開啓的,需要我們手動開啓二級緩存,使用二級緩存的屬性類需要實現Serializable序列化接口(可用來保存對象的狀態)。

【3】Mybatis是如何進行分頁的?分頁插件的原理是什麼?

Mybatis使用RowBounds對象進行分頁,也可以直接編寫sql實現分頁,也可以使用Mybatis的分頁插件PageHelper.
分頁插件的原理:實現Mybatis提供的接口,實現自定義插件,在插件的攔截方法內攔截待執行的SQL,然後加上分頁的邏輯去重寫SQL,實現分頁:

  •     select * from student 攔截SQL後重寫爲:select t.* from (select * from student)t limit 0҅10

【4】簡述Mybatis的插件運行原理,以及如何編寫一個插件?

Mybatis僅可以編寫針對ParameterHandler、ResultSetHandler、StatementHandler、Executor 這4中接口的插件,Mybatis通過動態代理,爲需要攔截的接口生成代理對象以實現接口方法攔截功能,每當執行這4種接口對象的方法時,就會進入攔截方法,具體就是InvocationHandler的 invoke()方法,當然,只會攔截那些你指定需要攔截的方法。

如何編寫插件:實現Mybatis的Interceptor接口,並且重寫 intercept()方法,然後在給插件編寫註解,指定要攔截哪一個接口的哪些方法即可,別忘了在配置文件中配置自己編寫的插件類。

【5】Mybatis動態sql是做什麼的?都有哪些動態sql?能簡述一下動態sql的執行原理麼?

Mybatis動態sql可以讓我們在Mapper.xml映射文件中,以標籤的方式編寫動態sql,完成邏輯判斷和動態拼接sql的功能,如:<if test="xxxx"></if>  、<where></where>、<choose><when test="xxx"></when></choose>等。
Mybatis提供了9中動態sql的標籤:    trim|where|set|foreach|if|choose|when|otherwise|bind

  • 動態sql的執行原理:使用OGNL從sql參數對象中計算表達式的值,根據表達式的值動態拼接sql,以此來完成動態sql的功能。

【6】#{}和${}的區別是什麼?

  • #{}是預編譯處理,${}是字符串替換
  • Mybatis在處理#{}是,會將sql中的#{}替換爲?佔位符,調用PreparedStatement的預編譯方法set()來賦值
  • Mybatis在處理${}時,就是把${}替換爲變量的值
  • 使用#{}可以有效防止sql注入風險,使用${}可能會有sql注入風險

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

  • Hibernate屬於全自動ORM映射工具,使用Hibernate查詢關聯對象或者關聯集合對象時,可以根據對象關係模型直接獲取,所以它是全自動的。
  • 而 Mybatis在查詢關聯對象或者關聯集合時,需要我們自己手動編寫對應的關聯查詢sql來完成,配置<association>、<collection>標籤來完成,所以 Mybatis被稱爲半自動ORM映射工具。

【8】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()方法的調用。

【9】Mybatis與Hibernate的區別?

相同點:
    Hibernate與MyBatis都可以是通過SessionFactoryBuider由XML配置文件生成SessionFactory,然後由SessionFactory 生成Session,最後由Session來開啓執行事務和SQL語句。其中SessionFactoryBuider,SessionFactory,Session的生命週期都是差不多的。Hibernate和MyBatis都支持JDBC和JTA事務處理。

不同點:

  • Mybatis和hibernate不同,它不完全是一個ORM框架,因爲MyBatis需要程序員自己編寫Sql語句,不過mybatis可以通過XML或註解方式靈活配置要運行的sql語句,並將java對象和sql語句映射生成最終執行的sql,最後將sql執行的結果再映射生成java對象。
  • Mybatis學習門檻低,簡單易學,程序員直接編寫原生態sql,可嚴格控制sql執行性能,靈活度高,非常適合對關係數據模型要求不高的軟件開發,例如互聯網軟件、企業運營類軟件等,因爲這類軟件需求變化頻繁,一但需求變化要求成果輸出迅速。但是靈活的前提是mybatis無法做到數據庫無關性,如果需要實現支持多種數據庫的軟件則需要自定義多套sql映射文件,工作量大。
  • Hibernate對象/關係映射能力強,數據庫無關性好,對於關係模型要求高的軟件(例如需求固定的定製化軟件)如果用hibernate開發可以節省很多代碼,提高效率。但是Hibernate的缺點是學習門檻高,要精通門檻更高,而且怎麼設計O/R映射,在性能和對象模型之間如何權衡,以及怎樣用好Hibernate需要具有很強的經驗和能力才行。
  • hibernate是全自動,而mybatis是半自動。
  • hibernate數據庫移植性遠大於mybatis。
  • hibernate通過它強大的映射結構和hql語言,大大降低了對象與數據庫(Oracle、MySQL等)的耦合性,而mybatis由於需要手寫sql,因此與數據庫的耦合性直接取決於程序員寫sql的方法,如果sql不具通用性而用了很多某數據庫特性的sql語句的話,移植性也會隨之降低很多,成本很高。
  • hibernate擁有完整的日誌系統,mybatis則欠缺一些。
  • hibernate日誌系統非常健全,涉及廣泛,包括:sql記錄、關係異常、優化警告、緩存提示、髒數據警告等;而mybatis則除了基本記錄功能外,功能薄弱很多。
  • sql直接優化上,mybatis要比hibernate方便很多。
  • 由於mybatis的sql都是寫在xml裏,因此優化sql比hibernate方便很多。而hibernate的sql很多都是自動生成的,無法直接維護sql;雖有hql,但功能還是不及sql強大,見到報表等變態需求時,hql也歇菜,也就是說hql是有侷限的;hibernate雖然也支持原生sql,但開發模式上卻與orm不同,需要轉換思維,因此使用上不是非常方便。總之寫sql的靈活度上hibernate不及mybatis。
  • 緩存機制上,hibernate要比mybatis更好一些。
  • MyBatis的二級緩存配置都是在每個具體的表-對象映射中進行詳細配置,這樣針對不同的表可以自定義不同的緩存機制。並且Mybatis可以在命名空間中共享相同的緩存配置和實例,通過Cache-ref來實現。而Hibernate對查詢對象有着良好的管理機制,用戶無需關心SQL。所以在使用二級緩存時如果出現髒數據,系統會報出錯誤並提示。

【10】Mybatis的好處是什麼?

  • sql語句與代碼分離,存放於xml配置文件中
  • 動態sql支持:用邏輯標籤控制動態SQL的拼接
  • 高級映射:查詢的結果集與java對象自動映射
  • 編寫原生SQL,SQL優化比較好處理

【11】簡述Mybatis的XML映射文件和Mybatis內部數據結構之間的映射關係?

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

【12】什麼是Mybatis的接口綁定,有什麼好處?

接口綁定,就是在MyBatis中任意定義接口,然後把接口裏面的方法和SQL語句綁定,我們直接調用接口方法就可以,這樣比起原來了SqlSession提供的方法,可以有更加靈活的選擇和設置。

【13】接口綁定有幾種實現方式,分別是怎麼實現的?注意事項?

接口綁定有兩種實現方式,一種是通過註解綁定,就是在接口的方法上面加上 @Select、@Update等註解,裏面包含Sql語句來綁定;另外一種就是通過xml裏面寫SQL來綁定,在這種情況下,要指定xml映射文件裏面的namespace必須爲接口的全路徑名。當Sql語句比較簡單時候,用註解綁定,當SQL語句比較複雜時候,用xml綁定。一般情況下,用xml綁定的比較多。
注意事項:

  •     (1)Mapper接口方法名和mapper.xml中定義的每個sql的id相同;
  •     (2)Mapper接口方法的輸入參數類型和mapper.xml中定義的每個sql 的parameterType的類型相同;
  •     (3)Mapper接口方法的輸出參數類型和mapper.xml中定義的每個sql的resultType的類型相同;
  •     (4)Mapper.xml文件中的namespace即是mapper接口的類路徑。

【14】Mybatis實現一對一有幾種方式?具體怎麼操作的?

  • 第一種方法:嵌套結果方式
<!--             一對一關聯查詢: 查詢所有的班級以及關聯的教師信息     -->
 
    <!--第一種方式:嵌套結果-->
    <resultMap id="oneToOne01" type="com.wsh.springboot.springbootmybatis.entity.ClassRoom">
        <!--
            column: 對應的是查詢的別名,而不是表字段名(當然這裏沒取別名就是表字段名)
            property: 實體類中的屬性名稱
        -->
        <id column="c_id" property="cid"/>
        <result column="c_name" property="cname"/>
        <!--
            javaType: 指定返回結果集中的對象屬性類型(全限定名)
        -->
        <association property="teacher" javaType="com.wsh.springboot.springbootmybatis.entity.Teacher">
            <id column="t_id" property="tid"/>
            <result column="t_name" property="tname"/>
        </association>
    </resultMap>
 
    <select id="getAllClassRoomAndTeacherInfo01" resultMap="oneToOne01">
        SELECT *
        FROM tbl_class t1
                 LEFT JOIN tbl_teacher t2
                           ON t1.teacher_id = t2.t_id
    </select>
  • 第二種方法:分步查詢方式
 <!--第二種方式:嵌套查詢/分步查詢-->
    <resultMap id="oneToOne02" type="com.wsh.springboot.springbootmybatis.entity.ClassRoom">
        <id column="c_id" property="cid"/>
        <result column="c_name" property="cname"/>
        <!--
            property: ClassRoom類中教師類的屬性名稱
            column: 所對應的外鍵字段名稱
            select: 根據第一步查詢出的教師ID查詢教師信息
         -->
        <association property="teacher" select="getTeacherById" column="teacher_id"/>
    </resultMap>
 
    <select id="getAllClassRoomAndTeacherInfo02" resultMap="oneToOne02">
        SELECT *
        FROM tbl_class t1
    </select>
 
    <select id="getTeacherById" resultType="com.wsh.springboot.springbootmybatis.entity.Teacher">
        SELECT t3.t_id   AS tid,
               t3.t_name AS tname
        FROM tbl_teacher t3
        WHERE t3.t_id = #{tid}
    </select>
  • 第三種方式:拓展VO方式
 <!--第三種方式:使用拓展VO實現-->
    <select id="getAllClassRoomAndTeacherInfo03" resultType="com.wsh.springboot.springbootmybatis.vo.ClassRoomTzVO">
        SELECT t1.c_id   AS cid,
               t1.c_name AS cname,
               t2.t_id   AS tid,
               t2.t_name AS tname
        FROM tbl_class t1
                 LEFT JOIN tbl_teacher t2
                           ON t1.teacher_id = t2.t_id
    </select>

【15】Mybatis實現一對多的關聯查詢都有哪些實現方式?具體是怎麼操作的?

  • 第一種方法:嵌套結果方式
<!--             一對多關聯查詢: 查詢所有的班級以及班級的所有學生信息、教師信息     -->
 
    <!--第一種方式: 嵌套結果方式 -->
    <resultMap id="oneToMany01" type="com.wsh.springboot.springbootmybatis.entity.ClassRoom">
        <id column="c_id" property="cid"/>
        <result column="c_name" property="cname"/>
        <!--教師信息-->
        <association property="teacher" javaType="com.wsh.springboot.springbootmybatis.entity.Teacher">
            <id column="t_id" property="tid"/>
            <result column="t_name" property="tname"/>
        </association>
        <!--學生集合信息-->
        <!--
            property: 指定ClassRoom類聲明的學生集合的屬性名稱
            ofType: 指定返回集合中的對象類型(全限定名)
        -->
        <collection property="students" ofType="com.wsh.springboot.springbootmybatis.entity.Student">
            <id column="s_id" property="sid"/>
            <result column="s_name" property="sname"/>
        </collection>
    </resultMap>
 
    <select id="getAllStudents01" resultMap="oneToMany01">
        SELECT *
        FROM tbl_class t1
                 LEFT JOIN tbl_teacher t2
                           ON t1.teacher_id = t2.t_id
                 LEFT JOIN tbl_student t3
                           ON t1.c_id = t3.class_id
    </select>
  • 第二種方法:分步查詢方式
<!--第二種方式: 嵌套查詢(分步查詢) -->
    <select id="getTeacherById2" resultType="com.wsh.springboot.springbootmybatis.entity.Teacher">
        SELECT t3.t_id   AS tid,
               t3.t_name AS tname
        FROM tbl_teacher t3
        WHERE t3.t_id = #{tid}
    </select>
 
    <select id="getStudentsByCId" resultType="com.wsh.springboot.springbootmybatis.entity.Student">
        SELECT t.s_id as sid, t.s_name as sname
        from tbl_student t
        where t.class_id = #{cid}
    </select>
 
    <resultMap id="oneToMany02" type="com.wsh.springboot.springbootmybatis.entity.ClassRoom">
        <id column="c_id" property="cid"/>
        <result column="c_name" property="cname"/>
        <!--
            select: 指定上一步驟查詢出來的結果傳到下一步該執行的SQL
        -->
        <association property="teacher" column="teacher_id"
                     javaType="com.wsh.springboot.springbootmybatis.entity.Teacher" select="getTeacherById2"/>
        <collection property="students" column="c_id" ofType="com.wsh.springboot.springbootmybatis.entity.Student"
                    select="getStudentsByCId"/>
    </resultMap>
 
    <select id="getAllStudents02" resultMap="oneToMany02">
        SELECT t.*
        FROM tbl_class t
    </select>

【16】Mybatis裏面的動態sql是怎麼設定的?用什麼語法?

 MyBatis裏面的動態Sql一般是通過if節點來實現,通過OGNL語法來實現,但是如果要寫的完整,必須配合where,trim節點,where節點是判斷包含節點有內容就插入where,否則不插入,trim節點是用來判斷如果動態語句是以and 或or開始,那麼會自動把這個and或者or取掉。 

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

  • 第一種方式:使用<resultMap>標籤,逐一定義數據庫列名和對象屬性名之間的映射關係。
  • 第二種方式:使用sql列的別名功能,將列的別名書寫爲對象屬性名。 有了列名與屬性名的映射關係後,Mybatis通過反射創建對象,同時使用反射給對象的屬性逐一賦值並返回,那些找不到映射關係的屬性,是無法完成賦值的。

【18】XML映射文件中,處理常見的 select|insert|updae|delete標籤之外,還有哪些標籤?

  • if
  • foreach
  • resultMap
  • choose
  • when
  • otherwise
  • where
  • trim
  • sql

【19】當實體類中的屬性名和表中的字段名不一樣,如果將查詢的結果封裝到指定POJO?

  • 方法一:通過在查詢的sql語句中定義字段名的別名,讓字段名的別名和實體類的屬性名一致。
  • 方法二:通過<resultMap>來映射字段名和實體類屬性名的一一對應的關係
  • 方法三:開啓駝峯命名法實現屬性名稱和查詢列名的自動映射

【20】模糊查詢like語句該怎麼寫?

  • 在java中拼接通配符,通過#{}賦值;
  • 在Sql語句中拼接通配符 (不安全 會引起Sql注入);

【21】通常一個XML映射文件,都會寫一個Mapper接口與之對應,Mapper接口的工作原理,是否可以重載?

不能重載,因爲通過Mapper接口尋找Xml對應的sql的時候【全限定名+方法名】的保存和尋找策略。
Mapper接口工作原理:使用了JDK動態代理原理,運行時會爲Mapper接口生成proxy代理接口,代理對象會攔截接口方法,去執行對應的sql返回數據。

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

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

【23】Mybatis的XML映射文件中,不同的XML映射文件,ID是否可以重複?

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

【24】Mybatis都有哪些Executor執行器?他們之間的區別是什麼?

  • SimpleExecutor:每執行一次update或select,就開啓一個Statement對象,用完立刻關閉Statement對象;
  • ReuseExecutor:執行update或select,以sql作爲key查找Statement對象,存在就使用,不存在就創建,用完後,不關閉Statement對象,而是放置於Map;
  • BatchExecutor:完成批處理;

【25】Mybatis中如何指定使用哪一種Executor執行器?

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

【26】Mybatis是否可以映射Enum枚舉類?

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

【27】Mapper接口中如何傳遞多個參數?

  • 第一種方法:直接在方法中傳遞參數,xml文件用#{0} #{1}來獲取
  • 第二種方法:使用 @param 註解,這樣可以直接在xml文件中通過#{name}來獲取
  • 一般如果Mapper接口的參數列表大於三個的時候,我們可以考慮封裝成一個Map對象傳遞過去,然後在XML中使用#{map.xxx}, xxx表示放在map中的鍵的名稱。

【28】ResultType與ResultMap的區別? 

  • 類的名字和數據庫相同時,可以直接設置resultType參數爲Pojo類;
  • 若不同,需要設置resultMap 將結果名字和Pojo名字進行轉換;

【29】使用MyBatis的Mapper接口調用時有哪些要求?

  • 1、Mapper接口方法名和mapper.xml中定義的每個sql的id相同;
  • 2、Mapper接口方法的輸入參數類型和mapper.xml中定義的每個sql 的parameterType的類型相同;
  • 3、Mapper接口方法的輸出參數類型和mapper.xml中定義的每個sql的resultType的類型相同;
  • 4、Mapper.xml文件中的namespace即是mapper接口的類路徑;

【30】MyBatis比IBatis比較大的幾個改進是什麼?

  • 接口綁定:包括註解綁定sql和xml綁定Sql;
  • 動態sql:動態sql由原來的節點配置變成OGNL表達式;
  • 高級映射:在一對一,一對多的時候引進了association,在一對多的時候引入了collection節點,不過都是在ResultMap裏面配置;

三、總結

分享腦圖總結:

 

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