深入浅出 MyBatis:参数类型详解与实用小技巧
在日常 Java 后端开发中,MyBatis 以其灵活的 SQL 编写能力和较低的学习成本深受欢迎。本文在全面讲解 MyBatis 四种常见输入参数类型(简单类型、Map、POJO、多个参数)及注解原理的基础上,补充了一系列实用小技巧,帮助你写出更安全、高效、可维护的持久层代码。
输入参数类型详解
1.1 简单类型参数
示例类型:
Integer
、String
、Character
、Date
等。
parameterType
含义:告诉 MyBatis 方法参数对应的 Java 类型,用于选择合适的 JDBCps.setXxx(...)
方法。省略场景:若方法仅有一个简单类型参数,MyBatis 会自动推断类型,通常可以省略
parameterType
。
<select id="selectById" resultType="com.sangui.pojo.Student">
SELECT * FROM t_student WHERE id = #{id}
select>
执行流程
SQL 中
?
由 JDBCPreparedStatement
接管;MyBatis 根据推断出的类型调用如
ps.setLong(1,1L)
、ps.setString(1,"zhangsan")
、ps.setDate(1,new Date(...))
;如需在复杂 SQL 片段(
引用、嵌套映射)中准确生效,建议显式写出
parameterType
,以免推断失误。1.2 Map 集合参数
<insert id="insertStudentByMap" parameterType="map">
INSERT INTO t_student
VALUES (NULL, #{name}, #{age}, #{height}, #{birth}, #{sex})
insert>
可省略
parameterType="map"
;方法入参会被转为
Map
,#{}
中的键名要与 Map 的 key 一致。1.3 POJO 实体参数
<insert id="insertStudentByPojo" parameterType="com.sangui.pojo.Student">
INSERT INTO t_student
VALUES (NULL, #{name}, #{age}, #{height}, #{birth}, #{sex})
insert>
若在全局或
中已注册别名,也可只写
parameterType="Student"
;MyBatis 会调用实体的 getter 获取属性值。
1.4 多参数与
@Param
注解当 Mapper 方法有多个参数时,MyBatis 会按索引生成默认名称:
List<Student> selectByNameAndSex(String name, Character sex);
对应 XML:
<select id="selectByNameAndSex" resultType="com.sangui.pojo.Student">
SELECT * FROM t_student
WHERE name = #{arg0}
AND sex = #{arg1}
select>
也可用
#{param1}
、#{param2}
;推荐使用
@Param
对参数重命名,提升可读性:List<Student> selectByNameAndSex(
"name") String name, (
"sex") Character sex (
);<select id="selectByNameAndSex" resultType="com.sangui.pojo.Student">
SELECT * FROM t_student
WHERE name = #{name}
AND sex = #{sex}
select>源码简析
MapperProxy 调用
invoke(proxy, method, args)
;内部维护
SortedMap
,关联索引与注解名称;names 若有
@Param
,构造ParamMap
并执行param.put(entry.getValue(), args[entry.getKey()]);
最终
ParamMap
同时含有"name"->"张三"
、"sex"->'男'
,以及"param1"
、"param2"
等默认条目。1.5 多层嵌套 Map 返回与
@MapKey
注解当需要将结果封装为
Map
时,可用> @MapKey
注解:@MapKey("id")
Map> selectAllReturnBigMaps();
最外层 Map 的 key 来源于结果集中每行的
id
字段;内层 Map 包含列名—列值对。
@Test
public void testSelectAllReturnBigMaps() {
CarMapper mapper = SqlSessionUtil.openSession()
.getMapper(CarMapper.class);
Map> maps = mapper.selectAllReturnBigMaps();
System.out.println(maps);
}示例输出
{1={id=1, produce_time=2020-10-11, brand=宝马520Li}, …}
实用小技巧合集
2.1
#{}
与${}
的区别
#{}
(推荐)
使用
PreparedStatement
预编译,自动防注入。示例日志(参数绑定):
2025-05-21 09:43:26.691 [main] DEBUG CarMapper.selectByCarType -
==> Preparing: SELECT … WHERE car_type = ?
2025-05-21 09:43:26.731 [main] DEBUG CarMapper.selectByCarType -
==> Parameters: 新能源(String)
${}
(谨慎)
使用
Statement
字符串拼接,存在注入风险。示例日志(未传参):
2025-05-21 09:49:25.485 [main] DEBUG CarMapper.selectByCarType -
==> Preparing: SELECT … WHERE car_type = 新能源推荐使用场景:
动态
ORDER BY
:仅允许
ASC
/DESC
,需在 Java 端做白名单校验。动态表名:如
t_log_${date}
,也必须严格校验格式。2.2 动态表名示例
注意:对
${date}
做正则或格式校验,防止任意字符注入。2.3 批量删除:
IN
与OR
不推荐(
OR
拼接):DELETE FROM t_car WHERE id = 1 OR id = 2 OR id = 3;推荐(
IN
+):
DELETE FROM t_car WHERE id IN
#{id}
若用
${ids}
拼接字符串,也需保证ids
形如1,2,3
。2.4 模糊查询的四种写法
Java 端拼
%
+# {}
(最安全、IDE 无报红)String brand = "%比亚迪%";
Listcars = mapper.selectByBrand(brand); XML 中拼接
%
("%"#{brand}"%"
)(会有 IDE 报红,但可运行)使用 SQL 函数
CONCAT
(IDE 友好,兼容性好)
${}
+CONCAT
(不推荐,存在注入风险)2.5 类型别名(Type Alias)
别名可省略,默认为类名首字母小写;
不区分大小写,增强 XML 可读性。
2.6 Mapper 配置载入方式
resource:相对类路径,移植性好;
class:指定接口,自动寻同包下 XML;
package:批量扫描,最简洁。
2.7 IDEA 代码模板
在 File → Settings → Editor → File and Code Templates 中,可自定义:
MyBatis 核心配置文件模板
Mapper XML 模板
JDBC/Logback 配置模板
新建时自动生成,显著提升开发效率。
2.8 插入数据时自动回写主键
Car car = new Car(null, "10086", "比亚迪汉", 7.98, "2022-11-11", "燃油车");
mapper.insertCar(car);
System.out.println("插入成功,主键 = " + car.getId());
INSERT INTO t_car
(car_num, brand, guide_price, produce_time, car_type)
VALUES (#{carNum}, #{brand}, #{guidePrice}, #{produceTime}, #{carType})
useGeneratedKeys="true"
会将数据库自动生成的主键回写到car.id
。
- 微信
- 赶快加我聊天吧
- 赶快加我聊天吧