MyBatis學習筆記(2)—對映關係篇

卡巴拉的樹發表於2018-01-27

MyBatis學習筆記(2)—對映關係篇
MyBatis學習筆記(1)—使用篇

MyBatis學習筆記(2)—對映關係篇

MyBatis學習筆記(3)—高階對映之一對一對映

Mybatis學習筆記(4)-高階對映之一對多對映

Mybatis學習筆記(5)-高階對映之多對多對映

...敬請期待

通過上一篇的入門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&lt;#{price}
        </select>
複製程式碼

因為mybatis使用xml定義Mapper,所以這裡'<'用了轉義符&lt;表示,其實還是一樣的。 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&lt;#{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在處理#時,會呼叫PreparedStatementset系列方法來賦值;處理$時,就是把${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語句裡多了兩個屬性useGeneratedKeyskeyProperty,這裡涉及到的是主鍵回填的概念。

  • 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中輸入對映和輸出對映的內容,其它一些高階的對映內容,留到後面的文章介紹。

相關文章