手寫基本流程
流程
- 定義接口 Mapper 和方法,用來調用數據庫操作。Mapper 接口操作數據庫需要通過代理類。
- 定義配置類對象 Configuration。
- 定義應用層的 API SqlSession。它有一個 getMapper()方法,我們會從配置類Configuration 裏面使用 Proxy.newProxyInatance()拿到一個代理對象MapperProxy。
- 有了代理對象 MapperProxy 之後,我們調用接口的任意方法,就是調用代理對象的 invoke()方法。
- 代理對象 MapperProxy 的 invoke()方法調用了 SqlSession 的 selectOne()。
- SqlSession 只是一個 API,還不是真正的 SQL 執行者,所以接下來會調用執行器 Executor 的 query()方法。
- 執行器 Executor 的 query()方法裏面就是對 JDBC 底層的 Statement 的封裝,最終實現對數據庫的操作,和結果的返回。
附上手寫Mybatis github地址
ps:只是簡單的理解加深印象,裏面有很多不足。
面試分析
- resultType 和 resultMap 的區別?
resultType 是<select>標籤的一個屬性,適合簡單對象(POJO、JDK 自帶類型:Integer、String、Map 等)只能自動映射,適合單表簡單查詢。
resultMap 是一個可以被引用的標籤,適合複雜對象,可指定映射關係,適合關聯複合查詢。
- 標籤collection 和 標籤association 的區別?
association:一對一
collection:一對多、多對多
- PrepareStatement 和 Statement 的區別?
兩個都是接口,PrepareStatement 是繼承自 Statement 的;
Statement 處理靜態 SQL,PreparedStatement 主要用於執行帶參數的語句;
PreparedStatement 的 addBatch()方法一次性發送多個查詢給數據庫;
PS 相似 SQL 只編譯一次(對語句進行了緩存,相當於一個函數),減少編譯次數;
PS 可以防止 SQL 注入;
MyBatis 默認值:PREPARED 也就是PreparedStatement
- MyBatis 解決了什麼問題?
1) 資源管理(底層對象封裝和支持數據源)
2)結果集自動映射
3)SQL 與代碼分離,集中管理
4)參數映射和動態 SQL
5)其他:緩存、插件等
- MyBatis 編程式開發中的核心對象及其作用?
SqlSessionFactoryBuilder 創建工廠類
SqlSessionFactory 創建會話
SqlSession 提供操作接口
MapperProxy 代理 Mapper 接口後,用於找到 SQL 執行
- Java 類型和數據庫類型怎麼實現相互映射?
通過 TypeHandler,例如 Java 類型中的 String 要保存成 varchar,就會自動調用相應的 Handler。如果沒有系統自帶的 TypeHandler,也可以自定義--比如之前文章中寫的json轉對象的操作。
- SIMPLE/REUSE/BATCH 三種執行器的區別?
SimpleExecutor 使用後直接關閉 Statement:closeStatement(stmt);
ReuseExecutor 放在緩存中,可複用:PrepareStatement:getStatement();
BatchExecutor 支持複用且可以批量執行 update(),通過 ps.addBatch()實現handler.batch(stmt);
- MyBatis 一級緩存與二級緩存的區別?
一級緩存:在同一個會話(SqlSession)中共享,默認開啓,維護在 BaseExecutor中。
二級緩存:在同一個 namespace 共享,需要在 Mapper.xml 中開啓,維護在CachingExecutor 中。
- MyBaits 支持哪些數據源類型?
UNPOOLED:不帶連接池的數據源。
POOLED : 帶 連 接 池 的 數 據 源 , 在 PooledDataSource 中 維 護PooledConnection。
JNDI:使用容器的數據源,比如 Tomcat 配置了 C3P0。
自定義數據源:實現 DataSourceFactory 接口,返回一個 DataSource。當 MyBatis 集成到 Spring 中的時候,使用 Spring 的數據源
- 關聯查詢的延遲加載是怎麼實現的?
動態代理(JAVASSIST、CGLIB),在創建實體類對象時進行代理,在調用代理對象的相關方法時觸發二次查詢。
- MyBatis 翻頁的幾種方式和區別?
邏輯翻頁:通過 RowBounds 對象。
物理翻頁:通過改寫 SQL 語句,可用插件攔截 Executor 實現。
- 解析全局配置文件的時候,做了什麼?
創建 Configuration,設置 Configuration
解析 Mapper.xml,設置 MappedStatement
- 沒有實現類,MyBatis 的方法是怎麼執行的?
MapperProxy 代理,代理類的 invoke()方法中調用了 SqlSession.selectOne()
- 接口方法和映射器的 statement id 是怎麼綁定起來的?(怎麼根據接口方法拿到 SQL 語句的?)
MappedStatement 對象中存儲了 statement 和 SQL 的映射關係
- 四大對象是什麼時候創建的?
Executor:openSession()
StatementHandler、ResultsetHandler、ParameterHandler:
執行 SQL 時,在 SimpleExecutor 的 doQuery()中創建
- MyBatis 哪些地方用到了代理模式?
接口查找 SQL:MapperProxy
日誌輸出:ConnectionLogger、StatementLogger
連接池:PooledDataSource 管理的 PooledConnection
延遲加載:ProxyFactory(JAVASSIST、CGLIB)
插件:Plugin
Spring 集成:SqlSessionTemplate 的內部類 SqlSessionInterceptor
- MyBatis 插件怎麼編寫和使用?原理是什麼?
使用:繼承 Interceptor 接口,加上註解,在 mybatis-config.xml 中配置
原理:動態代理,責任鏈模式,使用 Plugin 創建代理對象
在被攔截對象的方法調用的時候,先走到 Plugin 的 invoke()方法,再走到Interceptor 實現類的 intercept()方法,
最後通過 Invocation.proceed()方法調用被攔截對象的原方法
- JDK 動態代理,代理能不能被代理?
能。層層代理先進後出
- MyBatis 集成到 Spring 的原理是什麼?
SqlSessionTemplate 中有內部類SqlSessionInterceptor對DefaultSqlSession進行代理;
MapperFactoryBean 繼 承 了 SqlSessionDaoSupport 獲 取SqlSessionTemplate;
接口註冊到 IOC 容器中的 beanClass 是 MapperFactoryBean。
- DefaulSqlSession 和 SqlSessionTemplate 的區別是什麼?
一個線程安全一個線程不安全
1)爲什麼 SqlSessionTemplate 是線程安全的?
其內部類 SqlSessionInterceptor 的 invoke()方法中的 getSqlSession()方法:如果當前線程已經有存在的
SqlSession 對象,會在 ThreadLocal 的容器中拿到SqlSessionHolder,獲取 DefaultSqlSession。
如果沒有,則會 new 一個 SqlSession,並且綁定到 SqlSessionHolder,放到ThreadLocal 中。SqlSessionTemplate
中在同一個事務中使用同一個 SqlSession。調用 closeSqlSession()關閉會話時,如果存在事務,減少 holder 的引用計數。否則直接關閉 SqlSession。
2) 在編程式的開發中,有什麼方法保證 SqlSession 的線程安全?
SqlSessionManager 同時實現了 SqlSessionFactory、SqlSession 接口,通過ThreadLocal 容器維護 SqlSession。