深入浅出 MyBatis:参数类型详解与实用小技巧

前言

在日常 Java 后端开发中,MyBatis 以其灵活的 SQL 编写能力和较低的学习成本深受欢迎。本文在全面讲解 MyBatis 四种常见输入参数类型(简单类型、Map、POJO、多个参数)及注解原理的基础上,补充了一系列实用小技巧,帮助你写出更安全、高效、可维护的持久层代码。

输入参数类型详解

1.1 简单类型参数

  • 示例类型IntegerStringCharacterDate 等。

  • parameterType 含义:告诉 MyBatis 方法参数对应的 Java 类型,用于选择合适的 JDBC ps.setXxx(...) 方法。

  • 省略场景:若方法仅有一个简单类型参数,MyBatis 会自动推断类型,通常可以省略 parameterType


<select id="selectById" resultType="com.sangui.pojo.Student">
SELECT * FROM t_student WHERE id = #{id}
select>
  • 执行流程

    1. SQL 中 ? 由 JDBC PreparedStatement 接管;

    2. MyBatis 根据推断出的类型调用如 ps.setLong(1,1L)ps.setString(1,"zhangsan")ps.setDate(1,new Date(...))

    3. 如需在复杂 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(
 @Param("name") String name,
 @Param("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 批量删除:INOR

  • 不推荐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 模糊查询的四种写法

  1. Java 端拼 % + # {}(最安全、IDE 无报红)

    String brand = "%比亚迪%";
    List cars = mapper.selectByBrand(brand);
  2. XML 中拼接 %"%"#{brand}"%"(会有 IDE 报红,但可运行)

  3. 使用 SQL 函数 CONCAT(IDE 友好,兼容性好)

  4. ${} + 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

  • 微信
  • 赶快加我聊天吧
  • QQ
  • 赶快加我聊天吧
  • weinxin
三桂

发表评论 取消回复 您未登录,登录后才能评论,前往登录