MyBatis總結

MyBatis官方文檔

什麼是MyBatis?

MyBatis是一款優秀的持久層框架,它支持定製化SQL、存儲過程以及高級映射。MyBatis避免了幾乎所有的JDBC代碼和手動設置參數以及獲取結果集。MyBatis可以使用簡單的XML或註解來配置和映射原生信息,將接口和Java的POJOs(Plain Old Java Objects,普通的Java對象)映射成數據庫中的記錄。

爲什麼要使用MyBatis?

無論是Mybatis、Hibernate都是ORM的一種實現框架,都是對JDBC的一種封裝!

image

持久層中的幾種技術:

Hibernate(一個比較老舊的框架)

  1. 優點:用起來十分舒服,sql代碼都不用寫
  2. 缺點:處理複雜業務時,靈活度差, 複雜的HQL難寫難理解,例如多表查詢的HQL語句

JDBC

  1. 優點:易理解,幾個固定的步驟
  2. 缺點:開發起來太麻煩,什麼都需要自己寫

SpringDAO

  • 其實是JDBC的一層封裝就類似於dbutils一樣

可以認爲,MyBatis就是jdbc和Hibernate之間的一個平衡點

MyBatis 在 IBatis 的基礎上做了哪些大的改進?

  1. 有接口綁定,包括註解綁定sql和xml綁定Sql;
  2. 動態sql由原來的節點配置變成OGNL表達式;
  3. 在一對一,多對一的時候引進了association,在一對多的時候引入了collection節點,不過都是在resultMap裏面配置。

MyBatis與Hibernate有哪些不同?

MyBatis和Hibernate不同,它不完全是一個ORM框架,因爲MyBatis需要程序員自己編寫Sql語句,不過MyBatis可以通過XML或註解方式靈活配置要運行的sql語句,並將java對象和sql語句映射生成最終執行的sql,最後將sql執行的結果再映射生成java對象。

Mybatis學習門檻低,簡單易學,程序員直接編寫原生態sql,可嚴格控制sql執行性能,靈活度高,非常適合對關係數據模型要求不高的軟件開發,例如互聯網軟件、企業運營類軟件等,因爲這類軟件需求變化頻繁,一但需求變化要求成果輸出迅速。但是靈活的前提是Mybatis無法做到數據庫無關性,如果需要實現支持多種數據庫的軟件則需要自定義多套sql映射文件,工作量大。

Hibernate對象/關係映射能力強,數據庫無關性好,對於關係模型要求高的軟件(例如需求固定的定製化軟件)如果用 Hibernate開發可以節省很多代碼,提高效率。但是Hibernate的缺點是學習門檻高,要精通門檻更高,而且怎麼設計O/R映射,在性能和對象模型之間如何權衡,以及怎樣用好Hibernate需要具有很強的經驗和能力才行。

總之,按照用戶的需求在有限的資源環境下只要能做出維護性、擴展性良好的軟件架構都是好架構,所以框架只有適合纔是最好。

MyBatis工作流程

  • 通過Reader對象讀取MyBatis映射文件
  • 通過SqlSessionFactoryBuilder對象創建SqlSessionFactory對象
  • 獲取當前線程的SQLSession
  • 事務默認開啓
  • 通過SQLSession讀取映射文件中的操作編號,從而讀取SQL語句
  • 提交事務
  • 關閉資源

Mybatis中的事務是默認開啓的,因此我們在完成操作以後,需要我們手動去提交事務!

#{}與${}的區別

  • #{}解析傳遞進來的參數數據。
  • ${}對傳遞進來的參數原樣拼接在SQL中。
  • #{}是預編譯處理,${}是字符串替換。
  • 使用#{}可以有效的防止SQL注入,提高系統安全性。
