...敬請期待
通過上一篇的入門MyBatis學習筆記(1)—使用篇,可以發現除了一些基本配置外,Mybatis的精華就在於Mapper(對映)部分。Mybatis是針對對映器構造的SQL構建的輕量級框架,並且可以通過配置生成對應的JavaBean給呼叫者。在Mybatis中可以靈活使用SQL來滿足各種不同場景的需求,所以在網際網路企業內更偏愛於使用Mybatis來應對高速變化的需求,而傳統企業更偏向於使用Hibernate。
對映器的主要元素
上一節我們僅僅定義了一個select語句:
<mapper namespace="com.shuqing28.dao.CommodityDao">
<select id="getAllCommodity" resultType="com.shuqing28.pojo.Commodity">
SELECT * FROM commodity
</select>
</mapper>
複製程式碼
實際上增刪改查在Mybatis對映器裡面都有對應的元素
元素名稱 | 描述 | 備註 |
---|---|---|
select | 查詢語句,最常用的 | 可以自定義引數,返回結果集 |
insert | 插入語句 | 執行後返回一個整數,代表插入的條數 |
update | 更新語句 | 執行後返回一個整數,代表更新的條數 |
delete | 刪除語句 | 執行後返回一個整數,代表刪除的條數 |
parameterMap | 定義引數對映關係 | 即將被刪除的元素,不建議使用 |
sql | 允許定義一部分的SQL,然後在各個地方引用 | 例如,定義一個表名,在其它地方的SQL語句中使用 |
resultMap | 用來描述從資料庫結果集中載入物件 | 它將提供對映規則 |
cache | 給定名稱空間的快取配置 | ———— |
cache-ref | 其他名稱空間快取配置的引用 | ———— |
下面我們探究主要使用的幾個元素
select
select元素是我們最常用的元素,正如在SQL裡是查詢功能也是最主要的功能之一。在我們常識裡,查詢語句通常都是根據一個或者多個條件,查詢資料庫返回一個,多個或者所有欄位。之前我們舉得getAllCommodity
沒有查詢條件,返回所有欄位。
在Mybatis裡,我們可以傳入簡單的引數型別,像int,float,String這些,也可以傳入一些複雜的型別,比如JavaBean、Map之流的,Mybatis把這些引數轉換成SQL語句需要的型別後,返回結果,並且通過對映,將結果集自動繫結到JavaBean中,所以JDBC裡面那一套一個一個從ResultSet獲取值的操作都被省略了。
下面我們看一個有引數的select語句:
<select id="getCommodityById" parameterType="int" resultType="com.shuqing28.pojo.Commodity">
SELECT * FROM commodity where id=#{id}
</select>
複製程式碼
同時我們定義了介面方法:
Commodity getCommodityById(Integer id);
複製程式碼
不同於getAllCommodity
沒有傳入引數,getCommodityById
有一個int型別的傳入引數,返回的還是Commodity。
上面的select語句包含以下屬性:
- id標識了這條sql,id與介面名保持一致
- parameterType定義引數型別,這裡就是int
- resultType定義了返回值型別,這裡是我們定義的POJO類
這裡看resultType,我們查詢的是commodity的所有欄位,再次回顧欄位定義:
欄位名 | 型別 |
---|---|
id | int |
name | varchar(255) |
price | decimal(8,2) |
description | varchar(255) |
而我們的POJO類包含:
private Integer id;
private String name;
private Double price;
private String description;
複製程式碼
這裡Mybatis幫我們完成了自動對映,只要返回的SQL列名和JavaBean的屬性一致,Mybatis就可以幫助我們自動回填這些欄位而無需任何配置。我們可以再Mybatis的配置檔案裡,通過autoMappingBehavior來設定自動對映,它包含3個值:
- NONE:取消自動對映
- PARTIAL:這也是預設值,只會自動對映,沒有定義巢狀結果集對映的結果集
- FULL:會自動對映任意複雜的結果集(無論是否巢狀)
剛才我們舉例的是隻傳遞了一個引數,如果傳遞多個引數呢?這時候就要說說輸入對映了。
輸入對映
傳遞多個引數通常來說有3種方式,通過Map,通過註解,以及通過POJO物件
通過Map傳遞引數
假設我們想查詢帶有餅的食物,並且價格低於80的。那麼我們可以這麼寫SQL語句
<select id="getCommodityByMap" parameterType="map" resultType="com.shuqing28.pojo.Commodity">
SELECT * FROM commodity WHERE name like '%${name}%' AND price<#{price}
</select>
複製程式碼
因為mybatis使用xml定義Mapper,所以這裡'<'用了轉義符<
表示,其實還是一樣的。
parameterType定義為map,我們定義的介面也是接受Map作為引數
List<Commodity> getCommodityByMap(Map<String, Object> params);
複製程式碼
使用:
CommodityDao commodityDao = sqlSession.getMapper(CommodityDao.class);
Map<String, Object> paramsMap = new HashMap<String , Object>();
paramsMap.put("name", "餅");
paramsMap.put("price", 80.0);
List<Commodity> commodities = commodityDao.getCommodityByMap(paramsMap);
複製程式碼
最後列印commodities:
[Commodity{id=1001, name='野葡萄烤餅', price=10.0, description='吃完還有點餓'}, Commodity{id=1003, name='南瓜百吉餅', price=50.0, description='份大量足,可以去很遠的地方'}]
複製程式碼
雖然Map很簡單,但是光是看傳入引數Map,你不知道包含了怎樣的內容,若是深入還得看如何設定Map的,而且使用時還要一項項指定Map的Key的名字,所以不怎麼提倡使用了。
使用註解方式傳遞引數
註解方式使用了**@Param**註解,我們這樣定義介面:
List<Commodity> getCommodityByAnnation(@Param("name") String name, @Param("price") Double price);
複製程式碼
Mybatis根據@Param提供的名稱,把變數值傳到SQL語句中對應的引數中,引數的可讀性大大提高,但是不足之處在於,若是引數很多,一個一個寫很麻煩,降低了可讀性。
所以最經常使用的還是利用POJO傳遞引數。
使用POJO傳遞引數
通常我們只需要傳遞簡單的POJO物件就可以了,比如我們這樣定義select元素:
<select id="getCommodityByPOJO" parameterType="com.shuqing28.pojo.Commodity" resultType="com.shuqing28.pojo.Commodity">
SELECT * FROM commodity WHERE name like '%${name}%' AND price<#{price}
</select>
複製程式碼
測試:
@Test
public void getLowPriceCommodityByPOJO(){
SqlSession sqlSession = sqlSessionFactory.openSession();
try {
CommodityDao commodityDao = sqlSession.getMapper(CommodityDao.class);
Commodity commodity = new Commodity();
commodity.setName("餅");
commodity.setPrice(80.0);
List<Commodity> commodities = commodityDao.getCommodityByPOJO(commodity);
System.out.println(commodities);
} finally {
sqlSession.close();
}
}
複製程式碼
也會返回和上面一樣的結果,這裡Mybatis會把POJO裡對應的屬性值傳到SQL裡,然後返回結果,注意看傳入引數就是POJO的完全限定名,和ResultType是一樣的,其實可以通過別名減少名稱的長度,我們只需要在mybatis-config.xml
中的configuration中定義:
<typeAliases>
<typeAlias alias="commodity" type="com.shuqing28.pojo.Commodity" />
</typeAliases>
複製程式碼
就可以用commodity替換所有的全稱了,但是不能有重名的,否則會出錯。
有些負責的情況,我們需要定義POJO作為查詢引數,比如上面的例子,你只需要兩個輸入引數,你也可以定製一個只包含這兩個引數的POJO。
public class CommodityCustom {
private String name;
private Double price;
複製程式碼
當然使用過程還是一樣的,定義自己包裝過的POJO通常用來解決傳入查詢條件很複雜的情況,比如設計到幾張表聯查的,這時候原先定義的POJO解決不了問題,就需要定義一些複合的POJO。
在輸入引數中還有個經常被提到的問題,即#和$的區別
#和$的區別
Mybatis本身是基於JDBC封裝的。所以#{para}
是預編譯處理(PreparedStatement
),相當於原來的?
,而${para}
是字串替換。Mybatis在處理#時,會呼叫PreparedStatement
的set
系列方法來賦值;處理$
時,就是把${para}
替換成變數的值。#
方式能夠很大程度防止sql
注入,$
方式一般用於傳入資料庫物件,例如傳入表名,一般能用#
的就別用$
.
說完輸入我們說輸出對映。
輸出對映
MyBatis的輸出對映分為兩種方式,resultType和resultMap
resultType
我們上面所有的例子裡都是定義的resultType,resultType可以是簡單型別,比如我們想獲取商品數量啊之類的,還有就是輸出POJO物件或者POJO列表。
不管是POJO物件還是POJO列表,我們在resultType中的定義都是一樣的,只不過介面定義不一樣:
單個物件
Commodity getCommodityById(Integer id);
複製程式碼
Mybatis根據介面返回值判斷返回一條物件,如果用過ibatis的可以知道內部呼叫了session.selectOne。
返回列表
List<Commodity> getCommodityByPOJO(Commodity commodity);
複製程式碼
內部使用session.selectList,Mapper介面使用List<XXX>
作為返回值。
查詢出來的列名和POJO屬性只要有一個一致就會建立POJO物件,頂多別的欄位為預設值,但是如果全部不一致,就不會建立該POJO物件了。
resultMap
上面的resultType在查詢的列名和POJO屬性值一致的時候才可以對映成功,如果不一致的話,就需要resultMap登場表演了。
如果查詢出來的列名和POJO的屬性名不一致,通過定義一個resultMap對列名和POJO屬性名之間作一個對映關係。
在使用resultMap前我們需要定義resultMap
假設我們查詢商品類的兩個欄位id和name,但是查詢的時候定義為id_
和name_
。列名和屬性名不一致了,先定義resultMap
<resultMap id="commodityResultMap" type="commodity">
<id column="id_" property="id"/>
<result column="name_" property="name"/>
</resultMap>
複製程式碼
其中
- id標識這個resultMap
- type標識對映哪個POJO裡面的屬性,這裡因為上面說過別名了,所以就是對映的Commodity這個POJO
- id元素標識這個物件的主鍵,result就是普通欄位
- property代表POJO的屬性名稱
- column表示資料庫SQL的列名
再看我們的select元素:
<select id="getCommodityByPrice" parameterType="double" resultMap="commodityResultMap">
SELECT id id_, name name_ FROM USER WHERE price=#{price}
</select>
複製程式碼
使用resultType進行輸出對映時,只有查詢出來的列名和pojo中的屬性名一致,該列才可以對映成功。如果查詢出來的列名和pojo的屬性名不一致,通過定義一個resultMap對列名和pojo屬性名之間作一個對映關係。 此外,resultMap還可以做一對多、多對多等高階對映,這些內容將在後續文章中介紹。
insert
insert相對於select簡單很多,下面是往商品表插入一個商品的例項:
<insert id="insertCommodity" useGeneratedKeys="true"
keyProperty="id">
insert into commodity (name,price,description)
values (#{name},#{price},#{description})
</insert>
複製程式碼
介面為:
void insertCommodity(Commodity commodity);
複製程式碼
這裡我們注意到了insert語句裡多了兩個屬性useGeneratedKeys
和keyProperty
,這裡涉及到的是主鍵回填的概念。
- useGeneratedKeys:告訴Mybatis是否使用資料庫內建策略生成主鍵
- keyProperty:告訴Mybatis哪一列是主鍵 這樣我們無須傳入id值,Mybatis就可以幫我們設定主鍵,同時還會回填POJO內的id值,當我們像下面這樣呼叫時:
@Test
public void insertCommodity(){
SqlSession sqlSession = sqlSessionFactory.openSession();
try {
CommodityDao commodityDao = sqlSession.getMapper(CommodityDao.class);
Commodity commodity = new Commodity();
//commodity.setId(1005);
commodity.setName("艾蒿小麥餅");
commodity.setPrice(100.0);
commodity.setDescription("像在豔陽下戀愛");
commodityDao.insertCommodity(commodity);
System.out.println(commodity.getId());
} finally {
sqlSession.close();
}
}
複製程式碼
通過列印我們能得到1005
,說明commodity已經具備了id,這就是回填的效果。
在這裡,系統預設的方式是給主鍵值加1,如果我們想要定義自己的主鍵生成方式,可以使用selectKey進行自定義:
<insert id="insertCommodity" useGeneratedKeys="true"
keyProperty="id">
<selectKey keyProperty="id" resultType="int" order="BEFORE">
select if (max(id) is null, 1, max(id) + 2) as newId from commodity
</selectKey>
insert into commodity (name,price,description)
values (#{name},#{price},#{description})
</insert>
複製程式碼
這裡我們定義主鍵值是最大id加2,如果還沒有記錄,則初始化為1。
update和delete
update和的delete都會返回影響的條目數 下面僅僅列出配置的例項:
<update id="updateCommodity" parameterType="commodity">
update commodity set
name=#{name},
price=#{price},
description=#{description}
where id=#{id}
</update>
<delete id="deleteCommodity" parameterType="int">
delete from commodity where id=#{id}
</delete>
複製程式碼
這一節主要介紹了主要的幾個語句的使用,著重介紹了select語句,同時結合select語句說明了Mybatis中輸入對映和輸出對映的內容,其它一些高階的對映內容,留到後面的文章介紹。