SpringBoot系列Mybatis之引數傳遞的幾種姿勢

小灰灰Blog發表於2021-09-25

SpringBoot 系列 Mybatis 之引數傳遞的幾種姿勢

在 mybatis 的日常開發中,mapper 介面中定義的引數如何與 xml 中的引數進行對映呢?除了我們常用的@Param註解之外,其他的方式是怎樣的呢?

  • 不新增註解預設場景會怎樣?
  • 介面引數型別為Map/POJO又該如何處理?

本文將主要介紹一下mybatis的日常開發中,mapper介面中的定義的引數與xml中佔位符的幾種對映繫結方式

<!-- more -->

I. 環境配置

我們使用 SpringBoot + Mybatis + MySql 來搭建例項 demo

  • springboot: 2.2.0.RELEASE
  • mysql: 5.7.22

1. 專案配置

<dependencies>
    <dependency>
        <groupId>org.mybatis.spring.boot</groupId>
        <artifactId>mybatis-spring-boot-starter</artifactId>
        <version>2.2.0</version>
    </dependency>
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
    </dependency>
</dependencies>

核心的依賴mybatis-spring-boot-starter,至於版本選擇,到 mvn 倉庫中,找最新的

另外一個不可獲取的就是 db 配置資訊,appliaction.yml

spring:
  datasource:
    url: jdbc:mysql://127.0.0.1:3306/story?useUnicode=true&characterEncoding=UTF-8&useSSL=false&serverTimezone=Asia/Shanghai
    username: root
    password:

2. 資料庫表

用於測試的資料庫

