MyBatis提升数据库安全与性能的4大实战技巧(防注入/动态SQL/模糊优化/连接池)

 更新时间:2026年07月31日 10:09:47   作者:爱吃烤鸡翅的酸菜鱼  
在使用MyBatis框架进行数据库操作时,确保数据库的安全性和性能是非常重要的,下面是一些关键的实战技巧,可以帮助你提升数据库的安全性和性能

1.前言

哈喽大家好吖,【MyBatis保姆级教程】上下篇已经将主要内容讲解完毕,剩下的仍然重要的部分我将放到杂谈篇进行讲解。众所周知,MyBatis数据库操作是后端开发的基石,但你是否常被这些问题困扰?

  • SQL注入攻击防不胜防,数据安全岌岌可危?
  • 拼接动态SQL既麻烦又危险,容易出错且不安全?
  • LIKE '%keyword%'模糊查询慢如蜗牛,用户体验糟糕?
  • 连接池配置不当,高并发下性能骤降甚至连接耗尽?

这篇实战指南,将直击这四大核心痛点,提供可落地的解决方案。 阅读后,你将能够:

  • 彻底防御SQL注入: 掌握正确安全防注入的代码操作。
  • 编写安全的动态SQL: 学会安全、高效地构建复杂查询条件,避免手动拼接风险。
  • 显著优化模糊查询性能: 理解索引失效原因,应用实用优化技巧提升搜索速度。
  • 合理配置数据库连接池: 理解关键参数含义,根据负载进行调优,保障稳定高效。

解决这些问题,你的应用将更安全、更快速、更稳定。下面进入正题:

插播一条消息~

🔍 十年经验淬炼 · 系统化AI学习平台推荐

系统化学习AI平台 https://www.captainbed.cn/scy/

为什么值得投入?

  • 📚 完整知识体系:从数学基础 → 工业级项目(人脸识别/自动驾驶/GANs),内容由浅入深
  • 💻 实战为王:每小节配套可运行代码案例(提供完整源码)
  • 🎯 零基础友好:用生活案例讲解算法,无需担心数学/编程基础

🚀 特别适合

  • 想系统补强AI知识的开发者
  • 转型人工智能领域的从业者
  • 需要项目经验的学生

2.正文

2.1区分${}与#{}

核心区别总结

特性#{} (预编译/占位符)${} (字符串替换)
工作原理生成 PreparedStatement,使用 ? 占位直接拼接字符串到 SQL 语句中
安全性✅ 高 (防止 SQL 注入)❌ 极低 (存在 SQL 注入风险)
性能✅ 高 (SQL 可被数据库缓存复用)❌ 低 (每次需重新解析编译 SQL)
适用场景参数值传递 (WHERE 值, INSERT 值等)动态表名/列名、ORDER BY 子句 (需严格校验)

2.1.1SQL注入问题

场景说明

假设有一个根据用户名查询用户的功能,攻击者输入恶意参数 ' OR '1'='1 试图获取全部用户数据。

示例代码

2.1.1.1 使用 ${} (存在注入漏洞)

<!-- Mapper XML -->
<select id="findUserByNameUnsafe" resultType="User">
  SELECT * FROM user WHERE name = '${name}'
</select>
// Java 调用
String maliciousInput = "' OR '1'='1";
List<User> users = userMapper.findUserByNameUnsafe(maliciousInput);

 最终执行的 SQL:

SELECT * FROM user WHERE name = '' OR '1'='1'

✅ 运行结果: 返回 user 表中所有用户数据'1'='1' 永远为真)。
💥 风险: 数据库被非法拖库! 

2.1.1.2 使用 #{} (安全防注入)

<!-- Mapper XML -->
<select id="findUserByNameSafe" resultType="User">
  SELECT * FROM user WHERE name = #{name}
</select>
// Java 调用
String maliciousInput = "' OR '1'='1";
List<User> users = userMapper.findUserByNameSafe(maliciousInput);

 最终执行的 SQL:

SELECT * FROM user WHERE name = ?

参数值: "' OR '1'='1"(作为整体字符串传入)
✅ 运行结果: 查询 name = "' OR '1'='1" 的用户 → 返回空列表
🔒 安全: 恶意代码被当作普通字符串处理,无法改变 SQL 结构! 

2.1.2预编译SQL与即时间SQL

2.1.2.1 #{} 工作原理 (预编译 SQL)

Step 1: MyBatis 解析 SQL,将 #{name} 替换为 ?