//使用#{}
<insert id="save" parameterType="com.zhiyou100.mybatis.pojo.User" keyColumn="id" keyProperty="id" useGeneratedKeys="true">
    insert into user(name,password,phone,age,sex) values(#{name},#{password},#{phone},#{age},#{sex})
</insert>

//控制檯顯示日誌信息
DEBUG [main] - ==>  Preparing: insert into user(name,password,phone,age,sex) values(?,?,?,?,?) 
DEBUG [main] - ==>  Parameters: www(String), 123456(String), 111111(String), 18(Integer), true(Boolean)
DEBUG [main] - <==  Updates: 1
User [id=8, name=www, password=123456, phone=111111, age=18, sex=true]
//使用${}
<insert id="save" parameterType="com.zhiyou100.mybatis.pojo.User" keyColumn="id" keyProperty="id" useGeneratedKeys="true">
		insert into user(name,password,phone,age,sex) values('${name}','${password}','${phone}','${age}',${sex})
</insert>

//控制檯顯示日誌信息
DEBUG [main] - ==>  Preparing: insert into user(name,password,phone,age,sex) values('www','123456','111111','18',true) 
DEBUG [main] - ==> Parameters: 
DEBUG [main] - <==    Updates: 1
User [id=9, name=www, password=123456, phone=111111, age=18, sex=true]

在MyBatis裏面配置外部資源文件

  • 在全局配置文件裏面利用properties標籤
<properties resource="類路徑"></properties>
  • 在src類路徑下放入資源文件
<properties url="網絡路徑或者本地磁盤路徑"></properties>
  • 配置數據源
<dataSource type="POOLED">
	<property name="driver" value="${driver}"/>
	<property name="url" value="${url}"/>
	<property name="username" value="${username}"/>
	<property name="password" value="${password}"/>
</dataSource>

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

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

<select id="select" parameterType="java.lang.Integer"  resultMap="userResultMap">
	select user_id as id, user_name as name, user_password as password, user_phone as phone, user_age as age, user_sex as sex from user where user_id=#{id}
</select>

第2種: 通過來映射字段名和實體類屬性名的一一對應的關係。

<select id="select" parameterType="java.lang.Integer"  resultMap="userResultMap">
	select id as user_id,name as user_name,password as user_password,phone as user_phone,age as user_age,sex as user_sex from user where id=#{id}
</select>

<resultMap type="com.zhiyou100.mybatis.pojo.User" id="userResultMap">
    <!–用id屬性來映射主鍵字段–>
	<id property="id" column="user_id"/>
	<!–用result屬性來映射非主鍵字段,property爲實體類屬性名,column爲數據表中的屬性–>
	<result property="name" column="user_name"/>
	<result property="password" column="user_password"/>
	<result property="phone" column="user_phone"/>
	<result property="age" column="user_age"/>
	<result property="sex" column="user_sex"/>
</resultMap>

如何獲取自動生成的(主)鍵值?

一般插入數據的話,如果想要知道剛剛插入的數據的主鍵是多少,可以通過以下的方式來獲取。

<!--user對象插入到數據庫後,新記錄的主鍵要通過user對象返回,通過user獲取主鍵值。-->
<insert id="save" parameterType="com.zhiyou100.mybatis.pojo.User" keyColumn="id" keyProperty="id" useGeneratedKeys="true">
	insert into user(name,password,phone,age,sex) values('${name}','${password}','${phone}','${age}',${sex})
</insert>

在mapper中如何傳遞多個參數?

第1種:使用佔位符的思想,在映射文件中使用#{0},#{1}代表傳遞進來的第幾個參數。
使用@param註解:來命名參數

//對應的xml,#{0}代表接收的是dao層中的第一個參數,#{1}代表dao層中第二參數,更多參數一致往後加即可。
<select id="selectUser" resultMap="BaseResultMap">  
    select * from user where user_name = #{0} and user_password = #{1}
</select>  

@param註解方式
public interface usermapper {
    user selectuser(@param(username)String username, @param(password)String password);
}
    
<select id=selectUser resulttype=user> 
    select id, username, password from user where username = #{username} and password = #{password} 
</select>

第2種:使用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>

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的功能。

詳細案例參考

動態更新 image

動態查詢 image

動態插入 image

MyBatis的Xml映射文件中,不同的Xml映射文件,id是否可以重複?

如果配置了namespace那麼當然是可以重複的,因爲我們的Statement實際上就是namespace+id。
如果沒有配置namespace的話,那麼相同的id就會導致覆蓋了。

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

Hibernate屬於全自動ORM映射工具,使用Hibernate查詢關聯對象或者關聯集合對象時,可以根據對象關係模型直接獲取,所以它是全自動的。而MyBatis在查詢關聯對象或關聯集合對象時,需要手動編寫sql來完成,所以,稱之爲半自動ORM映射工具。

通常一個Xml映射文件,都會寫一個Dao接口與之對應,請問,這個Dao接口的工作原理是什麼?Dao接口裏的方法,參數不同時,方法能重載嗎?

Dao接口,就是常說的Mapper接口
接口的全限名,就是映射文件中的namespace的值
接口的方法名,就是映射文件中MappedStatement的id值
接口方法內的參數,就是傳遞給sql的參數

package com.zhiyou100.mybatis.pojo;

import java.util.List;

public interface UserMapper {
	List<User> selectAll();
}

Mapper接口是沒有實現類的,當調用接口方法時,接口全限名+方法名拼接字符串作爲key值,可唯一定位一個MappedStatement。

<!--
舉例:com.zhiyou100.mybatis.pojo.UserMapper.selectAll   
可以唯一找到namespacecom.zhiyou100.mybatis.pojo.UserMapper下面id = selectAllMappedStatement
-->
<mapper namespace="com.zhiyou100.mybatis.pojo.UserMapper">
    <select id="selectAll" resultType="com.zhiyou100.mybatis.pojo.User">
		select * from user
	</select>
</mapper>

在Mybatis中,每一個<select>、<insert>、<update>、<delete>標籤,都會被解析爲一個MappedStatement對象。

Dao接口裏的方法,是不能重載的,因爲是全限名+方法名的保存和尋找策略。

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

詳細資料參考

接口綁定有幾種實現方式,分別是怎麼實現的?

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

  1. 通過註解綁定,就是在接口的方法上面加上@Select @Update等註解裏面包含Sql語句來綁定。
  2. 通過xml裏面寫SQL來綁定,在這種情況下,要指定xml映射文件裏面的namespace必須爲接口的全路徑名。

MyBatis是如何進行分頁的?分頁插件的原理是什麼?

MyBatis使用RowBounds對象進行分頁,它是針對ResultSet結果集執行的內存分頁,而非物理分頁,可以在sql內直接書寫帶有物理分頁的參數來完成物理分頁功能,也可以使用分頁插件來完成物理分頁。

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

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

分頁插件參考資料1
分頁插件參考資料2
分頁插件參考資料3

簡述MyBatis的插件運行原理,以及如何編寫一個插件?

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

實現MyBatis的Interceptor接口並複寫intercept()方法,然後在給插件編寫註解,指定要攔截哪一個接口的哪些方法即可,記住,別忘了在配置文件中配置你編寫的插件。

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,支持延遲加載的原理都是一樣的。

MyBatis都有哪些Executor執行器?它們之間的區別是什麼?

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

  1. SimpleExecutor:每執行一次update或select,就開啓一個Statement對象,用完立刻關閉Statement對象。
  2. ReuseExecutor:執行update或select,以sql作爲key查找Statement對象,存在就使用,不存在就創建,用完後,不關閉Statement對象,而是放置於Map內,供下一次使用。簡言之,就是重複使用Statement對象。
  3. BatchExecutor:執行update(沒有select,JDBC批處理不支持select),將所有sql都添加到批處理中(addBatch()),等待統一執行(executeBatch()),它緩存了多個Statement對象,每個Statement對象都是addBatch()完畢後,等待逐一執行executeBatch()批處理。與JDBC批處理相同。

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

參考資料:
http://blog.csdn.net/eaphyy/article/details/71190441
http://blog.csdn.net/frankaqi/article/details/51872730
http://blog.csdn.net/gcxzflgl/article/details/71456021

發佈了31 篇原創文章 · 獲贊 42 · 訪問量 8萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章