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

news/2024/9/29 20:19:42 标签: mybatis, sql, 数据库, java, xml
xmlns="http://www.w3.org/2000/svg" style="display: none;">

目录

  • 前言
  • 1. 基本知识
  • 2. 注意事项
  • 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代码层面,通过参数校验来确保数据的安全和正确性,是防止问题发生的关键步骤


http://www.niftyadmin.cn/n/5683666.html

相关文章

Python 知识宝库 —— 数据可视化:matplotlib 与 seaborn 的使用技巧

&#x1f3ac; 鸽芷咕&#xff1a;个人主页 &#x1f525; 个人专栏: 《C干货基地》《粉丝福利》 ⛺️生活的理想&#xff0c;就是为了理想的生活! 博主简介 博主致力于嵌入式、Python、人工智能、C/C领域和各种前沿技术的优质博客分享&#xff0c;用最优质的内容带来最舒适的…

软件设计——随手笔记

头文件包含其它头文件尽量少include头文件会导致文件体积增大吗&#xff1f;不会。最后生成的可执行文件.hex里面只有函数定义、全局变量、以及一些字符串常量等。这些才会占据.hex文件的空间。c文件里定义变量和函数&#xff0c;h文件里声明变量&#xff08;有的用extern声明对…

Docker启动 Redis提示:Can‘t initialize Background Jobg

问题说明: 在使用docker启动redis失败&#xff0c;但是查看容器日志&#xff0c;除了提示 Fatal:Cant initialize Background Jobg&#xff0c;没有其他错误信息。经过长时间查找资料及试错&#xff0c;现记录下可能的产生原因及解决方案&#xff0c;以便以后参考。 产生原因&…

Meta首款多模态Llama 3.2开源:支持图像推理,还有可在手机上运行的版本 | LeetTalk Daily...

“LeetTalk Daily”&#xff0c;每日科技前沿&#xff0c;由LeetTools AI精心筛选&#xff0c;为您带来最新鲜、最具洞察力的科技新闻。 Meta最近推出的Llama Stack的发布标志着一个重要的里程碑。这一新技术的推出不仅为开发者提供了强大的多模态能力&#xff0c;还为企业和初…

基于nodejs+vue的宠物医院管理系统

作者&#xff1a;计算机学姐 开发技术&#xff1a;SpringBoot、SSM、Vue、MySQL、JSP、ElementUI、Python、小程序等&#xff0c;“文末源码”。 专栏推荐&#xff1a;前后端分离项目源码、SpringBoot项目源码、Vue项目源码、SSM项目源码 精品专栏&#xff1a;Java精选实战项目…

Git 的安装和配置

Git 是跨平台的&#xff0c;可以在 Windows&#xff0c;Linux、Unix 和 Mac 各几大平台上使用 由于笔者主要是使用 Windows&#xff0c;其他平台下安装 Git 的方法暂且不表&#xff08;可参考廖雪峰老师的博客&#xff1a;安装 Git&#xff09; ‍ Windows 安装 Git 从 Git…

【ARM 嵌入式 C 入门及渐进26 -- 内敛函数和宏定义的区别】

请阅读【嵌入式及芯片开发学必备专栏】 文章目录 内敛函数和宏定义的区别内联函数定义和使用内联函数特点内联函数示例 宏定义和使用宏定义特点宏定义示例 比较总结Sumamry 内敛函数和宏定义的区别 在 C 语言中&#xff0c;内联函数和宏定义都可以用来减少函数调用的开销和提高…

使用Spring Cloud Config和JCE加密配置文件的实战教程

使用Spring Cloud Config和JCE加密配置文件的实战教程 1. 前言 在微服务架构中&#xff0c;集中化管理配置文件是常见的需求。Spring Cloud Config 提供了集中化的配置管理方案&#xff0c;而在某些场景下&#xff0c;配置文件中可能包含敏感信息&#xff08;如数据库密码、A…