天天看點

關于Mybatis:Mybatis面試題總結

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

答:Dao接口,就是人們常說的Mapper接口,接口的全限名,就是映射檔案中的namespace的值,接口的方法名,就是映射檔案中MappedStatement的id值,接口方法内的參數,就是傳遞給sql的參數。Mapper接口是沒有實作類的,當調用接口方法時,接口全限名+方法名拼接字元串作為key值,可唯一定位一個MappedStatement,舉例:com.mybatis3.mappers.StudentDao.findStudentById,可以唯一找到namespace為com.mybatis3.mappers.StudentDao下面id = findStudentById的MappedStatement。在Mybatis中,每一個<select>、<insert>、<update>、<delete>标簽,都會被解析為一個MappedStatement對象。

Dao接口裡的方法,是不能重載的,因為是全限名+方法名的儲存和尋找政策。

Dao接口的工作原理是JDK動态代理,Mybatis運作時會使用JDK動态代理為Dao接口生成代理proxy對象,代理對象proxy會攔截接口方法,轉而執行MappedStatement所代表的sql,然後将sql執行結果傳回。

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

答:Mybatis使用RowBounds對象進行分頁,它是針對ResultSet結果集執行的記憶體分頁,而非實體分頁,可以在sql内直接書寫帶有實體分頁的參數來完成實體分頁功能,也可以使用分頁插件來完成實體分頁。

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

舉例:select * from student,攔截sql後重寫為:select t.* from (select * from student)t limit 0,10

3、簡述Mybatis的插件運作原理,以及如何編寫一個插件。

答:Mybatis僅可以編寫針對ParameterHandler、ResultSetHandler、StatementHandler、Executor這4種接口的插件,Mybatis使用JDK的動态代理,為需要攔截的接口生成代理對象以實作接口方法攔截功能,每當執行這4種接口對象的方法時,就會進入攔截方法,具體就是InvocationHandler的invoke()方法,當然,隻會攔截那些你指定需要攔截的方法。

實作Mybatis的Interceptor接口并複寫intercept()方法,然後在給插件編寫注解,指定要攔截哪一個接口的哪些方法即可,記住,别忘了在配置檔案中配置你編寫的插件。

4、Mybatis執行批量插入,能傳回資料庫主鍵清單嗎?

答:能,JDBC都能,Mybatis當然也能。

關于Mybatis:Mybatis面試題總結

5、Mybatis動态sql是做什麼的?都有哪些動态sql?能簡述一下動态sql的執行原理不?

答:Mybatis動态sql可以讓我們在Xml映射檔案内,以标簽的形式編寫動态sql,完成邏輯判斷和動态拼接sql的功能,Mybatis提供了9種動态sql标簽trim|where|set|foreach|if|choose|when|otherwise|bind。

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

6、Mybatis是如何将sql執行結果封裝為目标對象并傳回的?都有哪些映射形式?

答:第一種是使用<resultMap>标簽,逐一定義列名和對象屬性名之間的映射關系。第二種是使用sql列的别名功能,将列别名書寫為對象屬性名,比如T_NAME AS NAME,對象屬性名一般是name,小寫,但是列名不區分大小寫,Mybatis會忽略列名大小寫,智能找到與之對應對象屬性名,你甚至可以寫成T_NAME AS NaMe,Mybatis一樣可以正常工作。

有了列名與屬性名的映射關系後,Mybatis通過反射建立對象,同時使用反射給對象的屬性逐一指派并傳回,那些找不到映射關系的屬性,是無法完成指派的。

7、Mybatis能執行一對一、一對多的關聯查詢嗎?都有哪些實作方式,以及它們之間的差別。

答:能,Mybatis不僅可以執行一對一、一對多的關聯查詢,還可以執行多對一,多對多的關聯查詢,多對一查詢,其實就是一對一查詢,隻需要把selectOne()修改為selectList()即可;多對多查詢,其實就是一對多查詢,隻需要把selectOne()修改為selectList()即可。

