注:這道題是面試官面試我同事的。java
答:mysql
${}
是 Properties 文件中的變量佔位符,它能夠用於標籤屬性值和 sql 內部,屬於靜態文本替換,好比${driver}會被靜態替換爲com.mysql.jdbc.Driver
。#{}
是 sql 的參數佔位符,MyBatis 會將 sql 中的#{}
替換爲?號,在 sql 執行前會使用 PreparedStatement 的參數設置方法,按序給 sql 的?號佔位符設置參數值,好比 ps.setInt(0, parameterValue),#{item.name}
的取值方式爲使用反射從參數對象中獲取 item 對象的 name 屬性值,至關於 param.getItem().getName()
。注:這道題是京東面試官面試我時問的。面試
答:還有不少其餘的標籤,<resultMap>
、<parameterMap>
、<sql>
、<include>
、<selectKey>
,加上動態 sql 的 9 個標籤,trim|where|set|foreach|if|choose|when|otherwise|bind
等,其中爲 sql 片斷標籤,經過<include>
標籤引入 sql 片斷,<selectKey>
爲不支持自增的主鍵生成策略標籤。sql
注:這道題也是京東面試官面試我時問的。數據庫
答: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 執行結果返回。mybatis
注:我出的。app
答:MyBatis 使用 RowBounds 對象進行分頁,它是針對 ResultSet 結果集執行的內存分頁,而非物理分頁,能夠在 sql 內直接書寫帶有物理分頁的參數來完成物理分頁功能,也可使用分頁插件來完成物理分頁。工具
分頁插件的基本原理是使用 MyBatis 提供的插件接口,實現自定義插件,在插件的攔截方法內攔截待執行的 sql,而後重寫 sql,根據 dialect 方言,添加對應的物理分頁語句和物理分頁參數。
舉例:select _ from student
,攔截 sql 後重寫爲:select t._ from (select \* from student)t limit 0,10
注:我出的。
答:MyBatis 僅能夠編寫針對 ParameterHandler
、ResultSetHandler
、StatementHandler
、Executor
這 4 種接口的插件,MyBatis 使用 JDK 的動態代理,爲須要攔截的接口生成代理對象以實現接口方法攔截功能,每當執行這 4 種接口對象的方法時,就會進入攔截方法,具體就是 InvocationHandler
的 invoke()
方法,固然,只會攔截那些你指定須要攔截的方法。
實現 MyBatis 的 Interceptor 接口並複寫 intercept()
方法,而後在給插件編寫註解,指定要攔截哪個接口的哪些方法便可,記住,別忘了在配置文件中配置你編寫的插件。
注:我出的。
答:能,JDBC 都能,MyBatis 固然也能。
注:我出的。
答:MyBatis 動態 sql 可讓咱們在 Xml 映射文件內,以標籤的形式編寫動態 sql,完成邏輯判斷和動態拼接 sql 的功能,MyBatis 提供了 9 種動態 sql 標籤 trim|where|set|foreach|if|choose|when|otherwise|bind
。
其執行原理爲,使用 OGNL 從 sql 參數對象中計算表達式的值,根據表達式的值動態拼接 sql,以此來完成動態 sql 的功能。
注:我出的。
答:第一種是使用<resultMap>
標籤,逐必定義列名和對象屬性名之間的映射關係。第二種是使用 sql 列的別名功能,將列別名書寫爲對象屬性名,好比 T_NAME AS NAME,對象屬性名通常是 name,小寫,可是列名不區分大小寫,MyBatis 會忽略列名大小寫,智能找到與之對應對象屬性名,你甚至能夠寫成 T_NAME AS NaMe,MyBatis 同樣能夠正常工做。
有了列名與屬性名的映射關係後,MyBatis 經過反射建立對象,同時使用反射給對象的屬性逐一賦值並返回,那些找不到映射關係的屬性,是沒法完成賦值的。
注:我出的。
答:能,MyBatis 不只能夠執行一對1、一對多的關聯查詢,還能夠執行多對一,多對多的關聯查詢,多對一查詢,其實就是一對一查詢,只須要把 selectOne()
修改成 selectList()
便可;多對多查詢,其實就是一對多查詢,只須要把 selectOne()
修改成 selectList()
便可。
關聯對象查詢,有兩種實現方式,一種是單獨發送一個 sql 去查詢關聯對象,賦給主對象,而後返回主對象。另外一種是使用嵌套查詢,嵌套查詢的含義爲使用 join 查詢,一部分列是 A 對象的屬性值,另一部分列是關聯對象 B 的屬性值,好處是隻發一個 sql 查詢,就能夠把主對象和其關聯對象查出來。
那麼問題來了,join 查詢出來 100 條記錄,如何肯定主對象是 5 個,而不是 100 個?其去重複的原理是<resultMap>
標籤內的<id>
子標籤,指定了惟一肯定一條記錄的 id 列,MyBatis 根據列值來完成 100 條記錄的去重複功能,<id>
能夠有多個,表明了聯合主鍵的語意。
一樣主對象的關聯對象,也是根據這個原理去重複的,儘管通常狀況下,只有主對象會有重複記錄,關聯對象通常不會重複。
舉例:下面 join 查詢出來 6 條記錄,1、二列是 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 |
注:我出的。
答: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,支持延遲加載的原理都是同樣的。
注:我出的。
答:不一樣的 Xml 映射文件,若是配置了 namespace,那麼 id 能夠重複;若是沒有配置 namespace,那麼 id 不能重複;畢竟 namespace 不是必須的,只是最佳實踐而已。
緣由就是 namespace+id 是做爲 Map<String, MappedStatement>
的 key 使用的,若是沒有 namespace,就剩下 id,那麼,id 重複會致使數據互相覆蓋。有了 namespace,天然 id 就能夠重複,namespace 不一樣,namespace+id 天然也就不一樣。
注:我出的。
答:使用 BatchExecutor 完成批處理。
注:我出的
答: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 生命週期範圍內。
注:我出的
答:在 MyBatis 配置文件中,能夠指定默認的 ExecutorType 執行器類型,也能夠手動給 DefaultSqlSessionFactory
的建立 SqlSession 的方法傳遞 ExecutorType 類型參數。
注:我出的
答:MyBatis 能夠映射枚舉類,不單能夠映射枚舉類,MyBatis 能夠映射任何對象到表的一列上。映射方式爲自定義一個 TypeHandler
,實現 TypeHandler
的 setParameter()
和 getResult()
接口方法。TypeHandler
有兩個做用,一是完成從 javaType 至 jdbcType 的轉換,二是完成 jdbcType 至 javaType 的轉換,體現爲 setParameter()
和 getResult()
兩個方法,分別表明設置 sql 問號佔位符參數和獲取列查詢結果。
注:我出的
答:雖然 MyBatis 解析 Xml 映射文件是按照順序解析的,可是,被引用的 B 標籤依然能夠定義在任何地方,MyBatis 均可以正確識別。
原理是,MyBatis 解析 A 標籤,發現 A 標籤引用了 B 標籤,可是 B 標籤還沒有解析到,尚不存在,此時,MyBatis 會將 A 標籤標記爲未解析狀態,而後繼續解析餘下的標籤,包含 B 標籤,待全部標籤解析完畢,MyBatis 會從新解析那些被標記爲未解析的標籤,此時再解析 A 標籤時,B 標籤已經存在,A 標籤也就能夠正常解析完成了。
注:我出的
答:MyBatis 將全部 Xml 配置信息都封裝到 All-In-One 重量級對象 Configuration 內部。在 Xml 映射文件中,<parameterMap>
標籤會被解析爲 ParameterMap
對象,其每一個子元素會被解析爲 ParameterMapping 對象。<resultMap>
標籤會被解析爲 ResultMap
對象,其每一個子元素會被解析爲 ResultMapping
對象。每個<select>、<insert>、<update>、<delete>
標籤均會被解析爲 MappedStatement
對象,標籤內的 sql 會被解析爲 BoundSql 對象。
注:我出的
答:Hibernate 屬於全自動 ORM 映射工具,使用 Hibernate 查詢關聯對象或者關聯集合對象時,能夠根據對象關係模型直接獲取,因此它是全自動的。而 MyBatis 在查詢關聯對象或關聯集合對象時,須要手動編寫 sql 來完成,因此,稱之爲半自動 ORM 映射工具。
面試題看似都很簡單,可是想要能正確回答上來,一定是研究過源碼且深刻的人,而不是僅會使用的人或者用的很熟的人,以上全部面試題及其答案所涉及的內容,在個人 MyBatis 系列博客中都有詳細講解和原理分析。