《MyBatis 理論 40 問》包含以下 2 篇文章:
- MyBatis 理論 40 問(一)
- MyBatis 理論 40 問(二)
MyBatis 理論 40 問(二)
- 21.如何獲取生成的主鍵?
- 22.當實體類中的屬性名和表中的字段名不一樣 ,怎么辦?
- 23.Mapper 編寫有哪幾種方式?
- 24.什么是 MyBatis 的接口綁定?有哪些實現方式?
- 25.使用 MyBatis 的 mapper 接口調用時有哪些要求?
- 26.這個 Dao 接口的工作原理是什么?Dao 接口里的方法,參數不同時,方法能重載嗎?
- 27.MyBatis 的 Xml 映射文件中,不同的 Xml 映射文件,id 是否可以重復?
- 28.簡述 MyBatis 的 Xml 映射文件和 MyBatis 內部數據結構之間的映射關系?
- 29.MyBatis 是如何將 SQL 執行結果封裝為目標對象并返回的?都有哪些映射形式?
- 30.Xml 映射文件中,除了常見的 select | insert | updae | delete 標簽之外,還有哪些標簽?
- 31.MyBatis 映射文件中,如果 A 標簽通過 include 引用了 B 標簽的內容,請問,B 標簽能否定義在 A 標簽的后面,還是說必須定義在 A 標簽的前面?
- 32.MyBatis 能執行一對多,一對一的聯系查詢嗎,有哪些實現方法?
- 33.MyBatis 是否可以映射 Enum 枚舉類?
- 34.MyBatis 動態 SQL 是做什么的?都有哪些動態 SQL?能簡述一下動態 SQL 的執行原理嗎?
- 35.MyBatis 是如何進行分頁的?分頁插件的原理是什么?
- 36.簡述 MyBatis 的插件運行原理,以及如何編寫一個插件?
- 37.MyBatis 的一級、二級緩存
21.如何獲取生成的主鍵?
新增標簽中添加:keyProperty=“ID”
即可。
<insert id="insert" useGeneratedKeys="true" keyProperty="userId" >insert into user(user_name, user_password, create_time)values(#{userName}, #{userPassword} , #{createTime, jdbcType=TIMESTAMP})
</insert>
22.當實體類中的屬性名和表中的字段名不一樣 ,怎么辦?
(1)通過在查詢的 SQL 語句中定義字段名的別名,讓字段名的別名和實體類的屬性名一致。
<select id="getOrder" parameterType="int" resultType="com.jourwon.pojo.Order">select order_id id, order_no orderno, order_price price form orderswhere order_id=#{id};
</select>
(2) 通過 <resultMap>
來映射字段名和實體類屬性名的一一對應的關系。
<select id="getOrder" parameterType="int" resultMap="orderResultMap">select * from orders where order_id=#{id}
</select><resultMap type="com.jourwon.pojo.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>
23.Mapper 編寫有哪幾種方式?
(1)接口實現類繼承 SqlSessionDaoSupport
:使用此種方法需要編寫 mapper
接口,mapper
接口實現類、mapper.xml
文件。
- 在
sqlMapConfig.xml
中配置mapper.xml
的位置。
<mappers><mapper resource="mapper.xml 文件的地址" /><mapper resource="mapper.xml 文件的地址" />
</mappers>
- 定義
mapper
接口。 - 實現類集成
SqlSessionDaoSupport
。mapper
方法中可以用this.getSqlSession()
進行數據增刪改查。 spring
配置。
<bean id=" " class="mapper 接口的實現"><property name="sqlSessionFactory" ref="sqlSessionFactory"></property>
</bean>
(2)使用 org.mybatis.spring.mapper.MapperFactoryBean
。
- 在
sqlMapConfig.xml
中配置mapper.xml
的位置,如果mapper.xml
和mapper
接口的名稱相同且在同一個目錄,這里可以不用配置。 - 定義
mapper
接口。
<mappers><mapper resource="mapper.xml 文件的地址" /><mapper resource="mapper.xml 文件的地址" />
</mappers>
mapper.xml
中的namespace
為mapper
接口的地址。mapper
接口中的方法名和mapper.xml
中的定義的statement
的id
保持一致。Spring
中定義。
<bean id="" class="org.mybatis.spring.mapper.MapperFactoryBean"><property name="mapperInterface" value="mapper 接口地址" /><property name="sqlSessionFactory" ref="sqlSessionFactory" />
</bean>
(3)使用 mapper
掃描器。
mapper.xml
文件編寫。mapper.xml
中的namespace
為mapper
接口的地址;mapper
接口中的方法名和mapper.xml
中的定義的statement
的id
保持一致;- 如果將
mapper.xml
和mapper
接口的名稱保持一致則不用在sqlMapConfig.xml
中進行配置。
- 定義
mapper
接口。注意mapper.xml
的文件名和mapper
的接口名稱保持一致,且放在同一個目錄。 - 配置
mapper
掃描器。
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer"><property name="basePackage" value="mapper 接口包地址"></property><property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"/>
</bean>
- 使用掃描器后從
spring
容器中獲取mapper
的實現對象。
24.什么是 MyBatis 的接口綁定?有哪些實現方式?
接口綁定,就是在 MyBatis 中任意定義接口,然后把接口里面的方法和 SQL 語句綁定,我們直接調用接口方法就可以,這樣比起原來的 SqlSession
提供的方法我們可以有更加靈活的選擇和設置。
接口綁定有兩種實現方式:
- 通過注解綁定,就是在接口的方法上面加上
@Select
、@Update
等注解,里面包含 SQL 語句來綁定; - 通過
xml
里面寫 SQL 來綁定, 在這種情況下,要指定xml
映射文件里面的namespace
必須為接口的全路徑名。當 SQL 語句比較簡單時候,用注解綁定, 當 SQL 語句比較復雜時候,用xml
綁定,一般用xml
綁定的比較多。
25.使用 MyBatis 的 mapper 接口調用時有哪些要求?
- Mapper 接口方法名和
mapper.xml
中定義的每個 sql 的id
相同。 - Mapper 接口方法的輸入參數類型和
mapper.xml
中定義的每個 sql 的parameterType
的類型相同。 - Mapper 接口方法的輸出參數類型和
mapper.xml
中定義的每個 sql 的resultType
的類型相同。 Mapper.xml
文件中的namespace
即是 Mapper 接口的類路徑。
26.這個 Dao 接口的工作原理是什么?Dao 接口里的方法,參數不同時,方法能重載嗎?
- Dao 接口的工作原理是 JDK 動態代理,MyBatis 運行時會使用 JDK 動態代理為 Dao 接口生成代理
proxy
對象,代理對象proxy
會攔截接口方法,轉而執行MappedStatement
所代表的 SQL,然后將 SQL 執行結果返回。 - Dao 接口里的方法是不能重載的,因為是 全限名+方法名 的保存和尋找策略。
27.MyBatis 的 Xml 映射文件中,不同的 Xml 映射文件,id 是否可以重復?
- 不同的 Xml 映射文件,如果配置了
namespace
,那么id
可以重復;如果沒有配置namespace
,那么id
不能重復;畢竟namespace
不是必須的,只是最佳實踐而已。 - 原因就是
namespace + id
是作為 M a p < S t r i n g , M a p p e d S t a t e m e n t > Map<String, MappedStatement> Map<String,MappedStatement> 的 Key 使用的,如果沒有namespace
,就剩下id
,那么,id
重復會導致數據互相覆蓋。有了namespace
,自然id
就可以重復,namespace
不同,namespace + id
自然也就不同。
28.簡述 MyBatis 的 Xml 映射文件和 MyBatis 內部數據結構之間的映射關系?
MyBatis 將所有 Xml 配置信息都封裝到 All-In-One 重量級對象 Configuration
內部。在 Xml 映射文件中,<parameterMap>
標簽會被解析為 ParameterMap
對象,其每個子元素會被解析為 ParameterMapping
對象。 <resultMap>
標簽會被解析為 ResultMap
對象,其每個子元素會被解析為 ResultMapping
對象。每一個 <select>
、<insert>
、<update>
、<delete>
標簽均會被解析為 MappedStatement
對象,標簽內的 SQL 會被解析為 BoundSql
對象。
29.MyBatis 是如何將 SQL 執行結果封裝為目標對象并返回的?都有哪些映射形式?
- 第一種是使用
<resultMap>
標簽,逐一定義列名和對象屬性名之間的映射關系。 - 第二種是使用 SQL 列的別名功能,將列別名書寫為對象屬性名,比如
T_NAME AS NAME
,對象屬性名一般是name
,小寫,但是列名不區分大小寫,MyBatis 會忽略列名大小寫,智能找到與之對應對象屬性名,你甚至可以寫成T_NAME AS NaMe
,MyBatis 一樣可以正常工作。
有了列名與屬性名的映射關系后,MyBatis 通過反射創建對象,同時使用反射給對象的屬性逐一賦值并返回,那些找不到映射關系的屬性,是無法完成賦值的。
30.Xml 映射文件中,除了常見的 select | insert | updae | delete 標簽之外,還有哪些標簽?
還有很多其他的標簽, <resultMap>
、<parameterMap>
、<sql>
、<include>
、<selectKey>
,加上動態 SQL 的 9 9 9 個標簽:trim
、where
、set
、foreach
、if
、choose
、when
、otherwise
、bind
等。其中 <sql>
為 SQL 片段標簽,通過 <include>
標簽引入 SQL 片段, <selectKey>
為不支持自增的主鍵生成策略標簽。
31.MyBatis 映射文件中,如果 A 標簽通過 include 引用了 B 標簽的內容,請問,B 標簽能否定義在 A 標簽的后面,還是說必須定義在 A 標簽的前面?
雖然 MyBatis 解析 Xml 映射文件是按照順序解析的,但是,被引用的 B 標簽依然可以定義在任何地方,MyBatis 都可以正確識別。
原理是,MyBatis 解析 A 標簽,發現 A 標簽引用了 B 標簽,但是 B 標簽尚未解析到,尚不存在,此時,MyBatis 會將 A 標簽標記為未解析狀態,然后繼續解析余下的標簽,包含 B 標簽,待所有標簽解析完畢,MyBatis 會重新解析那些被標記為未解析的標簽,此時再解析 A 標簽時,B 標簽已經存在,A 標簽也就可以正常解析完成了。
32.MyBatis 能執行一對多,一對一的聯系查詢嗎,有哪些實現方法?
能,不僅可以 一對多、一對一,還可以 多對多、多對一。實現方式如下:
- 單獨發送一個 SQL 去查詢關聯對象,賦給主對象,然后返回主對象。
- 使用嵌套查詢,類似 JOIN 查詢,一部分是 A 對象的屬性值,另一部分是關聯對象 B 的屬性值,好處是只要發送一個屬性值,就可以把主對象和關聯對象查出來。
- 子查詢
33.MyBatis 是否可以映射 Enum 枚舉類?
MyBatis 可以映射枚舉類,不僅可以映射枚舉類,MyBatis 可以映射任何對象到表的一列上。映射方式為自定義一個 TypeHandler
,實現 TypeHandler
的 setParameter()
和 getResult()
接口方法。
TypeHandler
有兩個作用,一是完成從 javaType
至 jdbcType
的轉換,二是完成 jdbcType
至 javaType
的轉換,體現為 setParameter()
和 getResult()
兩個方法,分別代表設置 sql 問號占位符參數和獲取列查詢結果。
34.MyBatis 動態 SQL 是做什么的?都有哪些動態 SQL?能簡述一下動態 SQL 的執行原理嗎?
MyBatis 動態 SQL 可以讓我們在 Xml 映射文件內,以標簽的形式編寫動態 SQL,完成邏輯判斷和動態拼接 SQL 的功能,MyBatis 提供了 9 9 9 種動態 SQL 標簽:trim
、where
、set
、foreach
、if
、choose
、when
、otherwise
、bind
。
其執行原理為,使用 OGNL
從 SQL 參數對象中計算表達式的值,根據表達式的值動態拼接 SQL,以此來完成動態 SQL 的功能。
35.MyBatis 是如何進行分頁的?分頁插件的原理是什么?
MyBatis 使用 RowBounds
對象進行分頁,它是針對 ResultSet
結果集執行的內存分頁,而非物理分頁,可以在 SQL 內直接書寫帶有物理分頁的參數來完成物理分頁功能,也可以使用分頁插件來完成物理分頁。
分頁插件的基本原理是使用 MyBatis 提供的插件接口,實現自定義插件,在插件的攔截方法內攔截待執行的 SQL,然后重寫 SQL,根據 dialect
方言,添加對應的物理分頁語句和物理分頁參數。
舉例:
select * from student
攔截 SQL 后重寫為:
select t.* from (select * from student) t limit 0, 10
36.簡述 MyBatis 的插件運行原理,以及如何編寫一個插件?
MyBatis 僅可以編寫針對 ParameterHandler
、ResultSetHandler
、StatementHandler
、Executor
這 4 4 4 種接口的插件,MyBatis 使用 JDK 的動態代理,為需要攔截的接口生成代理對象以實現接口方法攔截功能,每當執行這 4 4 4 種接口對象的方法時,就會進入攔截方法,具體就是 InvocationHandler
的 invoke()
方法,當然,只會攔截那些你指定需要攔截的方法。
實現 MyBatis 的 Interceptor
接口并復寫 intercept()
方法,然后在給插件編寫注解,指定要攔截哪一個接口的哪些方法即可,記住,別忘了在配置文件中配置你編寫的插件。
37.MyBatis 的一級、二級緩存
- 一級緩存:基于
PerpetualCache
的 HashMap 本地緩存,其存儲作用域為 Session,當 Sessionflush
或close
之后,該 Session 中的所有 Cache 就將清空,默認打開一級緩存。 - 二級緩存與一級緩存其機制相同,默認也是采用
PerpetualCache
,HashMap 存儲,不同在于其存儲作用域為Mapper
(Namespace
),并且可自定義存儲源,如Ehcache
。默認不打開二級緩存,要開啟二級緩存,使用二級緩存屬性類需要實現Serializable
序列化接口(可用來保存對象的狀態),可在它的映射文件中配置<cache/>
。 - 對于緩存數據更新機制,當某一個作用域(一級緩存 Session / 二級緩存 Namespace)進行了
C
/U
/D
操作后,默認該作用域下所有select
中的緩存將被clear
。