什麼是MyBatis?
MyBatis是一款優秀的持久層框架,它支持定製化SQL、存儲過程以及高級映射。MyBatis避免了幾乎所有的JDBC代碼和手動設置參數以及獲取結果集。MyBatis可以使用簡單的XML或註解來配置和映射原生信息,將接口和Java的POJOs(Plain Old Java Objects,普通的Java對象)映射成數據庫中的記錄。
爲什麼要使用MyBatis?
無論是Mybatis、Hibernate都是ORM的一種實現框架,都是對JDBC的一種封裝!
持久層中的幾種技術:
Hibernate(一個比較老舊的框架)
- 優點:用起來十分舒服,sql代碼都不用寫
- 缺點:處理複雜業務時,靈活度差, 複雜的HQL難寫難理解,例如多表查詢的HQL語句
JDBC
- 優點:易理解,幾個固定的步驟
- 缺點:開發起來太麻煩,什麼都需要自己寫
SpringDAO
- 其實是JDBC的一層封裝就類似於dbutils一樣
可以認爲,MyBatis就是jdbc和Hibernate之間的一個平衡點
MyBatis 在 IBatis 的基礎上做了哪些大的改進?
- 有接口綁定,包括註解綁定sql和xml綁定Sql;
- 動態sql由原來的節點配置變成OGNL表達式;
- 在一對一,多對一的時候引進了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的功能。
動態更新
動態查詢
動態插入
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
可以唯一找到namespace爲com.zhiyou100.mybatis.pojo.UserMapper下面id = selectAll的MappedStatement。
-->
<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執行結果返回。
接口綁定有幾種實現方式,分別是怎麼實現的?
接口綁定有兩種實現方式:
- 通過註解綁定,就是在接口的方法上面加上@Select @Update等註解裏面包含Sql語句來綁定。
- 通過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
簡述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。
- SimpleExecutor:每執行一次update或select,就開啓一個Statement對象,用完立刻關閉Statement對象。
- ReuseExecutor:執行update或select,以sql作爲key查找Statement對象,存在就使用,不存在就創建,用完後,不關閉Statement對象,而是放置於Map內,供下一次使用。簡言之,就是重複使用Statement對象。
- 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