關聯對象查詢,有兩種實作方式,一種是單獨發送一個sql去查詢關聯對象,賦給主對象,然後傳回主對象。另一種是使用嵌套查詢,嵌套查詢的含義為使用join查詢,一部分列是A對象的屬性值,另外一部分列是關聯對象B的屬性值,好處是隻發一個sql查詢,就可以把主對象和其關聯對象查出來。

那麼問題來了,join查詢出來100條記錄,如何确定主對象是5個,而不是100個?其去重複的原理是<resultMap>标簽内的<id>子标簽,指定了唯一确定一條記錄的id列,Mybatis根據<id>列值來完成100條記錄的去重複功能,<id>可以有多個,代表了聯合主鍵的語意。

同樣主對象的關聯對象,也是根據這個原理去重複的,盡管一般情況下,隻有主對象會有重複記錄,關聯對象一般不會重複。

舉例:下面join查詢出來6條記錄,一、二列是Teacher對象列,第三列為Student對象列,Mybatis去重複處理後,結果為1個老師6個學生,而不是6個老師6個學生。

t_id    t_name           s_id
           

| 1 | teacher | 38 |

| 1 | teacher | 39 |

| 1 | teacher | 40 |

| 1 | teacher | 41 |

| 1 | teacher | 42 |

| 1 | teacher | 43 |

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()方法的調用。這就是延遲加載的基本原理。

當然了,不光是Mybatis,幾乎所有的包括Hibernate,支援延遲加載的原理都是一樣的。

9、Mybatis的Xml映射檔案中,不同的Xml映射檔案,id是否可以重複?

答:不同的Xml映射檔案,如果配置了namespace,那麼id可以重複;如果沒有配置namespace,那麼id不能重複;畢竟namespace不是必須的,隻是最佳實踐而已。

原因就是namespace+id是作為Map<String, MappedStatement>的key使用的,如果沒有namespace,就剩下id,那麼,id重複會導緻資料互相覆寫。有了namespace,自然id就可以重複,namespace不同,namespace+id自然也就不同。

10、Mybatis中如何執行批處理?

答:使用BatchExecutor完成批處理。

11、Mybatis都有哪些Executor執行器?它們之間的差別是什麼?

答:Mybatis有三種基本的Executor執行器,SimpleExecutor、ReuseExecutor、BatchExecutor。

SimpleExecutor:每執行一次update或select,就開啟一個Statement對象,用完立刻關閉Statement對象。

ReuseExecutor:執行update或select,以sql作為key查找Statement對象,存在就使用,不存在就建立,用完後,不關閉Statement對象,而是放置于Map<String, Statement>内,供下一次使用。簡言之,就是重複使用Statement對象。

BatchExecutor:執行update(沒有select,JDBC批處理不支援select),将所有sql都添加到批進行中(addBatch()),等待統一執行(executeBatch()),它緩存了多個Statement對象,每個Statement對象都是addBatch()完畢後,等待逐一執行executeBatch()批處理。與JDBC批處理相同。

作用範圍:Executor的這些特點,都嚴格限制在SqlSession生命周期範圍内。

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

答:在Mybatis配置檔案中,可以指定預設的ExecutorType執行器類型,也可以手動給DefaultSqlSessionFactory的建立SqlSession的方法傳遞ExecutorType類型參數。

關于Mybatis:Mybatis面試題總結

13、Mybatis是否可以映射Enum枚舉類?

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

14、Mybatis映射檔案中,如果A标簽通過include引用了B标簽的内容,請問,B标簽能否定義在A标簽的後面,還是說必須定義在A标簽的前面?

答:雖然Mybatis解析Xml映射檔案是按照順序解析的,但是,被引用的B标簽依然可以定義在任何地方,Mybatis都可以正确識别。

