详细分析Mybatis中的动态Sql(附Demo)

目录

  • 前言
  • [1. 基本知识](#1. 基本知识)
  • [2. 注意事项](#2. 注意事项)
  • [3. 拓展](#3. 拓展)

前言

以往的Java基本知识推荐阅读:

  1. java框架 零基础从入门到精通的学习路线 附开源项目面经等(超全)
  2. 【Java项目】实战CRUD的功能整理(持续更新)
  3. Mybatis从入门到精通(全)
  4. MyBatis-plus从入门到精通(全)

1. 基本知识

在MyBatis中,动态SQL是一种强大的功能,允许在<select>, <insert>, <update>, <delete> 等标签中根据条件动态构建SQL语句

这种动态性可以简化数据库查询的复杂度,避免硬编码查询逻辑

MyBatis 提供了几种常用的动态 SQL 标签,用来处理条件判断、循环和字符串拼接等操作:

  • <if>标签:根据给定的条件动态拼接SQL
xml 复制代码
<select id="findUsers" resultType="User">
    SELECT * FROM users
    <where>
        <if test="name != null">
            AND name = #{name}
        </if>
        <if test="age != null">
            AND age = #{age}
        </if>
    </where>
</select>
  • <choose>, <when>, <otherwise>标签:类似于Java中的switch语句,允许你在多个条件中选择一个来执行
xml 复制代码
<select id="findUsersByStatus" resultType="User">
    SELECT * FROM users
    <where>
        <choose>
            <when test="status != null">
                AND status = #{status}
            </when>
            <otherwise>
                AND status = 'active'
            </otherwise>
        </choose>
    </where>
</select>
  • <foreach>标签:用于处理循环,一般用来遍历集合构建IN查询等
xml 复制代码
<select id="findUsersByIds" resultType="User">
    SELECT * FROM users
    WHERE id IN
    <foreach item="id" collection="list" open="(" separator="," close=")">
        #{id}
    </foreach>
</select>

<trim>, <where>, <set>标签:

  • <trim>:去掉拼接后多余的前后缀
  • <where>:自动在拼接后的条件前加WHERE,并去除多余的AND
  • <set>:主要用于UPDATE语句,自动去掉多余的逗号
xml 复制代码
<update id="updateUser">
    UPDATE users
    <set>
        <if test="name != null">
            name = #{name},
        </if>
        <if test="age != null">
            age = #{age},
        </if>
    </set>
    WHERE id = #{id}
</update>

2. 注意事项

使用此种方式需要注意动态SQL带来的OOM风险

在复杂动态SQL的构建中,特别是当涉及大批量数据或复杂拼接时,容易导致内存溢出(OOM)问题

尤其是在<foreach>中处理大量数据时,如果不加以控制,可能会占用大量内存

假设有一个非常大的List,例如数百万条数据

如果在MyBatis中直接使用<foreach>将整个列表插入或作为查询条件,这会导致内存问题

xml 复制代码
<select id="findLargeUsersByIds" resultType="User">
    SELECT * FROM users
    WHERE id IN
    <foreach item="id" collection="list" open="(" separator="," close=")">
        #{id}
    </foreach>
</select>

在这种情况下,MyBatis会尝试将数百万个id拼接到一个SQL语句中,导致内存占用过高,最终导致OOM

规避OOM的方法

  1. 分页处理:可以将大批量数据分段处理,例如在代码中将list切分为较小的块
java 复制代码
List<List<Integer>> partitionedIds = Lists.partition(largeIdsList, 1000);
for (List<Integer> idsBatch : partitionedIds) {
    userMapper.findLargeUsersByIds(idsBatch);
}
  1. 避免过大的IN查询:IN查询是常见的OOM来源,可以通过数据库表连接等方式优化查询。

  2. 懒加载:通过懒加载策略,避免一次性加载过多数据到内存中

3. 拓展

在Java代码中,进行参数校验是一种很好的实践,尤其是在使用动态SQL时,如果不校验传入的参数,可能会导致意外的SQL拼接,从而带来性能问题或安全隐患

  • 基本的注解校验
java 复制代码
public class User {
    @NotNull(message = "Name cannot be null")
    private String name;

    @Min(value = 18, message = "Age should not be less than 18")
    private Integer age;
}

在使用这些注解后,如果传入的参数不符合条件,就会抛出ConstraintViolationException

  • 结合Spring的@Valid注解,可以自动在控制层或服务层进行参数校验
java 复制代码
@PostMapping("/createUser")
public ResponseEntity<?> createUser(@Valid @RequestBody User user, BindingResult result) {
    if (result.hasErrors()) {
        return new ResponseEntity<>(result.getAllErrors(), HttpStatus.BAD_REQUEST);
    }
    userService.createUser(user);
    return ResponseEntity.ok("User created successfully");
}
  • 自定义校验逻辑 :有时内置的注解不够,可以自定义校验注解
    例如,限制用户名长度或格式
java 复制代码
@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@Constraint(validatedBy = UsernameValidator.class)
public @interface ValidUsername {
    String message() default "Invalid username";
    Class<?>[] groups() default {};
    Class<? extends Payload>[] payload() default {};
}

public class UsernameValidator implements ConstraintValidator<ValidUsername, String> {
    public void initialize(ValidUsername constraint) {}

    public boolean isValid(String username, ConstraintValidatorContext context) {
        return username != null && username.matches("^[a-zA-Z0-9]+$");
    }
}

总的来说

MyBatis中的动态SQL功能非常灵活,但也存在潜在的性能和内存问题

在复杂查询中,务必小心处理大量数据操作,合理运用分页、懒加载等技术

在Java代码层面,通过参数校验来确保数据的安全和正确性,是防止问题发生的关键步骤

相关推荐
程序媛小果2 分钟前
基于java+SpringBoot+Vue的宠物咖啡馆平台设计与实现
java·vue.js·spring boot
小光学长6 分钟前
基于vue框架的的流浪宠物救助系统25128(程序+源码+数据库+调试部署+开发环境)系统界面在最后面。
数据库·vue.js·宠物
追风林7 分钟前
mac m1 docker本地部署canal 监听mysql的binglog日志
java·docker·mac
芒果披萨22 分钟前
El表达式和JSTL
java·el
零炻大礼包1 小时前
【SQL server】数据库远程连接配置
数据库
duration~1 小时前
Maven随笔
java·maven
zmgst1 小时前
canal1.1.7使用canal-adapter进行mysql同步数据
java·数据库·mysql
随心............1 小时前
python操作MySQL以及SQL综合案例
数据库·mysql
€☞扫地僧☜€1 小时前
docker 拉取MySQL8.0镜像以及安装
运维·数据库·docker·容器
CopyDragon1 小时前
设置域名跨越访问
数据库·sqlite