在MyBatis中,可以通过使用一些特定的标签(if、choose...)以及其他动态SQL功能来实现条件判断。
这使得SQL查询可以根据不同的条件动态生成,从而提高查询的灵活性和可维护性。
本文以订单列表简单查询为例, 对mybatis条件判断及动态sql进行简单拓展。
- CREATE TABLE order_table (
- `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '订单序号',
- `order_type` varchar(255) NULL COMMENT '订单类型',
- `status` varchar(255) NULL COMMENT '订单状态',
- `customer_id` bigint(20) NULL COMMENT '所属客户id',
- `quantity` double NULL COMMENT '数量',
- `address` varchar(500) NULL COMMENT '收货地',
- PRIMARY KEY (`id`)
- ) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_bin COMMENT = '订单表';
以if标签为例,if标签类似于 Java 中的 if 语句, 是mybatis中最常用的判断标签。
- 语法
- <if test="判断条件">
- SQL语句
- if>
- 简单使用
- 判断数值:
- <if test="id != null ">
- and id = #{id,jdbcType=BIGINT}
- if>
- 判断字符:
- // 判断是否为空
- <if test="orderType != null and orderType.trim().length() > 0">
- sql语句
- if>
- // 判断是否包含某个字符
- <if test="orderType != null and orderType eq 'customer'">
- sql语句
- if>
- 判断集合:
- <if test="idSet != null and idSet.size > 0">
- and id in
-
"idSet" item="item" separator="," open="(" close=")"> - #{item}
-
- if>
- // 判空
- <if test="@java.util.Objects@nonNull(customerId)">
- sql语句
- if>
- <if test="@org.apache.commons.lang3.StringUtils@isNotBlank(status)">
- sql语句
- if>
- // 判断是否相等
- <if test="@java.util.Objects@equals(1,flag)">
- sql语句
- if>
- <if test='@org.apache.commons.lang3.StringUtils@equals("customer",status)' >
- sql语句
- if>
- // 判断集合是否为空
- <if test="@org.apache.commons.collections.CollectionUtils@isNotEmpty(idSet)">
- and id in
-
"idSet" item="item" open="(" close=")" separator=","> - #{item}
-
- if>
- 同理
- // 判断两个字段同时不为空
- <if test="@org.apache.commons.lang3.ObjectUtils@allNotNull(flag1,flag2)">
- sql语句
- if>
- // 判断集合中只要包含某一个字段
- <if test="@org.apache.commons.collections.CollectionUtils@containsAny(flagList, "1")">
- sql语句
- if>
- 等等...
同理, 别的标签也适用;
列表查询中,有时需要对某些字段做特殊的处理查询, 例: 对数值字段进行特殊处理查询(>、<、>=、<=、!=), 对某些文本字段的查询做特殊处理..., 我们可以巧用占位符来实现这些处理。
在MyBatis中,# 和 $ 是两种不同的占位符,用于在SQL语句中插入参数。
占位符:用于安全的参数绑定和转义,防止SQL注入,适用于大多数情况。
$ 占位符:用于直接文本替换,不进行转义,适用于动态SQL片段或表名等非用户输入的值,但要特别注意SQL注入风险。
通过合理使用这两种占位符,可以在确保安全的前提下实现灵活的SQL查询。
新建枚举类(OperationFlagEnum), 事先与前端规定操作符的传递;
操作符枚举
- /**
- * 操作标识枚举
- *
- * @author alin
- * @date 2024-06-11
- */
- @Getter
- @AllArgsConstructor
- public enum OperationFlagEnum {
-
- EQUAL("EQUAL", "="),
- NOT_EQUAL("NOT_EQUAL", "!="),
- LARGER("LARGER", ">"),
- LESS("LESS", "<"),
- LARGER_OR_EQUAL("LARGER_OR_EQUAL", ">="),
- LESS_OR_EQUAL("LESS_OR_EQUAL", "<=");
-
- private String code;
- private String value;
-
- private static final Map
VALUE_MAP = new HashMap<>(values().length); -
- static {
- Arrays.stream(OperationFlagEnum.values()).forEach(c -> VALUE_MAP.put(c.getCode(), c));
- }
-
- public static OperationFlagEnum getByCode(String code) {
- return StringUtils.isBlank(code) ? null : VALUE_MAP.get(code);
- }
-
- public static String getValueByCodeDefault(String code) {
- OperationFlagEnum flagEnum = null;
- if(StringUtils.isNotEmpty(code)){
- flagEnum = getByCode(code);
- }
- return flagEnum == null ? EQUAL.getValue(): flagEnum.value;
- }
-
- }
mapper文件中的动态sql
- <if test="quantity != null">
- and quantity
- ${@com.alin.common.enums.OperationFlagEnum@getValueByCodeDefault(quantityFlag)}
- ${quantity}
- if>
若文本字段中含有某些特殊字符, 则对这个字段进行特殊处理, 例: 若某些字段值中间含有空格(或别的字符), 则对此字段用空格切割后进行范围查询;
新建mapper工具类: MapperUtils, 用于特殊处理;
- /**
- * @author alin
- * @date 2024-06-11
- */
- public class Mapperutils {
- private static final String SPACE = " ";
- public static final String EQUALS = " = #{${propertyField},jdbcType=VARCHAR}";
-
- public static final String IN = " in ('${propertyField}')";
-
- /**
- * 获取通用字符串查询模板
- *
- * @param propertyField model字段属性
- * @param value 对应值
- * @return
- */
- public static String getQuery(String propertyField, String value) {
- if (StringUtils.contains(value.trim(), SPACE)) {
- // sql注入过滤
- sqlInject(value);
- return replace(IN, StringUtils.join(StringUtils.split(value, SPACE), "','"));
- } else if(...) {
- .....
- }
- //sql注入过滤
- sqlInject(value);
- //等值查询
- return replace(EQUALS, propertyField);
- }
-
- /**
- * 替代
- *
- * @param type
- * @param propertyField
- * @return
- */
- public static String replace(String type, String propertyField) {
- Map
params = Maps.newHashMap(); - params.put("propertyField", propertyField);
- return new StrSubstitutor(params).replace(type);
- }
-
- /**
- * SQL注入过滤
- *
- * @param str 待验证的字符串
- * @throws
- */
- public static void sqlInject(String str) {
- if (StringUtils.isBlank(str)) {
- return;
- }
- //去掉'|"|;|\字符
- str = StringUtils.replace(str, "'", "");
- str = StringUtils.replace(str, "\"", "");
- str = StringUtils.replace(str, ";", "");
- str = StringUtils.replace(str, "\\", "");
-
- //转换成小写
- str = str.toLowerCase();
-
- //非法字符
- String[] keywords = {"master", "truncate", "insert", "select", "delete", "update", "declare", "alert", "create", "drop"};
-
- //判断是否包含非法字符
- for (String keyword : keywords) {
- if (str.contains(keyword)) {
- throw new RuntimeException("查询输入条件存在非法字符!");
- }
- }
- }
- }
mapper文件中的动态sql
- <if test="@org.apache.commons.lang3.StringUtils@isNotBlank(address)">
- and ord.address ${@com.alin.common.utils.MapperUtils@getQuery("ord.address",address)}
- if>