假设我们有一个简单的 users
表:
CREATE TABLE users (id INT PRIMARY KEY AUTO_INCREMENT,username VARCHAR(50),email VARCHAR(100),age INT,status VARCHAR(20)
);
示例数据:
INSERT INTO users (username, email, age, status) VALUES
('alice', 'alice@example.com', 30, 'active'),
('bob', 'bob@example.com', 22, 'inactive'),
('charlie', 'charlie@example.com', 25, 'active'),
('david', 'david@example.com', 35, 'inactive'),
('eve', 'eve@example.com', 29, 'active');
<if>
:条件判断
场景
用于在存在某个条件时动态添加查询条件。通常用于处理用户输入可选查询参数的情况,如搜索过滤器。
示例
<select id="findUsers" parameterType="map" resultType="User">SELECT * FROM users<where><if test="username != null">username = #{username}</if><if test="ageStart != null">AND age >= #{ageStart}</if><if test="ageEnd != null">AND age <= #{ageEnd}</if><if test="status != null">AND status = #{status}</if></where>
</select>
使用示例
-
传入参数:
{"username": null, "ageStart": 25, "ageEnd": 35, "status": "active"}
-
实际 SQL:
SELECT * FROM users WHERE age >= 25 AND age <= 35 AND status = 'active';
-
返回结果:
id username email age status 1 alice alice@example.com 30 active 3 charlie charlie@example.com 25 active 5 eve eve@example.com 29 active
传参错误的情况
- 传入:
{}
- 结果: SQL 语句变成
SELECT * FROM users
,返回所有记录,因为没有任何过滤条件。
<choose>
, <when>
, <otherwise>
这些元素在 MyBatis 中用于实现条件逻辑,类似于编程语言中的 switch
语句,但更适合用于构建 SQL 查询。
<choose>
: 类似于一个开始块,用来包含多个<when>
子句和一个可选的<otherwise>
子句。<when>
: 定义各个条件分支,每个<when>
标签都有一个test
属性,如果test
性成立,就使用该分支。<otherwise>
: 在所有<when>
都不满足条件时执行的默认逻辑。
场景
假设我们要根据不同参数执行不同的查询条件。比如用户可以选择用 username
、email
或默认使用 status
来检索 users
表中的数据。
示例
在 SQL 查询中设定 WHERE 1=1
开头可以方便拼接后续条件并确保查询语法的合法性。以下是如何在查询中使用这些标签:
<select id="findUsersDynamically" parameterType="map" resultType="User">SELECT * FROM usersWHERE 1=1<choose><when test="username != null">AND username = #{username}</when><when test="email != null">AND email = #{email}</when><otherwise>AND status = 'inactive'</otherwise></choose>
</select>
使用示例
传入参数
-
参数:
{"username": "alice"}
-
实际 SQL:
SELECT * FROM users WHERE 1=1 AND username = 'alice';
-
返回结果:
id username email age status 1 alice alice@example.com 30 active
传入参数
-
参数:
{"email": "bob@example.com"}
-
实际 SQL:
SELECT * FROM users WHERE 1=1 AND email = 'bob@example.com';
-
返回结果:
id username email age status 2 bob bob@example.com 22 inactive
传入参数(两者皆为空)
-
参数:
{"username": null, "email": null}
-
实际 SQL:
SELECT * FROM users WHERE 1=1 AND status = 'inactive';
-
返回结果:
id username email age status 2 bob bob@example.com 22 inactive 4 david david@example.com 35 inactive
适用场景
- 当查询条件存在几个互斥的选项时,用
<choose>
和<when>
可以确保只选用条件中的一个。 - 可以在不存在指定的参数时提供一个默认的执行逻辑通过
<otherwise>
,这可以用来引导默认的查询策略或者提高用户体验。
传参错误的处理
- 问题: 如果传入参数在
<when>
中无法满足任何条件。 - 解决: 使用
<otherwise>
提供安全默认值或行为,确保不因缺乏可用参数而导致无效查询。
通过合理使用 <choose>
, <when>
, <otherwise>
,能有效减少 SQL 查询的复杂性,并且实现灵活的业务数据提取逻辑。这个模型特别适合于多个可选参数逻辑中仅需执行一个条件的查询情况。
<trim>
, <where>
, <set>
<trim>
用于灵活地拼接 SQL,避免多余的操作符。
- 用途: 可自定义前缀和后缀,去或者添加特定的字符。
示例:
<select id="searchUsers" parameterType="map" resultType="User">SELECT * FROM users<trim prefix="WHERE" prefixOverrides="AND |OR "><if test="username != null">AND username = #{username}</if><if test="email != null">OR email = #{email}</if></trim>
</select>
<where>
自动处理起始的 WHERE
,并去除不需要的 AND
或 OR
。
使用示例在 <if>
的示例中已有展示。
<set>
- 用途:生成
UPDATE
语句的SET
子句,确保最后不会多了逗号。
示例:
<update id="updateUser" parameterType="User">UPDATE users<set><if test="email != null">email = #{email},</if><if test="status != null">status = #{status},</if></set>WHERE id = #{id}
</update>
使用示例
-
传入参数:
{"id": 1, "email": "newalice@example.com", "status": "inactive"}
-
实际 SQL:
UPDATE users SET email = 'newalice@example.com', status = 'inactive' WHERE id = 1;
-
操作结果:
users
表中 ID 为 1 的记录email
和status
字段被更新。
传参错误的情况
- 传入:
{"id": 1}
- 结果: SQL 未造成任何更改,因为没有字段需要更新。
<foreach>
场景
批量处理,如多个 ID 查询或者批量插入。
示例
查询多个 ID:
<select id="findUsersByIds" parameterType="list" resultType="User">SELECT * FROM usersWHERE id IN<foreach collection="list" item="id" open="(" separator="," close=")">#{id}</foreach>
</select>
使用示例
-
传入参数:
[1, 3, 5]
-
实际 SQL:
SELECT * FROM users WHERE id IN (1, 3, 5);
-
返回结果:
id username email age status 1 alice alice@example.com 30 active 3 charlie charlie@example.com 25 active 5 eve eve@example.com 29 active
传参错误的情况
- 传入:
[]
- 结果: 生成
WHERE id IN ()
,某些数据库可能会产生语法错误,需要在应用层进行空集合检查。
批量插入
<insert id="insertUsers" parameterType="list">INSERT INTO users (username, email, age, status)VALUES<foreach collection="list" item="user" separator=",">(#{user.username}, #{user.email}, #{user.age}, #{user.status})</foreach>
</insert>
- 使用场景: 当需要批量添加用户时,非常高效。
<foreach>详细解释
遍历列表
在 MyBatis 中,列表是一个常见的集合类型,通常用于处理一组对象,比如用户传入多个 ID 的场景。
示例:遍历列表
场景: 查询给定 ID 列表的用户。
<select id="findUsersByIds" parameterType="list" resultType="User">SELECT * FROM usersWHERE id IN<foreach collection="list" item="id" open="(" separator="," close=")">#{id}</foreach>
</select>
- collection=“list”: 指定要迭代的参数类型为
List
。 - item=“id”: 在每次迭代中,当前元素被引用为
id
。 - open=“(”: 指定 SQL 语句中
IN
子句的左括号。 - separator=“,”: 每个 ID 之间用逗号分隔。
- close=“)”: 指定 SQL 语句中
IN
子句的右括号。
使用示例
-
传入参数:
[1, 3, 5]
-
实际生成的 SQL:
SELECT * FROM users WHERE id IN (1, 3, 5);
遍历数组
数组与列表的处理方式类似,主要区别在于传递的参数类型为数组。
示例:遍历数组
场景: 查询给定用户名的记录。
<select id="findUsersByUsernames" parameterType="array" resultType="User">SELECT * FROM usersWHERE username IN<foreach collection="array" item="username" open="(" separator="," close=")">#{username}</foreach>
</select>
- collection=“array”: 指定要迭代的参数类型为数组。
- item=“username”: 在每次迭代中,当前元素被引用为
username
。
使用示例
-
传入参数:
["alice", "bob"]
-
实际生成的 SQL:
SELECT * FROM users WHERE username IN ('alice', 'bob');
遍历 Map
Map
在 MyBatis 中可以用于更灵活的键值对迭代,尤其是在需要处理键值对的动态 SQL 场景中。
示例:遍历 Map
场景: 更新用户信息,同时更新多个字段。
<update id="updateUserInfo" parameterType="map">UPDATE users<set><foreach collection="userMap" item="value" index="key" separator=",">${key} = #{value}</foreach></set>WHERE id = #{userId}
</update>
- collection=“userMap”: 指定要迭代的参数类型为
Map
。 - item=“value”: 在每次迭代中,表示当前键值对的值。
- index=“key”: 在每次迭代中,表示当前键值对的键。
使用注意中的 ${key}
表示不进行参数绑定直接将其作为 SQL 片段插入,需确保其安全性以防止 SQL 注入。
使用示例
-
传入参数:
Map<String, Object> userMap = new HashMap<>(); userMap.put("email", "newalice@example.com"); userMap.put("age", 31); Map<String, Object> params = new HashMap<>(); params.put("userMap", userMap); params.put("userId", 1);
-
实际生成的 SQL:
UPDATE users SET email = 'newalice@example.com', age = 31 WHERE id = 1;
注意事项
- 安全性: 使用
${}
时,务必确保数据的安全性以防 SQL 注入风险。 - 空集合处理: 对于可能为空的集合,在使用前可以进行检查,或者在SQL中做适当的处理。
- 性能: 如果集合过大,生成的 SQL 可能过长,需要在实际应用中权衡使用场景,可能要分批处理。