本篇文章是「深刻淺出MyBatis:技術原理與實踐」書籍的總結筆記。java
上一篇總結了MyBatis的配置,詳細說明了各個配置項,其中提到了映射器,它是MyBatis最強大的工具,也是使用最多的工具。sql
經過映射器,能夠很容易的進行數據的增刪改查操做,咱們抽象下進行這些操做的關鍵點:傳遞查詢參數、組裝各類場景下的查詢條件、關聯查詢、將查詢結果映射爲Java Bean對象或集合等。另外,能夠經過延遲加載、緩存提升數據查詢的性能。數據庫
文章索引:
apache
本篇就按照這個思路進行總結,首先列舉下映射器的主要元素,每一個元素提供的配置項和做用,而後重點介紹參數、結果映射、延遲加載、緩存、動態SQL等功能。緩存
映射器是由Java接口和XML文件(或註解)共同組成的,Java接口主要定義調用者接口,XML文件是配置映射器的核心文件,包括如下元素:安全
執行select語句前,須要定義參數,執行後,也提供了強大的映射規則或自動映射,將返回的結果集綁定到java bean中。服務器
select元素有不少配置項,下面簡單說明下:微信
好比根據米聊號獲取用戶信息:app
<select id="findByMiliao" parameterType="string" resultType="User">
select
u.*
from mxt_user u
where u.miliao=#{miliao}
</select>
複製代碼
上一篇介紹配置時,有個設置項autoMappingBehavior,默認爲自動映射沒有定義嵌套結果集映射的結果集;還有設置項mapUnderscoreToCamelCase,設置爲true時,會自動將以「下劃線」命名的數據庫字段名,自動映射爲以「駝峯式」命名的POJO。dom
傳遞多個參數時,有3種方式:
使用註解方式以下:
public List<Role> findRoleByNameAndNote(@Param("roleName") String rolename, @Param("note") String note);
複製代碼
使用Map傳遞參數,會致使業務可讀性喪失,致使之後擴展和維護不方便,不建議;若是參數個數<=5,建議使用註解的方式,由於過多參數將給調用者帶來困難;若是參數個數>5,建議使用JavaBean方式;
使用resultMap映射結果集,後面會單獨介紹。
屬性和select大部分都相同, 說下3個不一樣的屬性:
當useGeneratedKeys設爲true時,在插入的時候,會回填Java Bean的id值,經過返回的對象可獲取主鍵值。
若是想根據一些特殊關係設置主鍵的值,能夠在insert標籤內使用selectKey標籤,好比:若是t_role沒有記錄,則須要設置爲1,不然取最大id加2:
<insert id="insertRole" useGeneratedKeys="true" keyProperty="id" >
<selectKey keyProperty="id" resultType="int" order="before">
select if(max(id) is null,1,max(id)+2) as newId from t_role
</selectKey>
</insert>
複製代碼
比較簡單,就不過多介紹了。
上面已經介紹了參數傳遞,另外能夠指定參數的類型去讓對應的typeHandler處理它們。
#{age , javaType=int , jdbcType=NUMERIC }
複製代碼
還能夠對一些數值型的參數設置其保存的精度
#{price, javaType=double , jdbcType=NUMERIC , numericScale=2 }
複製代碼
通常都是傳遞字符串,設置的參數#{name}大部分狀況下,會建立預編譯語句,但有時候傳遞的是SQL語句自己,不是須要的參數,能夠經過$符號表示,好比傳遞參數columns爲"col1,col2,col3",能夠寫成下面語句:
select ${columns} from t_tablename
複製代碼
但要注意sql的安全性,防止sql注入。
定義:
<sql id="role_columns">
id,role_name,note
</sql>
複製代碼
使用:
<include refid="role_columns">
<property name="prefix" value="r" />
</include>
複製代碼
resultMap是MyBatis裏面最複雜的元素,它的做用是定義映射規則、級聯的更新、定製類型轉換器等。
由如下元素構成:
<resultMap>
<constructor> <!-- 配置構造方法 -->
<idArg/>
<arg/>
</constructor>
<id/> <!--指明哪一列是主鍵-->
<result/> <!--配置映射規則-->
<association/> <!--一對一-->
<collection/> <!--一對多-->
<discriminator> <!--鑑別器級聯-->
<case/>
</discriminator>
</resultMap>
複製代碼
有的實體不存在沒有參數的構造方法,須要使用constructor配置有參數的構造方法:
<resultMap id="role" type="com.xiaomi.kfs.mcc.core.domain.Role">
<constructor>
<idArg column="id" javaType="int"/>
<arg column="role_name" javaType="string"/>
</constructor>
</resultMap>
複製代碼
id指明主鍵列,result配置數據庫字段和POJO屬性的映射規則:
<resultMap id="role" type="com.xiaomi.kfs.mcc.core.domain.Role">
<id property="id" column="id" />
<result property="roleName" column="role_name" />
<result property="note" column="note" />
</resultMap>
複製代碼
association、collection用於配置級聯關係的,分別爲一對一和一對多,實際中,多對多關係的應用很少,由於比較複雜,會用一對多的關係把它分解爲雙向關係。
discriminator用於這樣一種場景:好比咱們去體檢,男和女的體檢項目不一樣,若是讓男生去檢查婦科項目,是不合理的, 經過discriminator能夠根據性別,返回不一樣的對象。
級聯關係的配置比較多,就不在此演示了,可查看文檔進行了解。
級聯的優點是可以方便地獲取數據,但有時不須要獲取全部數據,這樣會多執行幾條SQL,性能降低,爲了解決這個問題,須要使用延遲加載,只要使用相關級聯數據時,纔會發送SQL去取回數據。
在MyBatis的配置中有2個全局的參數 lazyLoadingEnabled 和 aggressiveLazyLoading ,第一個的含義是是否開啓延遲加載功能,第二個的含義是對任意延遲加載屬性的調用,會使延遲加載的對象完整加載,不然只會按需加載。
再理解下aggressiveLazyLoading屬性,好比學生對象的關聯對象以下:
當訪問學生信息的時候,會根據鑑別器把健康的狀況也會查找出來;當訪問課程成績的時候,同時也會把學生證信息查找出來,由於在默認狀況下,MyBatis是按層級延遲加載的。 但這不是咱們須要的,並不但願在訪問成績的時候,去加載學生證的信息,能夠設置aggressiveLazyLoading爲false,按需進行延遲加載數據。
上面的2個屬性都是全局設置,也能夠在association和collection元素上加上屬性值fetchType,它有兩個取值eager和lazy。
在沒有顯示配置緩存時,只開啓一級緩存,一級緩存是相對於同一個SqlSession而言的,在參數和SQL徹底同樣的狀況下,使用同一個SqlSession對象調用同一個Mapper的方法,只會執行一次SQL。
若是是不一樣的SqlSession對象,由於不一樣SqlSession是相互隔離的,即便用相同的Mapper、參數和方法,仍是會再次發送SQL到數據庫去執行。
二級緩存是SqlSessionFactory層面上的,須要進行顯示配置,實現二級緩存的時候,要求POJO必須是可序列化的,只須要簡單配置便可:
<cache />
複製代碼
這樣不少設置是默認的,有以下屬性能夠配置:
在大型服務器上,可能會使用專用的緩存服務器,好比Redis緩存,能夠經過實現org.apache.ibatis.cache.Cache接口很方便的實現:
public interface Cache {
String getId(); //緩存編號
void putObject(Object var1, Object var2); //保存對象
Object getObject(Object var1); //獲取對象
Object removeObject(Object var1); //移除對象
void clear(); //清空緩存
int getSize(); //獲取緩存對象大小
ReadWriteLock getReadWriteLock(); //獲取緩存的讀寫鎖
}
複製代碼
不少時候,須要根據不一樣的場景組裝查詢條件,MyBatis提供對SQL語句動態的組裝能力。
主要提供如下幾種元素:
trim能夠處理 and 和 逗號 拼接的問題,舉例以下:
<select id="findRoles" parameterType="string" >
select id,role_name,note from t_role
<trim prefix="where" prefixOverrides="and">
<if test="roleName!=null and roleName!=''">
and role_name like concat('%',#{roleName},'%')
</if>
</trim>
</select>
複製代碼
另外,可使用set元素設置更新的字段列表:
<update id="updateRole" parameterType="role">
update t_role
<set>
<if test="roleName!=null and roleName!=''">
role_name=#{roleName},
</if>
<if test="note!=null and note!=''">
note=#{note}
</if>
</set>
where id=#{id}
</update>
複製代碼
下一篇會介紹MyBatis的解析和運行原理。
歡迎掃描下方二維碼,關注個人我的微信公衆號 ~