mybatis 对 mccmncs 字符串处理 :310_-1,311_-1,312_-1,313_-1
mybatis
<if test="params.mccmncs != null and params.mccmncs != ''">
AND (
<foreach collection="params.mccmncs.split(',')" item="mccmnc" separator=" OR ">
<if test="mccmnc != null and mccmnc != ''">
<choose>
<!-- 如果当前项是 -1,只匹配 country -->
<when test="mccmnc.split('_')[1] == '-1'">
rbi.country = ${mccmnc.split('_')[0]}
</when>
<!-- 如果当前项不是 -1,需要检查同一个 country 是否存在 -1 -->
<otherwise>
<choose>
<!-- 检查是否存在同 country 的 -1 项 -->
<when test="params.mccmncs.contains(mccmnc.split('_')[0] + '_-1')">
<!-- 如果存在 -1,跳过此条件(通过 1=0 实现) -->
1 = 0
</when>
<!-- 如果不存在 -1,正常匹配 -->
<otherwise>
(rbi.country = ${mccmnc.split('_')[0]} AND rbi.operator = ${mccmnc.split('_')[1]})
</otherwise>
</choose>
</otherwise>
</choose>
</if>
</foreach>
)
</if>
这是 **MyBatis 动态 SQL** 的语法,它使用 **OGNL (Object-Graph Navigation Language)** 表达式语言。让我详细解析一下:
OGNL 表达式语言
MyBatis 的动态 SQL 中的 `test` 属性使用的是 OGNL 表达式,它支持很多 Java 对象的方法和操作。
1. **支持的字符串方法**
OGNL 可以调用 Java String 类的所有方法:
```xml
XML
<!-- 常用的字符串方法 -->
<if test="params.mccmncs != null and params.mccmncs != ''">
<!-- split() - 分割字符串 -->
<foreach collection="params.mccmncs.split(',')" item="item">
<!-- contains() - 包含检查 -->
<when test="params.mccmncs.contains('_-1')">
<!-- startsWith() - 开头检查 -->
<when test="item.startsWith('460')">
<!-- endsWith() - 结尾检查 -->
<when test="item.endsWith('-1')">
<!-- length() - 长度 -->
<when test="item.length() > 5">
<!-- substring() - 截取 -->
<when test="item.substring(0, 3) == '460'">
<!-- trim() - 去除空格 -->
<when test="item.trim() != ''">
<!-- toUpperCase() / toLowerCase() - 大小写转换 -->
<when test="item.toUpperCase() == 'ABC'">
<!-- replace() - 替换 -->
<when test="item.replace('_', '-') == '460-0'">
<!-- indexOf() - 查找位置 -->
<when test="item.indexOf('_') > 0">
<!-- matches() - 正则匹配 -->
<when test="item.matches('\\d+_-1')">
</if>
2. **支持的集合方法**
```
XML
```xml
<!-- List/Collection 方法 -->
<if test="list != null and list.size() > 0">
<if test="list.isEmpty()">
<if test="list.contains(item)">
<!-- Map 方法 -->
<if test="map.containsKey('key')">
<if test="map.get('key') != null">
3. **支持的运算符**
XML
<!-- 比较运算符 -->
<when test="value == 1"> <!-- 等于 -->
<when test="value != 1"> <!-- 不等于 -->
<when test="value > 1"> <!-- 大于 -->
<when test="value >= 1"> <!-- 大于等于 -->
<when test="value < 1"> <!-- 小于 (需要转义) -->
<when test="value <= 1"> <!-- 小于等于 (需要转义) -->
<!-- 逻辑运算符 -->
<when test="a and b"> <!-- 与 -->
<when test="a or b"> <!-- 或 -->
<when test="!a"> <!-- 非 -->
<when test="a || b"> <!-- 或 (另一种写法) -->
<when test="a && b"> <!-- 与 (另一种写法,需要转义) -->
<!-- 空值检查 -->
<when test="value == null">
<when test="value != null">
<!-- 字符串检查 -->
<when test="str == ''">
<when test="str != ''">
4. **支持的特殊操作**
``
XML
<!-- 三元运算符 -->
<when test="value > 0 ? 'positive' : 'negative'">
<!-- 访问对象属性 -->
<when test="user.name == 'admin'">
<when test="user.age > 18">
<!-- 访问数组/列表元素 -->
<when test="array[0] == 'first'">
<when test="list.get(0) == 'first'">
<!-- 调用静态方法 (需要完整类名) -->
<when test="@java.lang.Math@max(a, b) > 10">
<!-- 创建新对象 -->
<when test="new java.util.Date().getTime() > 0">
```
6. **注意事项和限制**
```xml
XML
<!-- ❌ 不支持:复杂的 Java 语法 -->
<!-- 不能使用 for 循环、while 循环等 -->
<!-- 不能定义变量 -->
<!-- 不能使用 lambda 表达式 -->
<!-- ⚠️ XML 特殊字符需要转义 -->
<when test="value < 10"> <!-- < 需要写成 < -->
<when test="value > 10"> <!-- > 需要写成 > -->
<when test="a && b"> <!-- && 需要写成 && -->
<!-- ✅ 推荐:简单清晰的表达式 -->
<!-- 复杂逻辑建议在 Java 代码中预处理 -->
```
7. **常见陷阱**
XML
<!-- ❌ 错误:数组越界 -->
<when test="mccmnc.split('_')[1] == '-1'">
<!-- 如果 mccmnc 是 "460" (没有下划线),会抛出 ArrayIndexOutOfBoundsException -->
<!-- ✅ 正确:先检查长度 -->
<when test="mccmnc.split('_').length > 1 and mccmnc.split('_')[1] == '-1'">
<!-- ❌ 错误:多次调用 split -->
<when test="mccmnc.split('_')[0] == '460' and mccmnc.split('_')[1] == '-1'">
<!-- 每次都会重新分割字符串,效率低 -->
<!-- ✅ 建议:在 Java 中预处理 -->
<!-- 复杂的字符串操作建议在 Service 层完成 -->
```
总结
-
**语法**:OGNL (Object-Graph Navigation Language)
-
**支持**:Java 对象的大部分方法(String、List、Map 等)
-
**限制**:不支持复杂的 Java 语法结构
-
**建议**:简单逻辑用 OGNL,复杂逻辑用 Java 预处理
你的代码是**可以运行的**,但需要注意:
-
确保 `mccmnc.split('_')` 总是返回至少 2 个元素
-
多次调用 `split()` 会影响性能
-
对于复杂的业务逻辑,建议在 Java 层面预处理数据