1. 引言
OGNL(Object-Graph Navigation Language,对象图导航语言)是一种功能强大的表达式语言,最初用于 WebWork 框架,后来随着 Struts 2 的流行而被广泛熟知。在 MyBatis 中,OGNL 同样扮演着重要角色,它被用于动态 SQL 的表达式解析,让我们能够编写灵活、可复用的数据库查询语句。
本文将系统讲解 MyBatis 中 OGNL 的使用方法,从基础语法到高级技巧,帮助你在日常开发中熟练运用这一强大工具。
2. OGNL 基础语法
2.1 什么是 OGNL
OGNL 是一种表达式语言,它允许我们通过简单的表达式来访问和操作 Java 对象的属性、方法以及集合。在 MyBatis 中,OGNL 主要用于 <if>、<choose>、<where> 等动态 SQL 标签中的条件判断。
2.2 基本表达式
在 MyBatis 的 XML 映射文件中,OGNL 表达式通常写在 ${} 或 #{} 中,但更常见的是直接写在动态 SQL 标签的属性值里:
xml
<select id="findUsers" resultType="User">
SELECT * FROM user
<where>
<if test="name != null and name != ''">
AND name = #{name}
</if>
<if test="age != null">
AND age = #{age}
</if>
</where>
</select>
在上面的例子中,test 属性中的 name != null and name != '' 就是 OGNL 表达式。
2.3 常用运算符
OGNL 支持丰富的运算符,包括:
| 运算符类型 | 运算符 | 示例 |
|---|---|---|
| 关系运算 | ==、!=、>、<、>=、<= |
age >= 18 |
| 逻辑运算 | and、or、not |
name != null and age > 18 |
| 字符串运算 | +(拼接) |
'Hello' + name |
| 三元运算 | ? : |
sex == 1 ? '男' : '女' |
| 成员运算 | in、not in |
id in (1, 2, 3) |
3. MyBatis 中 OGNL 的典型应用场景
3.1 动态条件判断
这是 OGNL 最常用的场景,通过判断参数是否为空来动态拼接 SQL:
xml
<select id="searchUsers" resultType="User">
SELECT * FROM user
<where>
<if test="username != null and username.trim() != ''">
AND username LIKE CONCAT('%', #{username}, '%')
</if>
<if test="status != null">
AND status = #{status}
</if>
<if test="startTime != null">
AND create_time >= #{startTime}
</if>
</where>
</select>
3.2 集合与数组判断
在处理批量操作或列表查询时,经常需要判断集合是否为空:
xml
<select id="selectByIds" resultType="User">
SELECT * FROM user
<where>
<if test="ids != null and ids.size() > 0">
AND id IN
<foreach collection="ids" item="id" open="(" separator="," close=")">
#{id}
</foreach>
</if>
</where>
</select>
3.3 多条件选择
使用 <choose>、<when>、<otherwise> 实现类似 Java 中 switch-case 的逻辑:
xml
<select id="queryUser" resultType="User">
SELECT * FROM user
<where>
<choose>
<when test="id != null">
AND id = #{id}
</when>
<when test="username != null">
AND username = #{username}
</when>
<otherwise>
AND status = 1
</otherwise>
</choose>
</where>
</select>
4. OGNL 高级用法
4.1 访问对象属性
OGNL 支持链式访问对象的嵌套属性:
xml
<if test="user.address.city != null">
AND city = #{user.address.city}
</if>
4.2 调用方法
可以在表达式中调用 Java 方法:
xml
<if test="name != null and name.length() > 5">
AND name = #{name}
</if>
4.3 使用静态方法
通过 @类名@方法名 调用静态方法:
xml
<if test="@java.lang.String@isEmpty(name)">
AND name IS NULL
</if>
4.4 访问静态常量
xml
<if test="status == @com.example.constant.UserStatus@ACTIVE">
AND status = #{status}
</if>
5. 常见问题与注意事项
5.1 特殊字符转义
在 XML 中,<、>、& 等字符需要转义:
| 原始字符 | XML 转义 |
|---|---|
< |
< |
> |
> |
& |
& |
5.2 参数为 null 的判断
判断字符串是否为空时,建议同时判断 null 和空字符串:
xml
<if test="name != null and name != ''">
5.3 避免 SQL 注入
使用 #{} 进行参数占位,不要使用 ${} 拼接用户输入:
xml
<!-- 安全写法 -->
AND name = #{name}
<!-- 危险写法,存在 SQL 注入风险 -->
AND name = '${name}'
6. 实战案例:动态多条件查询
下面是一个综合运用 OGNL 的完整示例:
xml
<select id="dynamicQuery" resultType="User" parameterType="map">
SELECT * FROM user
<where>
<if test="name != null and name != ''">
AND name LIKE CONCAT('%', #{name}, '%')
</if>
<if test="minAge != null">
AND age >= #{minAge}
</if>
<if test="maxAge != null">
AND age <= #{maxAge}
</if>
<if test="deptIds != null and deptIds.size() > 0">
AND dept_id IN
<foreach collection="deptIds" item="deptId" open="(" separator="," close=")">
#{deptId}
</foreach>
</if>
<if test="status != null">
AND status = #{status}
</if>
</where>
ORDER BY create_time DESC
</select>
对应的 Mapper 接口方法:
java
public interface UserMapper {
List<User> dynamicQuery(Map<String, Object> params);
}
调用示例:
java
Map<String, Object> params = new HashMap<>();
params.put("name", "张");
params.put("minAge", 18);
params.put("maxAge", 30);
params.put("deptIds", Arrays.asList(1, 2, 3));
params.put("status", 1);
List<User> users = userMapper.dynamicQuery(params);
7. 总结
OGNL 是 MyBatis 动态 SQL 的核心支撑,掌握它能够让我们编写出更加灵活、强大的数据库操作语句。本文从基础语法、典型应用场景、高级用法到实战案例,系统介绍了 MyBatis 中 OGNL 的使用方法。
在实际开发中,建议遵循以下几点:
- 优先使用
#{}防止 SQL 注入 - 注意 XML 特殊字符的转义
- 合理使用
<where>标签自动处理多余的 AND/OR - 对于复杂的查询条件,可以使用
<choose>实现多分支逻辑
希望本文能帮助你更好地理解和使用 MyBatis 中的 OGNL,提升开发效率。