深入浅出 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。
- 微信
- 赶快加我聊天吧

- 赶快加我聊天吧
