MyBatis 中 OGNL 的使用:从入门到实战

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
逻辑运算 andornot name != null and age > 18
字符串运算 +(拼接) 'Hello' + name
三元运算 ? : sex == 1 ? '男' : '女'
成员运算 innot 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 &gt;= #{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 转义
< &lt;
> &gt;
& &amp;

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 &gt;= #{minAge}
        </if>
        <if test="maxAge != null">
            AND age &lt;= #{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,提升开发效率。

相关推荐
土司大王16 小时前
LeetCode hot100——35.搜索插入位置:Java 二分模板、左闭右开区间与插入点分析
java·算法·leetcode
土司大王16 小时前
LeetCode hot100——34.在排序数组中查找元素的第一个和最后一个位置:Java 二分模板、边界分析
java·算法·leetcode
金金计较.17 小时前
Go语言-3
java·开发语言·golang
Wang's Blog18 小时前
Java框架快速入门: Spring Security+OAuth2之元注解简化权限表达式
java·数据库·spring
xifangge202518 小时前
AGENTS.md 怎么写?涵盖 Java、Python、Vue、Go 的 8 套开箱即用模板
java·vue.js·python
Wang's Blog18 小时前
Java框架快速入门: Spring Security+OAuth2之云服务集成与多因子认证设计
java·开发语言·spring
liangsheng_g19 小时前
SpringAOP拦截器链递归与事务钩子补偿源码实战
java·spring
SL_staff19 小时前
制造业私有化文档平台的技术实践:从知识孤岛到可追溯知识资产
java·spring·开源
SL_staff20 小时前
3天上线OKR系统:一名HR与1名工程师如何用JVS完成全栈交付
java·低代码·开源
wzdark21 小时前
基于链表的内存池设计与内存复用机制4
java·数据结构·链表