SELECT * FROM user WHERE name = ?

Step 2: 将 SQL 发送到数据库 预编译(生成执行计划)。

Step 3: 执行时传入参数值 "' OR '1'='1",数据库将其作为纯字符串处理。

✅ 优势:

  • 防 SQL 注入(参数值无法改变 SQL 结构)。
  • 高性能(预编译 SQL 可被数据库缓存复用)。

2.1.2.2${} 工作原理 (即时间 SQL)

Step 1: MyBatis 直接拼接字符串:

SELECT * FROM user WHERE name = '' OR '1'='1'

Step 2: 每次执行都会生成全新的 SQL 发送到数据库。

Step 3: 数据库每次都需要重新解析、编译、执行

❌ 风险与代价:

  • 引发 SQL 注入(用户输入可改变 SQL 语义)。
  • 性能低下(无法复用执行计划)。

2.1.3${} 的正确使用场景与避坑指南

2.1.3.1场景:动态排序字段 (ORDER BY)

<select id="findUsersOrdered" resultType="User">
  SELECT * FROM user
  ORDER BY ${sortField} ${sortOrder}
</select>

2.1.3.2避坑措施:白名单校验 (Java 层)

// Service 层校验排序字段
public List<User> getUsersOrdered(String sortField, String sortOrder) {
    // 定义允许的字段 (白名单)
    Set<String> validFields = Set.of("id", "name", "create_time");
    Set<String> validOrders = Set.of("ASC", "DESC");
    if (!validFields.contains(sortField)) {
        sortField = "id"; // 默认字段
    }
    if (!validOrders.contains(sortOrder)) {
        sortOrder = "ASC"; // 默认排序
    }
    return userMapper.findUsersOrdered(sortField, sortOrder);
}

2.1.3.3执行示例

// 正常调用
getUsersOrdered("name", "DESC"); 
// SQL: SELECT * FROM user ORDER BY name DESC
// 攻击尝试
getUsersOrdered("(SELECT 1 FROM DUAL); DROP TABLE user; --", "DESC");
// 白名单过滤 → SQL: SELECT * FROM user ORDER BY id ASC ✅

2.1.4关键结论

  • #{} 用于值传递:安全可靠,99% 场景使用它。
  • ${} 用于 SQL 片段:仅用于动态表名、列名、排序字段(必须配合白名单校验)。
  • 禁止 ${} 拼接用户输入:否则等于敞开大门欢迎 SQL 注入攻击。
  • 预编译是性能与安全的基石:永远优先选择 #{}

2.2动态SQL

什么是动态 SQL?

动态 SQL 是 MyBatis 最强大的特性之一,它允许根据运行时条件动态构建 SQL 语句。通过条件判断、循环等逻辑,可以避免编写大量重复 SQL,提高代码复用性和灵活性。

2.2.1<if>标签

XML

<select id="findUsers" resultType="User">
  SELECT * FROM user
  WHERE 1=1
  <if test="name != null">
    AND name = #{name}
  </if>
  <if test="email != null">
    AND email = #{email}
  </if>
</select>

 执行示例:

// 只传 name
mapper.findUsers("Alice", null);
// SQL: SELECT * FROM user WHERE 1=1 AND name = 'Alice'
// 传 name 和 email
mapper.findUsers("Bob", "bob@example.com");
// SQL: SELECT * FROM user WHERE 1=1 AND name = 'Bob' AND email = 'bob@example.com'

2.2.2<choose><when><otherwise>标签(类似switch-case)

XML