原理是,Mybatis解析A标簽,發現A标簽引用了B标簽,但是B标簽尚未解析到,尚不存在,此時,Mybatis會将A标簽标記為未解析狀态,然後繼續解析餘下的标簽,包含B标簽,待所有标簽解析完畢,Mybatis會重新解析那些被标記為未解析的标簽,此時再解析A标簽時,B标簽已經存在,A标簽也就可以正常解析完成了。

15、簡述Mybatis的Xml映射檔案和Mybatis内部資料結構之間的映射關系?

答:Mybatis将所有Xml配置資訊都封裝到All-In-One重量級對象Configuration内部。在Xml映射檔案中,<parameterMap>标簽會被解析為ParameterMap對象,其每個子元素會被解析為ParameterMapping對象。<resultMap>标簽會被解析為ResultMap對象,其每個子元素會被解析為ResultMapping對象。每一個<select>、<insert>、<update>、<delete>标簽均會被解析為MappedStatement對象,标簽内的sql會被解析為BoundSql對象。

16、為什麼說Mybatis是半自動ORM映射工具?它與全自動的差別在哪裡?

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

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

第1種: 通過在查詢的sql語句中定義字段名的别名,讓字段名的别名和實體類的屬性名一緻

<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>來映射字段名和實體類屬性名的一一對應的關系

<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為實體類屬性名,column為資料表中的屬性–> 
        <result property = “orderno” column =”order_no”/> 
        <result property=”price” column=”order_price” /> 
    </reslutMap>
           
  1. 在mapper中如何傳遞多個參數?

第一種:使用占位符的思想

•在映射檔案中使用#{0},#{1}代表傳遞進來的第幾個參數

•使用@param注解:來命名參數

•#{0},#{1}方式

//對應的xml,#{0}代表接收的是dao層中的第一個參數,#{1}代表dao層中第二參數,更多參數一緻往後加即可。

<select id="selectUser"resultMap="BaseResultMap">  
    select *  fromuser_user_t   whereuser_name = #{0} anduser_area=#{1}  
</select>  
•@param注解方式

public interface usermapper { 
         user selectuser(@param(“username”) string username, 
         @param(“hashedpassword”) string hashedpassword); 
        }
 <select id=”selectuser” resulttype=”user”> 
         select id, username, hashedpassword 
         from some_table 
         where username = #{username} 
         and hashedpassword = #{hashedpassword} 
    </select>
           

第二種:使用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();
        }

    <!--分頁查詢-->
    <select id="pagination" parameterType="map" resultMap="studentMap">

        /*根據key自動找到對應Map集合的value*/
        select * from students limit #{start},#{end};

    </select>
           
  1. Mybatis動态sql是做什麼的?都有哪些動态sql?能簡述一下動态sql的執行原理不?

    •Mybatis動态sql可以讓我們在Xml映射檔案内,以标簽的形式編寫動态sql,完成邏輯判斷和動态拼接sql的功能。

    •Mybatis提供了9種動态sql标簽:trim|where|set|foreach|if|choose|when|otherwise|bind。

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

  2. Mybatis的Xml映射檔案中,不同的Xml映射檔案,id是否可以重複?

如果配置了namespace那麼當然是可以重複的,因為我們的Statement實際上就是namespace+id

如果沒有配置namespace的話,那麼相同的id就會導緻覆寫了。

  1. 接口綁定有幾種實作方式,分别是怎麼實作的?

接口綁定有兩種實作方式:

•一種是通過注解綁定,就是在接口的方法上面加上@[email protected]等注解裡面包含Sql語句來綁定

•另外一種就是通過xml裡面寫SQL來綁定,在這種情況下,要指定xml映射檔案裡面的namespace必須為接口的全路徑名.

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

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

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

舉例:

select * from student,攔截sql後重寫為:select t.* from (select * from student)t limit 0,10
           
  1. 簡述Mybatis的插件運作原理,以及如何編寫一個插件

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

實作Mybatis的Interceptor接口并複寫intercept()方法,然後在給插件編寫注解,指定要攔截哪一個接口的哪些方法即可,記住,别忘了在配置檔案中配置你編寫的插件。