一、parameterType 属性的用法parameterType是 MyBatis 映射文件中用来声明传入参数类型的属性。其实它大多数时候可以省略但理解它有助于你明白参数是怎么被处理的。1. 基本作用告诉 MyBatis这个 SQL 接收什么类型的参数。select idselectById parameterTypejava.lang.Integer resultTypeUser SELECT * FROM user WHERE id #{id} /select属性含义parameterType传入参数的类型可省略resultType返回结果的类型不可省略2. 各种参数类型的写法① 单个基本类型Integer / String / Long!-- 三种写法等价 -- select idselectById parameterTypeint select idselectById parameterTypejava.lang.Integer select idselectById !-- 省略也行MyBatis 自动推断 -- SELECT * FROM user WHERE id #{id} /select调用User user mapper.selectById(100);② 单个对象最常用insert idinsertUser parameterTypecom.example.User INSERT INTO user(name, age) VALUES (#{name}, #{age}) /insert调用User user new User(); user.setName(张三); user.setAge(20); mapper.insertUser(user);${}或#{}里的属性名直接写对象的属性名#{name}、#{age}MyBatis 会自动调用user.getName()、user.getAge()。③ Map 类型select idselectByCondition parameterTypejava.util.HashMap SELECT * FROM user WHERE name #{name} AND age #{minAge} /select调用MapString, Object map new HashMap(); map.put(name, 张三); map.put(minAge, 18); mapper.selectByCondition(map);#{}里的 key就是 Map 的key名。④ 多个参数不用 parameterType用注解重要如果方法有多个参数不能直接用parameterType要用Param注解。// Mapper 接口 User selectByNameAndAge(Param(name) String name, Param(age) Integer age);!-- 不需要 parameterType -- select idselectByNameAndAge resultTypeUser SELECT * FROM user WHERE name #{name} AND age #{age} /select原理MyBatis 会自动把多个参数包装成一个MapParam(name)就是 Map 的key。⑤ List / Array 类型用于 IN 查询select idselectByIds parameterTypejava.util.List resultTypeUser SELECT * FROM user WHERE id IN foreach collectionlist itemid open( separator, close) #{id} /foreach /select调用ListInteger ids Arrays.asList(1, 2, 3); mapper.selectByIds(ids);注意collectionlist是固定写法如果是数组写collectionarray。核心规则MyBatis 怎么确定 collection 的名字调用方式collection应该写什么原因无ParamList参数listMyBatis 对单个 List 参数的默认别名无Param 数组参数arrayMyBatis 对单个数组参数的默认别名有Param(ids)ids显式指定了参数名以注解值为准多个参数无Paramparam1,param2...多参数时按位置命名极不推荐传入 MapMap 的 key直接用 key 作为变量名回到代码中情况一Mapper 接口没有Param// 接口没有任何注解 ListUser selectByIds(ListInteger ids);此时 MyBatis 内部会把参数封装成{list: [1,2,3]}所以 XML 中必须写collectionlist。情况二Mapper 接口加了Param// 接口显式命名 ListUser selectByIds(Param(ids) ListInteger ids);此时 MyBatis 内部封装成{ids: [1,2,3]}XML 中就必须写collectionids。如果还写collectionlist反而会报错BindingException: Parameter list not found. Available parameters are [ids, param1] 最佳实践建议永远使用Param显式命名不要依赖默认别名。理由可读性collectionids比collectionlist语义清晰得多安全性重构参数名时 IDE 能自动同步默认别名不会一致性无论单参数还是多参数写法统一避坑团队成员不一定记得list/array这个隐式约定⚠️ 补充一个常见陷阱如果你用的是MyBatis-Plus或者某些框架自动生成的 Mapper它们可能已经在接口上加了Param但你没注意到。这时候照抄网上的collectionlist就会报错。始终以你自己接口上的注解为准。3. 别名配置让 parameterType 更简洁MyBatis 内置了很多别名不用写全限定名全限定名别名java.lang.Integerint/Integerjava.lang.Stringstring/Stringjava.util.HashMaphashmapjava.util.Listlistjava.util.Datedate你也可以在mybatis-config.xml里自定义别名typeAliases typeAlias typecom.example.User aliasUser/ /typeAliases然后就可以简写insert parameterTypeUser ... /insert4. 到底要不要写 parameterType结论可以省略但建议视情况写。场景建议基本类型int, String省略MyBatis 自动推断自定义对象写上可读性更好Map / List写上防止别人看不懂复杂项目统一规范要么全写要么全不写5. 常见坑坑 1parameterType 写错了也不报错!-- 参数实际是 String但写成了 int -- select parameterTypeint SELECT * FROM user WHERE name #{name} /select不会报错因为 MyBatis 只是把它当声明实际类型由传入的参数决定。但如果用了类型处理器可能会出问题。坑 2多个参数没加 Param// ❌ 错误没有 Param User selectByNameAndAge(String name, Integer age);select idselectByNameAndAge SELECT * FROM user WHERE name #{name} AND age #{age} /select报错There is no getter for property named name in class java.lang.String原因没有ParamMyBatis 会把参数封装成param1、param2要用#{param1}、#{param2}访问或者加Param。坑 3对象属性名和 #{} 里的名字不一致public class User { private String userName; // 属性叫 userName }!-- ❌ 错误 -- #{name} !-- 找不到 getter报错 -- !-- ✅ 正确 -- #{userName}一句话总结parameterType是声明这个 SQL 接收什么类型的参数基本类型和常见集合可以省略自定义对象建议写上提高可读性。多个参数不能用 parameterType要用Param注解。二、传参2-1、简单类型简单类型包括byte short int long float double char基本数据类型Byte Short Integer Long Float Double Character包装类Stringjava.util.Datejava.sql.Date类型用途特点java.util.Date通用日期时间类Java 标准库的一部分包含日期 时间信息java.sql.Date专门用于 JDBC表示 SQL 中的DATE类型只有日期时间会被清零00:00:001、基本类型或单个参数示例Student selectById(Long id);对应的 Mapper XMLselect idselectById resultTypeStudent parameterTypejava.lang.Long SELECT * FROM t_student WHERE id #{id} /select说明只有一个参数时#{}中写任意名字都可以推荐使用参数名。parameterType是可选的但写上更规范。因为Mybatis框架自身带有类型自动判断机制所以大部分情况下paramterType属性都是可以省略不写的SQL语句最终是这样的select * from t_student where id ?JDBC代码是一定要给?传值的。怎么传值ps.setXxx(第几个问号传什么值);ps.setLong(1, 1L);ps.setString(1, zhangsan);ps.setDate(1, new Date());ps.setInt(1, 100); ...mybatis底层到底调用setXxx的哪个方法取决于parameterType属性的值。【注意】parameterType的值是可以起别名的Mybatis本身自定义了许多别名具体可以查看配置文件配置_MyBatis中文网2-2、传递 Map 参数示例Test public void testInsertStudent() throws ParseException { SqlSession sqlSession SqlSessionUtils.openSession(); StudentMapper studentMapper sqlSession.getMapper(StudentMapper.class); SimpleDateFormat simpleDateFormat new SimpleDateFormat(yyyy-MM-dd); Date birth simpleDateFormat.parse(1996-09-09); MapString, Object map new HashMap(); map.put(key1, 王五); map.put(key2, 15); map.put(key3, 1.65); map.put(key4, birth); map.put(key5, 女); int count studentMapper.insertStudent(map); System.out.println(count); sqlSession.commit(); sqlSession.close(); }int insertStudent(MapString, Object map);insert idinsertStudent parameterTypemap insert into t_student (id, name, age, height, birth, sex) value (null, #{key1}, #{key2}, #{key3}, #{key4}, #{key5}) /insert【注意】Map 中的key对应#{}的名字。paramterType可以省略2-3、传递 Java 对象POJO示例int insertStudent(Student student);insert idinsertStudent parameterTypeStudent INSERT INTO t_student(name, age, gender) VALUES(#{name}, #{age}, #{gender}) /insert说明MyBatis 会自动读取student.getName()、student.getAge()。#{}中写的是属性名。parameterType可以省略parameterType可以写全类名parameterType可以写别名student因为配置了类有默认别名2-4、多个参数1、方式一mybatis自动创建的mapkey是索引value是参数名。【备注】1、此时xml中不需要写parameterType因为多个参数类型可能不一致2、此方法可读性比较差2、方式二使用Param注解推荐相当于告诉mybatis自动创建的mapkey的名字是通过Param注解指定的示例三、查询语句的返回值3-1、返回单个 POJO 对象示例Car selectById(String carNum);select idselectById resultTypecom.wsbazinga.bank.pojo.Car select id, car_num as carNum, brand, guide_price as guidePrice, produce_time as produceTime, car_type as carType from t_car where id #{id} /select3-2、返回 ListPOJO示例ListCar selectAll();select idselectAll resultTypecar select id, car_num as carNum, brand, guide_price as guidePrice, produce_time as produceTime, car_type as carType from t_car /select【注意】若是返回的结果是list但是返回参数却是pojo则会报错TooManyResultException3-3、返回 Map若是返回的数据没有合适的实体类对应的话可以采用map集合接收字段名叫key字段值叫value。示例MapString, Object selectReMap(Long id);【注意】此时返回的结果要是单条数据3-4、返回 ListMap若是返回的数据没有合适的实体类对应的话可以采用map集合接收。示例ListMapString, Object selectReMap2(String brand);【注意】此时返回的结果要是多条数据3-5、返回值类型是MapString, Map这种情况表示最外层是一个Mapkey 是某个字段的值例如id、name等value 是一条记录用MapString, Object表示字段名和字段值【目的】方便查找数据MyBatis 提供了MapKey注解让你将某一列作为 Map 的 key。示例3-6、使用 resultMap复杂映射查询结果的列名和java对象的属性名对应不上怎么办方式1、起别名方式2使用resultMap进行结果映射Car selectReResMap(Long id);方式3是否开启驼峰命名自动映射前提java命名规范首字母小写后面每个单词首字母大写。sql命名规范全部小写单词之间下划线分割。实体类中的属性名数据库表的列名carNumcar_numcarTypecar_type开启该功能在mybatis-config.xml中配置settings setting namemapUnderscoreToCamelCase valuetrue/ /settings3-7、MyBatis 查询总记录条数当然可以MyBatis 查询总记录条数是非常常见的需求通常用于分页PageHelper、手动分页数据分析如统计注册用户数量判断是否存在数据返回条数是否大于03-7-1、基本查询使用COUNT(*)1. Mapper 接口定义int countAllStudents();2. 对应 Mapper XMLselect idcountAllStudents resultTypeint SELECT COUNT(*) FROM t_student /select3. 使用方式int count studentMapper.countAllStudents(); System.out.println(总人数 count);2-7-2、带条件查询总数如查询年龄大于20的学生数量1. Mapper 方法int countByAge(int age);2. Mapper XMLselect idcountByAge resultTypeint parameterTypeint SELECT COUNT(*) FROM t_student WHERE age gt; #{age} /select注意XML 中要写成gt;2-7-3、返回类型可以是int或longSQL推荐返回类型COUNT(*)long更保险尤其是大表业务能确保不会超过 21 亿intLong count studentMapper.countSomething();四、MyBatis#{}参数绑定1、语法本质#{...}内部使用的是OGNL 表达式Object-Graph Navigation Language支持对象属性导航。写法OGNL 层级解析逻辑#{name}1 层参数根对象.getName()#{user.name}2 层参数根对象.getUser().getName()#{user.department.name}3 层参数根对象.getUser().getDepartment().getName()⚠️核心原则#{...}总是从参数根对象开始解析点号.表示进入下一层对象取属性。2、三种写法详解2-1.#{name}— 单层取值含义直接从传入的参数对象中取name属性。适用场景传入的是单个简单对象Java Bean、基本类型、String 等。// Mapper 接口 User findByName(User user); // User 有 name 属性 // XML select idfindByName resultTypeUser SELECT * FROM user WHERE name #{name} /select解析过程参数根对象 user 实例 → user.getName()2-2.#{user.name}— 两层取值含义先从参数根对象中取出名为user的属性/键再取该对象的name属性。适用场景多参数被 MyBatis 自动包装成 Map传入的是 Map且 Map 中有 key 为user的对象示例 A多参数 Param// Mapper 接口 User find(Param(user) User user, Param(age) int age); // XML select idfind resultTypeUser SELECT * FROM user WHERE name #{user.name} AND age #{age} /select解析过程参数根对象 Map{user: User实例, age: 25} → map.get(user) → User实例 → User实例.getName()示例 B传入 MapMapString, Object params new HashMap(); params.put(user, user); // XML 中 #{user.name} // 从 params 中取出 user再取 name2-3.#{user.department.name}— 多层嵌套取值含义逐级导航 — 取user→ 取user.department→ 取department.name。适用场景参数对象内部有嵌套对象需要用到深层属性。// 实体类 public class User { private String name; private Department department; // 嵌套对象 } public class Department { private String deptName; } // Mapper 接口 User findByDeptName(Param(user) User user); // XML select idfindByDeptName resultTypeUser SELECT * FROM user WHERE dept_name #{user.department.deptName} /select解析过程参数根对象 Map{user: User实例} → map.get(user) → User实例 → User实例.getDepartment() → Department实例 → Department实例.getDeptName()3、对比速查表写法参数类型是否正确说明#{name}单个 User 对象✅user.getName()#{user.name}单个 User 对象❌User 类没有user属性#{name}Param(user) User user❌Map 中没有name这个 key#{user.name}Param(user) User user✅map.get(user).getName()#{user.department.name}Param(user) User user✅多级嵌套导航#{user.name}MapString,Object含 keyuser✅从 Map 中取出 user 对象4、Param注解的作用一旦加了Param(user)哪怕只有一个参数MyBatis 也会把它包装成 Map这时候就必须用#{user.name}来取值。情况MyBatis 内部处理XML 中如何取值单参数无Param直接传递该对象本身#{name}直接取对象属性单参数有Param(user)包装成Map{user: 对象}#{user.name}先从 Map 取 key再取属性代码对比// Mapper 接口 User findByName(User user); // 情况1无 Param User findByName(Param(user) User user); // 情况2有 Param情况 1无 Paramselect idfindByName resultTypeUser SELECT * FROM user WHERE name #{name} !-- ✅ 正确直接访问 user 对象的 name 属性 -- /select情况 2有 Param(user)select idfindByName resultTypeUser !-- ❌ 报错Map 中没有 name 这个 key -- SELECT * FROM user WHERE name #{name} !-- ✅ 正确先从 Map 取出 user再取 name 属性 -- SELECT * FROM user WHERE name #{user.name} /select为什么会这样MyBatis 的源码逻辑很简单只要检测到Param注解无论参数个数一律走 Map 包装逻辑。// 伪代码示意 if (有 Param 注解) { // 包装成 Map map.put(user, user参数值); // Param(user) 指定的名字作为 key 参数根对象 map; } else if (参数个数 1) { // 直接传递 参数根对象 user参数值; } else { // 多参数且无 Param默认用 param1, param2... 参数根对象 map; }记忆技巧Param是 Map 的开关— 一旦打开所有参数都进 Map取值时必须带上Param指定的 key 名。场景建议单参数简单对象不加Param直接用#{name}单参数但想明确命名 / 后续可能扩展加Param用#{user.name}多参数必须加Param用#{user.name}