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. 專案
- 工程:https://github.com/liuyueyi/spring-boot-demo
- 原始碼:https://github.com/liuyueyi/spring-boot-demo/tree/master/spring-boot/103-mybatis-xml
系列博文
1. 微信公眾號: 一灰灰 Blog
盡信書則不如,以上內容,純屬一家之言,因個人能力有限,難免有疏漏和錯誤之處,如發現 bug 或者有更好的建議,歡迎批評指正,不吝感激
下面一灰灰的個人部落格,記錄所有學習和工作中的博文,歡迎大家前去逛逛
- 一灰灰 Blog 個人部落格 https://blog.hhui.top
- 一灰灰 Blog-Spring 專題部落格 http://spring.hhui.top