<select id="searchUsers" resultType="User">
  SELECT * FROM user
  WHERE
  <choose>
    <when test="id != null">
      id = #{id}
    </when>
    <when test="name != null">
      name LIKE CONCAT('%', #{name}, '%')
    </when>
    <otherwise>
      status = 'ACTIVE'
    </otherwise>
  </choose>
</select>

执行示例:

// 传 id
mapper.searchUsers(1001, null, null);
// SQL: SELECT * FROM user WHERE id = 1001
// 传 name
mapper.searchUsers(null, "John", null);
// SQL: SELECT * FROM user WHERE name LIKE '%John%'
// 不传参数
mapper.searchUsers(null, null, null);
// SQL: SELECT * FROM user WHERE status = 'ACTIVE'

2.2.3<where>标签

解决条件前缀问题(自动处理 AND/OR)

XML

<select id="findActiveUsers" resultType="User">
  SELECT * FROM user
  <where>
    <if test="name != null">
      name = #{name}
    </if>
    <if test="role != null">
      AND role = #{role}
    </if>
  </where>
</select>

优势:

  • 自动删除首个条件前的 AND/OR
  • 当所有条件为空时,不生成 WHERE 关键字
  • 避免 WHERE 1=1 这种不优雅的写法

2.2.4<set>标签

XML

<update id="updateUser">
  UPDATE user
  <set>
    <if test="name != null">name=#{name},</if>
    <if test="email != null">email=#{email},</if>
    <if test="role != null">role=#{role}</if>
  </set>
  WHERE id=#{id}
</update>

执行示例:

// 只更新 email
User user = new User();
user.setId(1001);
user.setEmail("new@example.com");
mapper.updateUser(user);
// SQL: UPDATE user SET email='new@example.com' WHERE id=1001

特点: 自动去除末尾多余的逗号 

2.2.5<foreach>标签

场景 1:IN 查询

XML

<select id="findUsersByIds" resultType="User">
  SELECT * FROM user
  WHERE id IN
  <foreach item="id" collection="ids" 
           open="(" separator="," close=")">
    #{id}
  </foreach>
</select>

执行示例:

List<Integer> ids = Arrays.asList(101, 102, 103);
mapper.findUsersByIds(ids);
// SQL: SELECT * FROM user WHERE id IN (101,102,103)

场景 2:批量插入

XML

<insert id="batchInsert">
  INSERT INTO user (name, email) VALUES
  <foreach item="user" collection="users" separator=",">
    (#{user.name}, #{user.email})
  </foreach>
</insert>

 执行示例:

INSERT INTO user (name, email) VALUES
('Alice','alice@test.com'),
('Bob','bob@test.com'),
('Charlie','charlie@test.com')

2.2.6<trim>标签

功能: 更灵活地处理前缀/后缀

XML

<select id="dynamicSearch" resultType="User">
  SELECT * FROM user
  <trim prefix="WHERE" prefixOverrides="AND |OR ">
    <if test="name != null">
      AND name = #{name}
    </if>
    <if test="role != null">
      OR role = #{role}
    </if>
  </trim>
</select>

参数说明:

  • prefix:添加前缀
  • prefixOverrides:删除指定的前缀(支持正则)
  • suffix:添加后缀
  • suffixOverrides:删除指定的后缀

动态SQL总结

场景推荐方案优势
条件查询<where> + <if>智能处理 AND/OR
动态更新<set>自动处理逗号
多路分支<choose>/<when>类似 switch-case 结构
批量操作<foreach>高效处理集合
复杂条件<trim>自定义字符串修剪
SQL 片段复用<sql> + <include>减少重复代码
动态排序<choose> 或 <foreach>安全实现 ORDER BY

最佳实践:动态 SQL 应保持简洁可读,超过 10 行的动态 SQL 建议拆分为多个方法或使用注解方式实现。

2.3like查询(模糊查询)

问题核心

  • 直接拼接导致错误:LIKE #{value} → LIKE ?(需值为%value%
  • ${} 有注入风险:LIKE '%${value}%' 不安全

2.3.1使用拼接符

// Service层处理
public List<User> searchUsers(String keyword) {
    String searchTerm = "%" + keyword + "%";  // 添加通配符
    return userMapper.findByNameLike(searchTerm);
}
<!-- Mapper XML -->
<select id="findByNameLike" resultType="User">
  SELECT * FROM user 
  WHERE name LIKE #{pattern}
</select>

 ✅ 执行结果:

searchUsers("john");
// SQL:SELECT * FROM user WHERE name LIKE '%john%'
// 返回:所有包含"john"的用户(如"john123", "xxx_john")

2.3.2使用CONCAT函数

<!-- MySQL示例 -->
<select id="findByNameLike" resultType="User">
  SELECT * FROM user 
  WHERE name LIKE CONCAT('%', #{keyword}, '%')
</select>
<!-- Oracle示例 -->
<select id="findByNameLike" resultType="User">
  SELECT * FROM user 
  WHERE name LIKE '%' || #{keyword} || '%'
</select>

⚠️ 优缺点:

  • 优点:SQL意图清晰
  • 缺点:数据库兼容性差(不同数据库语法不同)

2.3.3 使用<bind>标签(XML专用)

<select id="findByNameLike" resultType="User">
  <bind name="pattern" value="'%' + keyword + '%'" />
  SELECT * FROM user 
  WHERE name LIKE #{pattern}
</select>

🚫 限制:

  • 仅适用于XML配置
  • 灵活性有限(难以实现左模糊 value%

2.3.4模糊查询方案对比

方案安全性灵活性数据库兼容性推荐指数
Java层拼接通配符✅ 高✅ 高✅ 完美⭐⭐⭐⭐⭐
SQL的CONCAT函数✅ 高❌ 低❌ 各数据库不同⭐⭐
<bind>标签✅ 高⚠️ 中✅ 完美⭐⭐⭐
${}直接拼接❌ 危险✅ 高✅ 完美⛔ 禁止使用

2.4数据库连接池

2.4.1介绍

什么是数据库连接池?
数据库连接池(Connection Pool)是管理数据库连接的缓冲池技术。它在应用启动时创建一定数量的数据库连接并保持这些连接处于活动状态。当应用需要访问数据库时,直接从池中获取连接;使用完毕后将连接归还给连接池,而不是真正关闭连接。

为什么需要连接池?

  • 性能优化:避免每次请求都创建新连接(TCP三次握手、数据库认证等耗时操作)
  • 资源管理:控制最大连接数,防止数据库被过多连接压垮
  • 连接复用:减少系统开销,提高响应速度
  • 健康监测:自动检测失效连接,保证连接可用性

连接池工作原理:

核心参数解析:

参数名说明默认值
initialSize初始连接数0-10
minIdle最小空闲连接数0-10
maxActive/maxPoolSize关键 最大活跃连接数8-100
maxWait获取连接的最大等待时间(ms)30000
validationQuery连接检测SQL(如SELECT 1-
testWhileIdle是否对空闲连接进行检测true/false
timeBetweenEvictionRunsMillis检测间隔(ms)60000

2.4.2使用

2.4.2.1HikariCP (性能王者)

特点:

  • Spring Boot 2.x+ 默认连接池
  • 极致轻量(130KB)
  • 高并发性能最优
  • 智能连接管理

Spring Boot 配置 (application.yml):

spring:
  datasource:
    url: jdbc:mysql://localhost:3306/mydb
    username: root
    password: 123456
    driver-class-name: com.mysql.cj.jdbc.Driver
    hikari:
      pool-name: MyHikariPool
      maximum-pool-size: 20          # 关键配置!
      minimum-idle: 5                # 推荐等于maximum-pool-size
      connection-timeout: 30000      # 30秒
      idle-timeout: 600000           # 10分钟
      max-lifetime: 1800000          # 30分钟
      connection-test-query: SELECT 1
      leak-detection-threshold: 5000 # 连接泄露检测(ms)

关键参数说明:

  • maximum-pool-size:必须根据实际负载设置(公式:CPU核心数*2 + 有效磁盘数
  • leak-detection-threshold:生产环境建议设置(5000-10000ms)

2.4.2.2 Druid (监控之王)

特点:

  • 阿里巴巴开源
  • 强大的监控功能
  • SQL防火墙
  • 详细统计信息
  • 加密支持

Spring Boot 配置 (application.yml):

spring:
  datasource:
    url: jdbc:mysql://localhost:3306/mydb
    username: root
    password: 123456
    driver-class-name: com.mysql.cj.jdbc.Driver
    type: com.alibaba.druid.pool.DruidDataSource # 指定类型
    druid:
      initial-size: 5
      min-idle: 5
      max-active: 20                 # 相当于Hikari的maximum-pool-size
      max-wait: 60000                # 获取连接超时时间
      filters: stat,wall,slf4j        # 开启监控
      # 监控配置
      stat-view-servlet:
        enabled: true
        url-pattern: /druid/*
        login-username: admin
        login-password: admin
      web-stat-filter:
        enabled: true
        url-pattern: /*
      filter:
        stat:
          log-slow-sql: true         # 记录慢SQL
          slow-sql-millis: 1000      # 慢SQL阈值(ms)

启用监控页面:
访问 http://localhost:8080/druid 查看:

  • 数据源状态
  • SQL监控
  • URL监控
  • Session监控
  • Spring监控

2.4.2.3连接池对比:HikariCP vs Druid

特性HikariCPDruid
性能⭐⭐⭐⭐⭐ (最快)⭐⭐⭐⭐ (优秀)
监控功能⭐⭐ (基础)⭐⭐⭐⭐⭐ (强大)
SQL防火墙❌ 不支持✅ 支持
加密支持❌ 不支持✅ 支持
连接泄露检测✅ 优秀✅ 优秀
配置复杂度⭐⭐ (简单)⭐⭐⭐ (较复杂)
社区活跃度⭐⭐⭐⭐ (全球活跃)⭐⭐⭐⭐ (国内活跃)
Spring Boot整合✅ 默认集成✅ 需显式配置

3.小结

掌握这四招,你数据库操作的水平绝对能上一个新台阶!安全问题刻不容缓,性能优化也永无止境。 别再让这些问题拖后腿了,现在就动手优化吧!

到此这篇关于MyBatis提升数据库安全与性能的4大实战技巧(防注入/动态SQL/模糊优化/连接池)的文章就介绍到这了,更多相关MyBatis数据库安全与性能优化内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • 在 Spring Boot 中使用 Quartz 调度作业的示例详解

    在 Spring Boot 中使用 Quartz 调度作业的示例详解

    这篇文章主要介绍了在 Spring Boot 中使用 Quartz 调度作业的示例详解,在本文中,我们将看看如何使用Quartz框架来调度任务,Quartz支持在特定时间运行作业、重复作业执行、将作业存储在数据库中以及Spring集成,需要的朋友可以参考下
    2022-07-07
  • spring-data-elasticsearch @Field注解无效的完美解决方案

    spring-data-elasticsearch @Field注解无效的完美解决方案

    这篇文章主要介绍了spring-data-elasticsearch @Field注解无效的完美解决方案,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2021-07-07
  • Java中JVM的类加载器详解

    Java中JVM的类加载器详解

    这篇文章主要介绍了Java中的类加载器详解,从概念上来讲, 自定义类加载器一般指的是程序中由开发人员自定义的类加载器,但是Java虚拟机规范却没有这么定义,而是将所有派生于抽象类ClassLoader的类加载器都划分为自定义类加载器,需要的朋友可以参考下
    2023-07-07
  • JAVA验证身份证号码有效性的实例代码

    JAVA验证身份证号码有效性的实例代码

    很多项目业务都会设计到人员信息,那么身份证号就是必不可少的校验项,下面这篇文章主要给大家介绍了关于JAVA验证身份证号码有效性的相关资料,文中通过实例代码介绍的非常详细,需要的朋友可以参考下
    2022-08-08
  • spring的xml文件打开没有namespace等操作选项的解决方案

    spring的xml文件打开没有namespace等操作选项的解决方案

    这篇文章主要介绍了spring的xml文件打开没有namespace等操作选项的解决方案,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2021-09-09
  • springboot整合JavaCV实现视频截取第N帧并保存图片

    springboot整合JavaCV实现视频截取第N帧并保存图片

    这篇文章主要为大家详细介绍了springboot如何整合JavaCV实现视频截取第N帧并保存为图片,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起了解一下
    2023-08-08
  • Java实现三个线程进行顺序打印的两种方案

    Java实现三个线程进行顺序打印的两种方案

    三个线程按顺序打印是一个经典的并发编程面试题,通常要求线程 A 打印 1,线程 B 打印 2,线程 C 打印 3,然后循环往复,所以本文给大家介绍了Java实现三个线程进行顺序打印的两种方案的相关资料,需要的朋友可以参考下
    2026-01-01
  • Spring BeanFactory和FactoryBean的详细对比和解析

    Spring BeanFactory和FactoryBean的详细对比和解析

    在Spring框架中BeanFactory和FactoryBean虽然名称相似,但功能和角色截然不同,这篇文章主要介绍了Spring BeanFactory和FactoryBean详细对比和解析的相关资料,文中通过代码介绍的非常详细,需要的朋友可以参考下
    2026-07-07
  • SpringBoot加载profile全面解析

    SpringBoot加载profile全面解析

    SpringBoot的Profile机制通过多配置文件和注解实现环境隔离,支持开发、测试、生产等不同环境的灵活配置切换,无需修改代码,关键点包括配置文件命名规范、激活方式、优先级及企业级安全部署实践,本文介绍SpringBoot加载profile的过程,感兴趣的朋友一起看看吧
    2025-08-08
  • Eureka服务发现注册实现教程

    Eureka服务发现注册实现教程

    文章介绍了微服务架构中的服务发现机制,重点讲解了Eureka服务发现框架的原理、配置和使用方法,Eureka通过服务注册、心跳检测和客户端缓存机制来实现服务的高可用性和负载均衡,文章还详细描述了Eureka的一些特性和配置过程中的注意事项,并提供了一些实战示例
    2025-11-11

最新评论