Mapper–>XML 的参数传递
基本概念
在 MyBatis 中,Mapper 接口的方法通过参数向对应的 Mapper XML 中的 SQL 映射语句传参。XML 中通过 #{}
或 ${}
取出参数:
#{}
是预编译方式,安全(会防止 SQL 注入)。${}
是字符串拼接方式,不安全(容易被注入,一般用于拼接表名、字段名等动态结构)。
常见的传参方式
单个参数
使用场景:方法只有一个参数,且时基本数据类型或者字符串。
Mapper 接口
User selectUserById(int id);
Mapper XML
<select id="selectUserById" resultType="User">
SELECT * FROM user WHERE id = #{id}
</select>
多个参数
使用场景:方法有多个参数,MyBatis 默认无法识别参数名,需使用 @Param
指定别名。
Mapper 接口
User selectUser(@Param("id") int id, @Param("name") String name);
Mapper XML
<select id="selectUser" resultType="User">
SELECT * FROM user WHERE id = #{id} AND name = #{name}
</select>
Java Bean(实体类)
使用场景:传参对象是一个 JavaBean(如 POJO)。
实体类
public class User {
private Integer id;
private String name;
}
Mapper 接口
User selectUserByBean(User user);
Mapper XML
<select id="selectUserByBean" resultType="User">
SELECT * FROM user WHERE id = #{id} AND name = #{name}
</select>
🔍 注意:MyBatis 会自动根据 JavaBean 的字段名匹配。
@Param+Bean 混合传参
使用场景: 需要传一个实体对象 + 其他参数,需要根据 @Param
注解指定参数。
Mapper 接口
List<User> selectUsers(@Param("user") User user, @Param("age") int age);
Mapper XML
<select id="selectUsers" resultType="User">
SELECT * FROM user
WHERE name = #{user.name}
AND age > #{age}
</select>
返回结果映射实体类
基本概念
在 MyBatis 中,SQL 查询结果如何映射到 Java 实体类(POJO) 是关键能力之一。MyBatis 提供了灵活的方式来映射返回结果到实体类,核心方式包括:
- 自动映射(resultType)
- 自定义映射(resultMap)
常见映射方式
自动映射(resultType)
当数据库列名和 Java 实体类的属性名一致或符合驼峰命名规则时,MyBatis 会自动映射字段。
实体类
public class User {
private Integer id;
private String userName;
private Integer age;
}
Mapper XML
<select id="selectUserById" resultType="com.example.User">
SELECT id, user_name, age FROM user WHERE id = #{id}
</select>
这里
user_name
能自动映射到userName
,因为 MyBatis 默认支持下划线转驼峰(如果开启了该配置)。
自定义映射(resultMap)
实体类
public class User {
private Integer id;
private String userName;
private Integer age;
}
Mapper XML
<resultMap id="UserResultMap" type="com.example.User">
<id property="id" column="user_id"/>
<result property="username" column="user_name"/>
<result property="age" column="age"/>
</resultMap>
<select id="selectUserById" resultMap="UserResultMap">
SELECT user_id, user_name, age FROM user WHERE user_id = #{id}
</select>
id
表示主键字段,result
表示普通字段。
这里将,user_id
的结果映射到 id
。
复杂结构:嵌套对象或集合映射
实体类
public class Address {
private Integer id;
private String city;
private String street;
}
public class User {
private Integer id;
private String userName;
private Address address;
}
Mapper XML
<resultMap id="UserWithAddressMap" type="com.example.User">
<id property="id" column="id"/>
<result property="userName" column="user_name"/>
<association property="address" javaType="com.example.Address">
<id property="id" column="address_id"/>
<result property="city" column="city"/>
<result property="street" column="street"/>
</association>
</resultMap>
<select id="selectUserWithAddress" resultMap="UserWithAddressMap">
SELECT
u.id, u.user_name,
a.id AS address_id, a.city, a.street
FROM user u
JOIN address a ON u.address_id = a.id
WHERE u.id = #{id}
</select>
一对多嵌套集合
实体类
一个部门下,对应多个员工。
public class Department {
private Integer id;
private String name;
private List<Employee> employees;
}
public class Employee {
private Integer id;
private String name;
}
Mapper XML
<resultMap id="DeptWithEmployees" type="com.example.Department">
<id property="id" column="dept_id"/>
<result property="name" column="dept_name"/>
<collection property="employees" ofType="com.example.Employee">
<id property="id" column="emp_id"/>
<result property="name" column="emp_name"/>
</collection>
</resultMap>
<select id="selectDepartmentWithEmployees" resultMap="DeptWithEmployees">
SELECT
d.id AS dept_id, d.name AS dept_name,
e.id AS emp_id, e.name AS emp_name
FROM
department d
LEFT JOIN
employee e ON d.id = e.dept_id
WHERE d.id = #{id}
</select>
映射流程
Mybatis 内部维护一个缓存(MAP),用来存储已经创建的主对象(Department
),key 是主键值(dept_id
)。
- ResultSet 逐行遍历
- 读取主对象主键(
dept_id
) - 判断缓存中是否存在 Department(
id = dept_id
)- 否:新建 Department 示例,并缓存。
- 是:取出已有的实例
- 映射主对象属性(
dept_name
) - 创建 Employee 对象(
emp_id
,emp_name
) - 将 Employee 对象添加到 Department.employees 员工列表。
- 下一行,重复操作。
+------------------------------------+
| ResultSet 逐行遍历 |
+-------------------+----------------
|
v
+------------------------------------+
| 读取主对象主键 (dept_id) |
+-------------------+----------------
|
v
+----------------------------------------------+
| 判断缓存中是否存在 Department(id = dept_id) |
+-------------------+--------------------------+
| 是 | 否
v v
+---------------------+ +-----------------------------+
| 取出已有的实例 | | 新建 Department 实例,并缓存 |
+----------+----------+ +-------------+---------------+
\ /
\ /
v v
+-------------------------------+
| 映射 Employee 对象 |
| (emp_id, emp_name) |
+---------------+---------------+
|
v
+---------------------------------------------+
| 将 Employee 添加到 Department.employees 列表 |
+------------------+--------------------------+
|
v
+-----------------------------+
| 下一行,重复上述过程 |
+-----------------------------+