CREATE TABLE `money` (
  `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
  `name` varchar(20) NOT NULL DEFAULT '' COMMENT '使用者名稱',
  `money` int(26) NOT NULL DEFAULT '0' COMMENT '錢',
  `is_deleted` tinyint(1) NOT NULL DEFAULT '0',
  `create_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '建立時間',
  `update_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新時間',
  PRIMARY KEY (`id`),
  KEY `name` (`name`)
) ENGINE=InnoDB AUTO_INCREMENT=551 DEFAULT CHARSET=utf8mb4;

II. 引數傳遞

接下來我們看一下 Mapper 介面中的引數與 xml 檔案中的引數對映的幾種姿勢;關於 mybatis 專案的搭建,這裡就略過,重點資訊有下面幾個

資料庫實體物件

@Data
public class MoneyPo {
    private Integer id;

    private String name;

    private Long money;

    private Integer isDeleted;

    private Timestamp createAt;

    private Timestamp updateAt;

    private Integer cnt;
}

mapper 介面

@Mapper
public interface MoneyMapper {
}

xml 檔案,在資原始檔夾下,目錄層級與 mapper 介面的包路徑完全一致(遵循預設的 Mapper 介面與 xml 檔案繫結關係,詳情檢視SpringBoot 系列 Mybatis 之 Mapper 介面與 Sql 繫結幾種姿勢

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.git.hui.boot.mybatis.mapper.MoneyMapper">

    <resultMap id="BaseResultMap" type="com.git.hui.boot.mybatis.entity.MoneyPo">
        <id column="id" property="id" jdbcType="INTEGER"/>
        <result column="name" property="name" jdbcType="VARCHAR"/>
        <result column="money" property="money" jdbcType="INTEGER"/>
        <result column="is_deleted" property="isDeleted" jdbcType="TINYINT"/>
        <result column="create_at" property="createAt" jdbcType="TIMESTAMP"/>
        <result column="update_at" property="updateAt" jdbcType="TIMESTAMP"/>
    </resultMap>
    <sql id="money_po">
      id, name, money, is_deleted, create_at, update_at
    </sql>
</mapper>

1. @Param 註解

在介面的引數上新增@Param註解,在內部指定傳遞給 xml 的引數名

一個簡單的 case 如下

int addMoney(@Param("id") int id, @Param("money") int money);

重點關注上面的引數

  • 通過@Param來指定傳遞給 xml 時的引數名

對應的 xml 檔案中的 sql 如下,使用#{}來實現引數繫結

<update id="addMoney" parameterType="java.util.Map">
    update money set money=money+#{money} where id=#{id}
</update>

2. 單引數

接下來我們看一下不使用@Param註解時,預設場景下,xml 中應該如何指定引數;因為單引數與多引數的實際結果不一致,這裡分開進行說明

單引數場景下,xml 中的引數名,可以用任意值來表明

mapper 介面定義如下

/**
 * 單個引數時,預設可以直接通過引數名來表示,實際上#{}中用任意一個值都可以,沒有任何限制,都表示的是這個唯一的引數
 * @param id
 * @return
 */
MoneyPo findById(int id);

/**
 * 演示xml中的 #{} 為一個匹配補上的字串,也可以正確的實現引數替換
 * @param id
 * @return
 */
MoneyPo findByIdV2(int id);

對應的 xml 檔案內容如下

<select id="findById" parameterType="java.lang.Integer" resultMap="BaseResultMap">
    select
    <include refid="money_po"/>
    from money where id=#{id}
</select>

<select id="findByIdV2" parameterType="java.lang.Integer" resultMap="BaseResultMap">
    select
    <include refid="money_po"/>
    from money where id=#{dd}
</select>

重點看一下上面的findByIdV2,上面的 sql 中傳參使用的是 #{dd},和 mapper 介面中的引數名並不相同,但是最終的結果卻沒有什麼區別

3. 多引數

當引數個數超過 1 個的時候,#{}中的引數,有兩種方式

  • param1...N: 其中 n 代表的介面中的第幾個引數
  • arg0...N
/**
 * 不指定引數名時,mybatis自動封裝一個  param1 ... paramN的Map,其中n表示第n個引數
 * 也可以使用 arg0...n 來指代具體的引數
 *
 * @param name
 * @param money
 * @return
 */
List<MoneyPo> findByNameAndMoney(String name, Integer money);

對應的 xml 如下

<select id="findByNameAndMoney" resultMap="BaseResultMap">
    select
    <include refid="money_po"/>
    -- from money where name=#{param1} and money=#{param2}
    from money where name=#{arg0} and money=#{arg1}
</select>

注意上面的 xml 中,兩種傳參都是可以的,當然不建議使用這種預設的方式來傳參,因為非常不直觀,對於後續的維護很不優雅

3. Map 傳參

如果引數型別並不是簡單型別,當時 Map 型別時,在 xml 檔案中的引數,可以直接使用 map 中對應的 key 來指代

/**
 * 引數型別為map時,直接使用key即可
 * @param map
 * @return
 */
List<MoneyPo> findByMap(Map<String, Object> map);

對應的 xml 如下

<select id="findByMap" resultMap="BaseResultMap">
    select
    <include refid="money_po"/>
    from money
    <trim prefix="WHERE" prefixOverrides="AND | OR">
        <if test="id != null">
            id = #{id}
        </if>
        <if test="name != null">
            AND name=#{name}
        </if>
        <if test="money != null">
            AND money=#{money}
        </if>
    </trim>
</select>

4. POJO 物件

另外一種常見的 case 是傳參為簡單的實體物件,這個時候 xml 中的引數也可以直接使用物件的 fieldName 來指代,和 map 的使用方式差不多

/**
 * 引數型別為java物件,同樣直接使用field name即可
 * @param po
 * @return
 */
List<MoneyPo> findByPo(MoneyPo po);

對應的 xml 檔案如下

<select id="findByPo" parameterType="com.git.hui.boot.mybatis.entity.MoneyPo" resultMap="BaseResultMap">
    select
    <include refid="money_po"/>
    from money
    <trim prefix="WHERE" prefixOverrides="AND | OR">
        <if test="id != null">
            id = #{id}
        </if>
        <if test="name != null">
            AND name=#{name}
        </if>
        <if test="money != null">
            AND money=#{money}
        </if>
    </trim>
</select>

5. 簡單引數 + Map 引數

當引數有多個,其中部分為簡單型別,部分為 Map,這樣的場景下引數如何處理呢?

  • 簡單型別遵循上面的規則
  • map 引數的傳參,使用字首 + "." + key 的方式

一個例項如下

List<MoneyPo> findByIdOrCondition(@Param("id") int id, @Param("map") Map<String, Object> map);

List<MoneyPo> findByIdOrConditionV2(int id, Map<String, Object> map);

對應的 xml 如下

<select id="findByIdOrCondition" resultMap="BaseResultMap">
    select <include refid="money_po"/> from money where id = #{id} or  `name`=#{map.name}
</select>

<select id="findByIdOrConditionV2" resultMap="BaseResultMap">
    select <include refid="money_po"/> from money where id = #{param1} or `name`=#{param2.name}
</select>

6.小結

本文主要介紹 mybatis 中傳參的幾種姿勢:

  • 預設場景下,單引數時,xml 檔案中可以用任意名稱代替傳參
  • 預設場景下,多引數時,第一個引數可用 param1 或 arg0 來表示,第二個引數為 param2 或 arg1。。。
  • 單引數,且為 map 時,可以直接使用 map 的 key 作為傳參
  • 單引數,pojo 物件時,使用物件的 fieldName 來表示傳參
  • @Param 註解中定義的值,表示這個引數與 xml 中的佔位對映關聯
  • 多引數場景下,簡單物件 + map/pojo 時,對於 map/pojo 中的引數佔位,可以通過 paramN.xxx 的方式來完成

最後一個問題來了,mybatis是如何將mapper介面中引數與xml中的佔位進行關聯對映的呢?

預知後事如何,且看下文詳述;我是一灰灰,歡迎各位大佬關注回訪

III. 不能錯過的原始碼和相關知識點

0. 專案

系列博文

1. 微信公眾號: 一灰灰 Blog

盡信書則不如,以上內容,純屬一家之言,因個人能力有限,難免有疏漏和錯誤之處,如發現 bug 或者有更好的建議,歡迎批評指正,不吝感激

下面一灰灰的個人部落格,記錄所有學習和工作中的博文,歡迎大家前去逛逛

一灰灰blog

相關文章