MyBatis 传递多个参数的 5 种方式
1. 使用 @Param 注解(推荐)
// Mapper接口
User selectUser(@Param("username") String name,
@Param("age") int age);
// XML中使用
<select id="selectUser">
SELECT * FROM user
WHERE username = #{username} AND age = #{age}
</select>
优点:
-
参数名清晰,可读性强
-
支持基本类型和对象混合传参
2. 通过 Map 传递
// Java代码
Map<String, Object> params = new HashMap<>();
params.put("name", "张三");
params.put("age", 25);
mapper.selectByMap(params);
// XML中使用
<select id="selectByMap" parameterType="map">
SELECT * FROM user
WHERE username = #{name} AND age = #{age}
</select>
适用场景:
-
动态条件查询
-
参数数量不固定时
3. 使用 JavaBean 对象
// 定义参数对象
@Data
public class UserQuery {
private String username;
private Integer age;
}
// Mapper接口
List<User> selectByCondition(UserQuery query);
// XML中使用属性名直接引用
<select id="selectByCondition" parameterType="UserQuery">
SELECT * FROM user
WHERE username = #{username} AND age = #{age}
</select>
优点:
-
参数结构化
-
便于复用和扩展
4. 参数位置索引(不推荐)
// Mapper接口
User selectUser(String name, int age);
// XML中使用arg0/arg1或param1/param2
<select id="selectUser">
SELECT * FROM user
WHERE username = #{arg0} AND age = #{param2}
</select>
缺点:
-
可读性差
-
参数顺序变更会导致错误
5. 混合使用 @Param 和 JavaBean
// 接口方法
List<User> selectByMixed(@Param("page") PageInfo page,
@Param("query") UserQuery query);
// XML中使用
<select id="selectByMixed">
SELECT * FROM user
WHERE username = #{query.username}
AND age = #{query.age}
LIMIT #{page.offset}, #{page.size}
</select>
适用场景:
-
分页查询+条件过滤组合
-
需要复用参数对象时
各方案对比
方式 | 可读性 | 维护性 | 适用场景 |
---|---|---|---|
@Param 注解 | ★★★★★ | ★★★★☆ | 参数少且明确 |
Map 传参 | ★★☆☆☆ | ★★☆☆☆ | 动态参数 |
JavaBean | ★★★★☆ | ★★★★★ | 复杂条件查询 |
位置索引 | ★☆☆☆☆ | ★☆☆☆☆ | 遗留代码兼容(不推荐新用) |
混合模式 | ★★★★☆ | ★★★★☆ | 需要组合参数时 |
最佳实践建议
-
2-3个参数:优先用
@Param
注解 -
4+个参数:封装为JavaBean
-
动态查询:Map 或 JavaBean + Builder模式
-
避免:使用
arg0/param1
这种隐式引用
特殊场景处理
批量操作传参
<update id="batchUpdate">
UPDATE user SET status = #{status}
WHERE id IN
<foreach item="id" collection="ids" open="(" separator="," close=")">
#{id}
</foreach>
</update>
调用方式:
mapper.batchUpdate(@Param("status") int status,
@Param("ids") List<Long> ids);
存储过程调用
<select id="callProcedure" statementType="CALLABLE">
{call user_procedure(
#{username,mode=IN},
#{age,mode=IN},
#{count,mode=OUT,jdbcType=INTEGER}
)}
</select>
原理图解
通过合理选择传参方式,可以使代码既清晰又灵活。
MyBatis多参数传递的餐厅点餐比喻
1. @Param注解 → 自助点餐机
服务员.下单(
@Param("主食") "牛肉面",
@Param("饮料") "冰红茶"
);
-
操作方式:每个菜品有明确分类按钮
-
优势:厨房(MyBatis)清楚看到:
-
#{主食}
→ 牛肉面 -
#{饮料}
→ 冰红茶
-
-
特点:适合点2-3个明确菜品
2. Map传参 → 手写便签条
Map 便签 = new HashMap();
便签.put("特辣", true);
便签.put("加料", "卤蛋");
服务员.下单(便签);
-
特点:
-
厨师要自己解读潦草字迹(通过key取值)
-
适合临时加特殊要求
-
-
风险:字迹不清可能上错菜(key写错)
3. JavaBean对象 → 标准菜单
菜单 订单 = new 菜单();
订单.set主菜("宫保鸡丁");
订单.set甜点("杨枝甘露");
服务员.下单(订单);
-
优势:
-
菜单有固定栏目(属性字段)
-
厨师直接看栏目做菜(
#{主菜}
自动匹配)
-
-
最佳实践:点全套套餐时(前菜+主菜+甜点+饮料)
4. 参数位置 → 神秘盲盒餐
服务员.下单("鱼香肉丝", "微辣"); // 第一个是菜?第二个是辣度?
厨房解读:
炒#{param1},口味#{param2}
-
灾难现场:
-
如果调换顺序:
下单("重辣","水煮鱼")
→ 炒"重辣"?
-
-
如同:让厨师蒙眼做菜
5. 混合模式 → 套餐+单点
服务员.下单(
@Param("套餐") 情侣套餐,
@Param("加菜") 单点小龙虾
);
厨房处理:
准备#{套餐.主菜}
额外制作#{加菜}
-
适用场景:既有固定套餐又想额外加菜时
餐厅情景剧
顾客A:(用@Param)
"我要①#{主食}:饺子,②#{蘸料}:醋+辣椒"
顾客B:(用Map)
扔出一张便签:"多加香菜 不要蒜"
顾客C:(用JavaBean)
递上勾选好的标准菜单单
顾客D:(错误示范)
对着后厨大喊:"米饭 红烧肉"
(厨师困惑:到底是"红烧肉配米饭"还是"红烧肉味的米饭"?)
厨师长建议(最佳实践)
-
单人简餐:用点餐机(@Param)
-
清晰高效:"#{菜名}:鱼香肉丝 #{辣度}:中辣 "
-
-
定制套餐:填菜单(JavaBean)
套餐.set主菜("牛排");
套餐.set配菜("土豆泥");
3.特殊要求:附加便签(Map)
extras.put("忌口", "不要洋葱");
4.千万别:
对着厨房乱喊(参数位置)
"面条 牛肉" ← 这是要牛肉面?还是面条+牛肉两道菜?
为什么不要用参数位置?
就像去餐厅说:"给我第一个和第三个"
厨师内心OS:
"第一个是筷子?第三个是餐巾纸?
还是菜单上第一道和第三道菜?"
👉 永远给参数明确的名字!